From 3231a80e8b69ed96f0b591f027c58839b50727e4 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Wed, 1 Nov 2017 16:22:12 -0400 Subject: [PATCH 001/326] 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 Reviewed-by: Mark Mentovai --- minidump/minidump.gyp | 2 + minidump/minidump_annotation_writer.cc | 162 ++++++++++++++++ minidump/minidump_annotation_writer.h | 109 +++++++++++ minidump/minidump_annotation_writer_test.cc | 188 +++++++++++++++++++ minidump/minidump_extensions.h | 26 +++ minidump/minidump_test.gyp | 1 + minidump/test/minidump_writable_test_util.cc | 15 ++ minidump/test/minidump_writable_test_util.h | 15 +- 8 files changed, 514 insertions(+), 4 deletions(-) create mode 100644 minidump/minidump_annotation_writer.cc create mode 100644 minidump/minidump_annotation_writer.h create mode 100644 minidump/minidump_annotation_writer_test.cc diff --git a/minidump/minidump.gyp b/minidump/minidump.gyp index e36006c9..63e0ba6b 100644 --- a/minidump/minidump.gyp +++ b/minidump/minidump.gyp @@ -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', diff --git a/minidump/minidump_annotation_writer.cc b/minidump/minidump_annotation_writer.cc new file mode 100644 index 00000000..54a3ecba --- /dev/null +++ b/minidump/minidump_annotation_writer.cc @@ -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 + +#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& 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 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& list) { + DCHECK_EQ(state(), kStateMutable); + for (const auto& annotation : list) { + auto writer = std::make_unique(); + writer->InitializeFromSnapshot(annotation); + AddObject(std::move(writer)); + } +} + +void MinidumpAnnotationListWriter::AddObject( + std::unique_ptr 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 +MinidumpAnnotationListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector 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 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 diff --git a/minidump/minidump_annotation_writer.h b/minidump/minidump_annotation_writer.h new file mode 100644 index 00000000..fc30dbc3 --- /dev/null +++ b/minidump/minidump_annotation_writer.h @@ -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 +#include + +#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& data); + + //! \brief Returns the MinidumpAnnotation referencing this object’s data. + const MinidumpAnnotation* minidump_annotation() const { return &annotation_; } + + protected: + // MinidumpWritable: + + bool Freeze() override; + size_t SizeOfObject() override; + std::vector 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& list); + + //! \brief Adds a single MinidumpAnnotationWriter to the list to be written. + void AddObject(std::unique_ptr 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 Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + std::unique_ptr minidump_list_; + std::vector> objects_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpAnnotationListWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_ANNOTATION_WRITER_H_ diff --git a/minidump/minidump_annotation_writer_test.cc b/minidump/minidump_annotation_writer_test.cc new file mode 100644 index 00000000..9b4e80de --- /dev/null +++ b/minidump/minidump_annotation_writer_test.cc @@ -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 + +#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( + sizeof(MinidumpAnnotationList) + count * sizeof(MinidumpAnnotation)); + location_descriptor.Rva = 0; + return MinidumpWritableAtLocationDescriptor( + 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 kValue{'v', 'a', 'l', 'u', 'e', '\0'}; + + auto annotation_writer = std::make_unique(); + 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 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(); + 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 kValue1{'r', 'e', 'd', '\0'}; + const std::vector kValue2{'m', 'a', 'g', 'e', 'n', 't', 'a', '\0'}; + + MinidumpAnnotationListWriter list_writer; + + auto annotation = std::make_unique(); + annotation->InitializeWithData(kName, kType, kValue1); + list_writer.AddObject(std::move(annotation)); + + annotation = std::make_unique(); + 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 diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h index ad69aecb..3be5cea1 100644 --- a/minidump/minidump_extensions.h +++ b/minidump/minidump_extensions.h @@ -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. //! diff --git a/minidump/minidump_test.gyp b/minidump/minidump_test.gyp index beb41519..cfc0606d 100644 --- a/minidump/minidump_test.gyp +++ b/minidump/minidump_test.gyp @@ -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', diff --git a/minidump/test/minidump_writable_test_util.cc b/minidump/test/minidump_writable_test_util.cc index 1841243a..1a832bf3 100644 --- a/minidump/test/minidump_writable_test_util.cc +++ b/minidump/test/minidump_writable_test_util.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 const typename T::ListType* MinidumpListAtLocationDescriptor( const std::string& file_contents, @@ -313,6 +319,15 @@ MinidumpWritableAtLocationDescriptor( MinidumpSimpleStringDictionaryListTraits>(file_contents, location); } +template <> +const MinidumpAnnotationList* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor( + file_contents, location); +} + namespace { template diff --git a/minidump/test/minidump_writable_test_util.h b/minidump/test/minidump_writable_test_util.h index dd8f5531..5b176d2b 100644 --- a/minidump/test/minidump_writable_test_util.h +++ b/minidump/test/minidump_writable_test_util.h @@ -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 structure’s 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( const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location); +template <> +const MinidumpAnnotationList* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + //! \brief Returns a typed minidump object located within a minidump file’s //! contents, where the offset of the object is known. //! From f9e587b036b18a054586a78925bbc3349986a6f5 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Wed, 1 Nov 2017 18:14:53 -0400 Subject: [PATCH 002/326] Remove NOTREACHED() in ModuleSnapshot::AnnotationObjects() impls. This is causing crashpad_handler_test to fail in Debug on Windows. Bug: crashpad:192 Change-Id: Icf3ff387050ee2becf471f4e7c3a75394b1dd436 Reviewed-on: https://chromium-review.googlesource.com/749792 Reviewed-by: Mark Mentovai --- snapshot/mac/module_snapshot_mac.cc | 1 - snapshot/win/module_snapshot_win.cc | 1 - 2 files changed, 2 deletions(-) diff --git a/snapshot/mac/module_snapshot_mac.cc b/snapshot/mac/module_snapshot_mac.cc index 2d750370..d04c0e2e 100644 --- a/snapshot/mac/module_snapshot_mac.cc +++ b/snapshot/mac/module_snapshot_mac.cc @@ -187,7 +187,6 @@ std::map ModuleSnapshotMac::AnnotationsSimpleMap() std::vector ModuleSnapshotMac::AnnotationObjects() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); return {}; } diff --git a/snapshot/win/module_snapshot_win.cc b/snapshot/win/module_snapshot_win.cc index b130eccb..c3a83fdf 100644 --- a/snapshot/win/module_snapshot_win.cc +++ b/snapshot/win/module_snapshot_win.cc @@ -192,7 +192,6 @@ std::map ModuleSnapshotWin::AnnotationsSimpleMap() std::vector ModuleSnapshotWin::AnnotationObjects() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); return {}; } From e38c57a9c65887f26892b9c5b3a71caae1857093 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Wed, 1 Nov 2017 17:06:18 -0400 Subject: [PATCH 003/326] Expose the annotation_objects on MinidumpModuleCrashpadInfo. This writes any set annotation objects list for a module into a minidump, though no crash handler currently sets annotation objects for a crashing process. Bug: crashpad:192 Change-Id: Ib6d92edecb8d40061eaee08cbbc5c20dd1f048ef Reviewed-on: https://chromium-review.googlesource.com/744942 Commit-Queue: Robert Sesek Reviewed-by: Mark Mentovai --- minidump/minidump_extensions.h | 14 +- .../minidump_module_crashpad_info_writer.cc | 27 +++- .../minidump_module_crashpad_info_writer.h | 13 ++ ...nidump_module_crashpad_info_writer_test.cc | 149 +++++++++++++++++- 4 files changed, 192 insertions(+), 11 deletions(-) diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h index 3be5cea1..fbaa6ee5 100644 --- a/minidump/minidump_extensions.h +++ b/minidump/minidump_extensions.h @@ -344,7 +344,7 @@ struct ALIGNAS(4) PACKED MinidumpModuleCrashpadInfo { //! module controls the data that appears here. //! //! These strings correspond to ModuleSnapshot::AnnotationsVector() and do not - //! duplicate anything in #simple_annotations. + //! duplicate anything in #simple_annotations or #annotation_objects. //! //! This field is present when #version is at least `1`. MINIDUMP_LOCATION_DESCRIPTOR list_annotations; @@ -354,10 +354,20 @@ struct ALIGNAS(4) PACKED MinidumpModuleCrashpadInfo { //! //! These key-value pairs correspond to //! ModuleSnapshot::AnnotationsSimpleMap() and do not duplicate anything in - //! #list_annotations. + //! #list_annotations or #annotation_objects. //! //! This field is present when #version is at least `1`. MINIDUMP_LOCATION_DESCRIPTOR simple_annotations; + + //! \brief A MinidumpAnnotationList object containing the annotation objects + //! stored within the module. The module controls the data that appears + //! here. + //! + //! These key-value pairs correspond to ModuleSnapshot::AnnotationObjects() + //! and do not duplicate anything in #list_annotations or #simple_annotations. + //! + //! This field may be present when #version is at least `1`. + MINIDUMP_LOCATION_DESCRIPTOR annotation_objects; }; //! \brief A link between a MINIDUMP_MODULE structure and additional diff --git a/minidump/minidump_module_crashpad_info_writer.cc b/minidump/minidump_module_crashpad_info_writer.cc index a9c5e5d6..a31feaea 100644 --- a/minidump/minidump_module_crashpad_info_writer.cc +++ b/minidump/minidump_module_crashpad_info_writer.cc @@ -17,6 +17,7 @@ #include #include "base/logging.h" +#include "minidump/minidump_annotation_writer.h" #include "minidump/minidump_simple_string_dictionary_writer.h" #include "snapshot/module_snapshot.h" #include "util/file/file_writer.h" @@ -28,7 +29,8 @@ MinidumpModuleCrashpadInfoWriter::MinidumpModuleCrashpadInfoWriter() : MinidumpWritable(), module_(), list_annotations_(), - simple_annotations_() { + simple_annotations_(), + annotation_objects_() { module_.version = MinidumpModuleCrashpadInfo::kVersion; } @@ -54,6 +56,12 @@ void MinidumpModuleCrashpadInfoWriter::InitializeFromSnapshot( if (simple_annotations->IsUseful()) { SetSimpleAnnotations(std::move(simple_annotations)); } + + auto annotation_objects = std::make_unique(); + annotation_objects->InitializeFromList(module_snapshot->AnnotationObjects()); + if (annotation_objects->IsUseful()) { + SetAnnotationObjects(std::move(annotation_objects)); + } } void MinidumpModuleCrashpadInfoWriter::SetListAnnotations( @@ -70,8 +78,15 @@ void MinidumpModuleCrashpadInfoWriter::SetSimpleAnnotations( simple_annotations_ = std::move(simple_annotations); } +void MinidumpModuleCrashpadInfoWriter::SetAnnotationObjects( + std::unique_ptr annotation_objects) { + DCHECK_EQ(state(), kStateMutable); + + annotation_objects_ = std::move(annotation_objects); +} + bool MinidumpModuleCrashpadInfoWriter::IsUseful() const { - return list_annotations_ || simple_annotations_; + return list_annotations_ || simple_annotations_ || annotation_objects_; } bool MinidumpModuleCrashpadInfoWriter::Freeze() { @@ -90,6 +105,11 @@ bool MinidumpModuleCrashpadInfoWriter::Freeze() { &module_.simple_annotations); } + if (annotation_objects_) { + annotation_objects_->RegisterLocationDescriptor( + &module_.annotation_objects); + } + return true; } @@ -110,6 +130,9 @@ MinidumpModuleCrashpadInfoWriter::Children() { if (simple_annotations_) { children.push_back(simple_annotations_.get()); } + if (annotation_objects_) { + children.push_back(annotation_objects_.get()); + } return children; } diff --git a/minidump/minidump_module_crashpad_info_writer.h b/minidump/minidump_module_crashpad_info_writer.h index f6a2f320..850db040 100644 --- a/minidump/minidump_module_crashpad_info_writer.h +++ b/minidump/minidump_module_crashpad_info_writer.h @@ -28,6 +28,7 @@ namespace crashpad { +class MinidumpAnnotationListWriter; class MinidumpSimpleStringDictionaryWriter; class ModuleSnapshot; @@ -74,6 +75,17 @@ class MinidumpModuleCrashpadInfoWriter final void SetSimpleAnnotations( std::unique_ptr simple_annotations); + //! \brief Arranges for MinidumpModuleCrashpadInfo::annotation_objects to + //! point to the MinidumpAnnotationListWriter object to be written by + //! \a annotation_objects. + //! + //! This object takes ownership of \a annotation_objects and becomes its + //! parent in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetAnnotationObjects( + std::unique_ptr annotation_objects); + //! \brief Determines whether the object is useful. //! //! A useful object is one that carries data that makes a meaningful @@ -94,6 +106,7 @@ class MinidumpModuleCrashpadInfoWriter final MinidumpModuleCrashpadInfo module_; std::unique_ptr list_annotations_; std::unique_ptr simple_annotations_; + std::unique_ptr annotation_objects_; DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCrashpadInfoWriter); }; diff --git a/minidump/minidump_module_crashpad_info_writer_test.cc b/minidump/minidump_module_crashpad_info_writer_test.cc index d6e8efc8..63a9338c 100644 --- a/minidump/minidump_module_crashpad_info_writer_test.cc +++ b/minidump/minidump_module_crashpad_info_writer_test.cc @@ -20,7 +20,9 @@ #include #include "gtest/gtest.h" +#include "minidump/minidump_annotation_writer.h" #include "minidump/minidump_simple_string_dictionary_writer.h" +#include "minidump/test/minidump_byte_array_writer_test_util.h" #include "minidump/test/minidump_file_writer_test_util.h" #include "minidump/test/minidump_string_writer_test_util.h" #include "minidump/test/minidump_writable_test_util.h" @@ -103,6 +105,8 @@ TEST(MinidumpModuleCrashpadInfoWriter, EmptyModule) { EXPECT_EQ(module->list_annotations.Rva, 0u); EXPECT_EQ(module->simple_annotations.DataSize, 0u); EXPECT_EQ(module->simple_annotations.Rva, 0u); + EXPECT_EQ(module->annotation_objects.DataSize, 0u); + EXPECT_EQ(module->annotation_objects.Rva, 0u); } TEST(MinidumpModuleCrashpadInfoWriter, FullModule) { @@ -111,6 +115,7 @@ TEST(MinidumpModuleCrashpadInfoWriter, FullModule) { static constexpr char kValue[] = "value"; static constexpr char kEntry[] = "entry"; std::vector vector(1, std::string(kEntry)); + const AnnotationSnapshot annotation("one", 42, {'t', 'e', 's', 't'}); StringFile string_file; @@ -130,6 +135,10 @@ TEST(MinidumpModuleCrashpadInfoWriter, FullModule) { std::move(simple_string_dictionary_entry_writer)); module_writer->SetSimpleAnnotations( std::move(simple_string_dictionary_writer)); + auto annotation_list_writer = + std::make_unique(); + annotation_list_writer->InitializeFromList({annotation}); + module_writer->SetAnnotationObjects(std::move(annotation_list_writer)); EXPECT_TRUE(module_writer->IsUseful()); module_list_writer->AddModule(std::move(module_writer), kMinidumpModuleListIndex); @@ -140,14 +149,16 @@ TEST(MinidumpModuleCrashpadInfoWriter, FullModule) { ASSERT_EQ(string_file.string().size(), sizeof(MinidumpModuleCrashpadInfoList) + sizeof(MinidumpModuleCrashpadInfoLink) + - sizeof(MinidumpModuleCrashpadInfo) + - sizeof(MinidumpRVAList) + - sizeof(RVA) + - sizeof(MinidumpSimpleStringDictionary) + + sizeof(MinidumpModuleCrashpadInfo) + sizeof(MinidumpRVAList) + + sizeof(RVA) + sizeof(MinidumpSimpleStringDictionary) + sizeof(MinidumpSimpleStringDictionaryEntry) + - sizeof(MinidumpUTF8String) + arraysize(kEntry) + 2 + // padding + sizeof(MinidumpAnnotationList) + 2 + // padding + sizeof(MinidumpAnnotation) + sizeof(MinidumpUTF8String) + + arraysize(kEntry) + 2 + // padding sizeof(MinidumpUTF8String) + arraysize(kKey) + - sizeof(MinidumpUTF8String) + arraysize(kValue)); + sizeof(MinidumpUTF8String) + arraysize(kValue) + + sizeof(MinidumpUTF8String) + annotation.name.size() + 1 + + sizeof(MinidumpByteArray) + annotation.value.size()); const MinidumpModuleCrashpadInfoList* module_list = MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 1); @@ -165,6 +176,8 @@ TEST(MinidumpModuleCrashpadInfoWriter, FullModule) { EXPECT_NE(module->list_annotations.Rva, 0u); EXPECT_NE(module->simple_annotations.DataSize, 0u); EXPECT_NE(module->simple_annotations.Rva, 0u); + EXPECT_NE(module->annotation_objects.DataSize, 0u); + EXPECT_NE(module->annotation_objects.Rva, 0u); const MinidumpRVAList* list_annotations = MinidumpWritableAtLocationDescriptor( @@ -188,18 +201,36 @@ TEST(MinidumpModuleCrashpadInfoWriter, FullModule) { EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( string_file.string(), simple_annotations->entries[0].value), kValue); + + const MinidumpAnnotationList* annotation_objects = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module->annotation_objects); + ASSERT_TRUE(annotation_objects); + + ASSERT_EQ(annotation_objects->count, 1u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), annotation_objects->objects[0].name), + annotation.name); + EXPECT_EQ(annotation_objects->objects[0].type, 42u); + EXPECT_EQ(annotation_objects->objects[0].reserved, 0u); + EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(), + annotation_objects->objects[0].value), + annotation.value); } TEST(MinidumpModuleCrashpadInfoWriter, ThreeModules) { constexpr uint32_t kMinidumpModuleListIndex0 = 0; static constexpr char kKey0[] = "key"; static constexpr char kValue0[] = "value"; + const AnnotationSnapshot annotation0("name", 0x8FFF, {'t', '\0', 't'}); constexpr uint32_t kMinidumpModuleListIndex1 = 2; constexpr uint32_t kMinidumpModuleListIndex2 = 5; static constexpr char kKey2A[] = "K"; static constexpr char kValue2A[] = "VVV"; static constexpr char kKey2B[] = "river"; static constexpr char kValue2B[] = "hudson"; + const AnnotationSnapshot annotation2a("A2", 0xDDDD, {2, 4, 6, 8}); + const AnnotationSnapshot annotation2b("A3", 0xDDDF, {'m', 'o', 'o'}); StringFile string_file; @@ -216,6 +247,12 @@ TEST(MinidumpModuleCrashpadInfoWriter, ThreeModules) { std::move(simple_string_dictionary_entry_writer_0)); module_writer_0->SetSimpleAnnotations( std::move(simple_string_dictionary_writer_0)); + auto annotation_list_writer_0 = + std::make_unique(); + auto annotation_writer_0 = std::make_unique(); + annotation_writer_0->InitializeFromSnapshot(annotation0); + annotation_list_writer_0->AddObject(std::move(annotation_writer_0)); + module_writer_0->SetAnnotationObjects(std::move(annotation_list_writer_0)); EXPECT_TRUE(module_writer_0->IsUseful()); module_list_writer->AddModule(std::move(module_writer_0), kMinidumpModuleListIndex0); @@ -240,6 +277,15 @@ TEST(MinidumpModuleCrashpadInfoWriter, ThreeModules) { std::move(simple_string_dictionary_entry_writer_2b)); module_writer_2->SetSimpleAnnotations( std::move(simple_string_dictionary_writer_2)); + auto annotation_list_writer_2 = + std::make_unique(); + auto annotation_writer_2a = std::make_unique(); + annotation_writer_2a->InitializeFromSnapshot(annotation2a); + auto annotation_writer_2b = std::make_unique(); + annotation_writer_2b->InitializeFromSnapshot(annotation2b); + annotation_list_writer_2->AddObject(std::move(annotation_writer_2a)); + annotation_list_writer_2->AddObject(std::move(annotation_writer_2b)); + module_writer_2->SetAnnotationObjects(std::move(annotation_list_writer_2)); EXPECT_TRUE(module_writer_2->IsUseful()); module_list_writer->AddModule(std::move(module_writer_2), kMinidumpModuleListIndex2); @@ -279,6 +325,21 @@ TEST(MinidumpModuleCrashpadInfoWriter, ThreeModules) { string_file.string(), simple_annotations_0->entries[0].value), kValue0); + const MinidumpAnnotationList* annotation_list_0 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_0->annotation_objects); + ASSERT_TRUE(annotation_list_0); + + ASSERT_EQ(annotation_list_0->count, 1u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + annotation_list_0->objects[0].name), + annotation0.name); + EXPECT_EQ(annotation_list_0->objects[0].type, annotation0.type); + EXPECT_EQ(annotation_list_0->objects[0].reserved, 0u); + EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(), + annotation_list_0->objects[0].value), + annotation0.value); + EXPECT_EQ(module_list->modules[1].minidump_module_list_index, kMinidumpModuleListIndex1); const MinidumpModuleCrashpadInfo* module_1 = @@ -298,6 +359,11 @@ TEST(MinidumpModuleCrashpadInfoWriter, ThreeModules) { string_file.string(), module_1->simple_annotations); EXPECT_FALSE(simple_annotations_1); + const MinidumpAnnotationList* annotation_list_1 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_1->annotation_objects); + EXPECT_FALSE(annotation_list_1); + EXPECT_EQ(module_list->modules[2].minidump_module_list_index, kMinidumpModuleListIndex2); const MinidumpModuleCrashpadInfo* module_2 = @@ -330,6 +396,30 @@ TEST(MinidumpModuleCrashpadInfoWriter, ThreeModules) { EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( string_file.string(), simple_annotations_2->entries[1].value), kValue2B); + + const MinidumpAnnotationList* annotation_list_2 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_2->annotation_objects); + ASSERT_TRUE(annotation_list_2); + + ASSERT_EQ(annotation_list_2->count, 2u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + annotation_list_2->objects[0].name), + annotation2a.name); + EXPECT_EQ(annotation_list_2->objects[0].type, annotation2a.type); + EXPECT_EQ(annotation_list_2->objects[0].reserved, 0u); + EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(), + annotation_list_2->objects[0].value), + annotation2a.value); + + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + annotation_list_2->objects[1].name), + annotation2b.name); + EXPECT_EQ(annotation_list_2->objects[1].type, annotation2b.type); + EXPECT_EQ(annotation_list_2->objects[1].reserved, 0u); + EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(), + annotation_list_2->objects[1].value), + annotation2b.value); } TEST(MinidumpModuleCrashpadInfoWriter, InitializeFromSnapshot) { @@ -341,6 +431,8 @@ TEST(MinidumpModuleCrashpadInfoWriter, InitializeFromSnapshot) { static constexpr char kValue2[] = "different_value"; static constexpr char kEntry3A[] = "list"; static constexpr char kEntry3B[] = "erine"; + const AnnotationSnapshot annotation( + "market", 1, {'2', '3', 'r', 'd', ' ', 'S', 't', '.'}); std::vector module_snapshots; @@ -369,6 +461,10 @@ TEST(MinidumpModuleCrashpadInfoWriter, InitializeFromSnapshot) { module_snapshot_3.SetAnnotationsVector(annotations_vector_3); module_snapshots.push_back(&module_snapshot_3); + TestModuleSnapshot module_snapshot_4; + module_snapshot_4.SetAnnotationObjects({annotation}); + module_snapshots.push_back(&module_snapshot_4); + auto module_list_writer = std::make_unique(); module_list_writer->InitializeFromSnapshot(module_snapshots); @@ -378,7 +474,7 @@ TEST(MinidumpModuleCrashpadInfoWriter, InitializeFromSnapshot) { ASSERT_TRUE(module_list_writer->WriteEverything(&string_file)); const MinidumpModuleCrashpadInfoList* module_list = - MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 3); + MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 4); ASSERT_TRUE(module_list); EXPECT_EQ(module_list->modules[0].minidump_module_list_index, 0u); @@ -413,6 +509,9 @@ TEST(MinidumpModuleCrashpadInfoWriter, InitializeFromSnapshot) { string_file.string(), simple_annotations_0->entries[1].value), kValue0A); + EXPECT_FALSE(MinidumpWritableAtLocationDescriptor( + string_file.string(), module_0->annotation_objects)); + EXPECT_EQ(module_list->modules[1].minidump_module_list_index, 2u); const MinidumpModuleCrashpadInfo* module_2 = MinidumpWritableAtLocationDescriptor( @@ -439,6 +538,9 @@ TEST(MinidumpModuleCrashpadInfoWriter, InitializeFromSnapshot) { string_file.string(), simple_annotations_2->entries[0].value), kValue2); + EXPECT_FALSE(MinidumpWritableAtLocationDescriptor( + string_file.string(), module_2->annotation_objects)); + EXPECT_EQ(module_list->modules[2].minidump_module_list_index, 3u); const MinidumpModuleCrashpadInfo* module_3 = MinidumpWritableAtLocationDescriptor( @@ -464,6 +566,39 @@ TEST(MinidumpModuleCrashpadInfoWriter, InitializeFromSnapshot) { MinidumpWritableAtLocationDescriptor( string_file.string(), module_3->simple_annotations); EXPECT_FALSE(simple_annotations_3); + + EXPECT_FALSE(MinidumpWritableAtLocationDescriptor( + string_file.string(), module_3->annotation_objects)); + + EXPECT_EQ(module_list->modules[3].minidump_module_list_index, 4u); + const MinidumpModuleCrashpadInfo* module_4 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_list->modules[3].location); + ASSERT_TRUE(module_4); + + EXPECT_EQ(module_4->version, MinidumpModuleCrashpadInfo::kVersion); + + EXPECT_FALSE(MinidumpWritableAtLocationDescriptor( + string_file.string(), module_4->list_annotations)); + EXPECT_FALSE( + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_4->simple_annotations)); + + auto* annotation_list_4 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_4->annotation_objects); + ASSERT_TRUE(annotation_list_4); + + ASSERT_EQ(annotation_list_4->count, 1u); + + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + annotation_list_4->objects[0].name), + annotation.name); + EXPECT_EQ(annotation_list_4->objects[0].type, annotation.type); + EXPECT_EQ(annotation_list_4->objects[0].reserved, 0u); + EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(), + annotation_list_4->objects[0].value), + annotation.value); } } // namespace From 620a29dbf35656cc422ee3b0d45b7b9c3506bcdd Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Wed, 1 Nov 2017 16:24:09 -0400 Subject: [PATCH 004/326] Add support for reading annotation objects in ModuleSnapshotMinidump. This will be used to include the annotations as form-post data when uploading reports. Bug: crashpad:192 Change-Id: I85ba9afd3cae7c96c0f8fe4f31a2460c97ed42d3 Reviewed-on: https://chromium-review.googlesource.com/747514 Commit-Queue: Robert Sesek Reviewed-by: Mark Mentovai --- .../minidump/minidump_annotation_reader.cc | 119 ++++++++++++++++++ .../minidump/minidump_annotation_reader.h | 41 ++++++ snapshot/minidump/module_snapshot_minidump.cc | 23 +++- snapshot/minidump/module_snapshot_minidump.h | 2 + .../process_snapshot_minidump_test.cc | 92 +++++++++++++- snapshot/snapshot.gyp | 2 + 6 files changed, 270 insertions(+), 9 deletions(-) create mode 100644 snapshot/minidump/minidump_annotation_reader.cc create mode 100644 snapshot/minidump/minidump_annotation_reader.h diff --git a/snapshot/minidump/minidump_annotation_reader.cc b/snapshot/minidump/minidump_annotation_reader.cc new file mode 100644 index 00000000..b1e4f985 --- /dev/null +++ b/snapshot/minidump/minidump_annotation_reader.cc @@ -0,0 +1,119 @@ +// 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/minidump/minidump_annotation_reader.h" + +#include + +#include "base/logging.h" +#include "minidump/minidump_extensions.h" +#include "snapshot/minidump/minidump_string_reader.h" + +namespace crashpad { +namespace internal { + +namespace { + +bool ReadMinidumpByteArray(FileReaderInterface* file_reader, + RVA rva, + std::vector* data) { + if (rva == 0) { + data->clear(); + return true; + } + + if (!file_reader->SeekSet(rva)) { + return false; + } + + uint32_t length; + if (!file_reader->ReadExactly(&length, sizeof(length))) { + return false; + } + + std::vector local_data(length); + if (!file_reader->ReadExactly(local_data.data(), length)) { + return false; + } + + data->swap(local_data); + return true; +} + +} // namespace + +bool ReadMinidumpAnnotationList(FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + std::vector* list) { + if (location.Rva == 0) { + list->clear(); + return true; + } + + if (location.DataSize < sizeof(MinidumpAnnotationList)) { + LOG(ERROR) << "annotation list size mismatch"; + return false; + } + + if (!file_reader->SeekSet(location.Rva)) { + return false; + } + + uint32_t count; + if (!file_reader->ReadExactly(&count, sizeof(count))) { + return false; + } + + if (location.DataSize != + sizeof(MinidumpAnnotationList) + count * sizeof(MinidumpAnnotation)) { + LOG(ERROR) << "annotation object size mismatch"; + return false; + } + + std::vector minidump_annotations(count); + if (!file_reader->ReadExactly(minidump_annotations.data(), + count * sizeof(MinidumpAnnotation))) { + return false; + } + + std::vector annotations; + annotations.reserve(count); + + for (size_t i = 0; i < count; ++i) { + const MinidumpAnnotation* minidump_annotation = &minidump_annotations[i]; + + AnnotationSnapshot annotation; + // The client-exposed size of this field is 16-bit, but the minidump field + // is 32-bit for padding. Take just the lower part. + annotation.type = static_cast(minidump_annotation->type); + + if (!ReadMinidumpUTF8String( + file_reader, minidump_annotation->name, &annotation.name)) { + return false; + } + + if (!ReadMinidumpByteArray( + file_reader, minidump_annotation->value, &annotation.value)) { + return false; + } + + annotations.push_back(std::move(annotation)); + } + + list->swap(annotations); + return true; +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/minidump/minidump_annotation_reader.h b/snapshot/minidump/minidump_annotation_reader.h new file mode 100644 index 00000000..a5bafb5b --- /dev/null +++ b/snapshot/minidump/minidump_annotation_reader.h @@ -0,0 +1,41 @@ +// 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 SNAPSHOT_MINIDUMP_MINIDUMP_ANNOTATION_READER_H_ +#define SNAPSHOT_MINIDUMP_MINIDUMP_ANNOTATION_READER_H_ + +#include +#include + +#include + +#include "snapshot/annotation_snapshot.h" +#include "util/file/file_reader.h" + +namespace crashpad { +namespace internal { + +//! \brief Reads a MinidumpAnnotationList from a minidump file at \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 ReadMinidumpAnnotationList(FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + std::vector* list); + +} // namespace internal +} // namespace crashpad + +#endif // SNAPSHOT_MINIDUMP_MINIDUMP_ANNOTATION_READER_H_ diff --git a/snapshot/minidump/module_snapshot_minidump.cc b/snapshot/minidump/module_snapshot_minidump.cc index 195fc892..06cd1bb6 100644 --- a/snapshot/minidump/module_snapshot_minidump.cc +++ b/snapshot/minidump/module_snapshot_minidump.cc @@ -15,6 +15,7 @@ #include "snapshot/minidump/module_snapshot_minidump.h" #include "minidump/minidump_extensions.h" +#include "snapshot/minidump/minidump_annotation_reader.h" #include "snapshot/minidump/minidump_simple_string_dictionary_reader.h" #include "snapshot/minidump/minidump_string_list_reader.h" @@ -138,8 +139,7 @@ ModuleSnapshotMinidump::AnnotationsSimpleMap() const { std::vector ModuleSnapshotMinidump::AnnotationObjects() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); - return {}; + return annotation_objects_; } std::set> ModuleSnapshotMinidump::ExtraMemoryRanges() @@ -193,10 +193,21 @@ bool ModuleSnapshotMinidump::InitializeModuleCrashpadInfo( return false; } - return ReadMinidumpSimpleStringDictionary( - file_reader, - minidump_module_crashpad_info.simple_annotations, - &annotations_simple_map_); + if (!ReadMinidumpSimpleStringDictionary( + file_reader, + minidump_module_crashpad_info.simple_annotations, + &annotations_simple_map_)) { + return false; + } + + if (!ReadMinidumpAnnotationList( + file_reader, + minidump_module_crashpad_info.annotation_objects, + &annotation_objects_)) { + return false; + } + + return true; } } // namespace internal diff --git a/snapshot/minidump/module_snapshot_minidump.h b/snapshot/minidump/module_snapshot_minidump.h index ad65dbc9..e9dfa778 100644 --- a/snapshot/minidump/module_snapshot_minidump.h +++ b/snapshot/minidump/module_snapshot_minidump.h @@ -25,6 +25,7 @@ #include #include "base/macros.h" +#include "snapshot/annotation_snapshot.h" #include "snapshot/module_snapshot.h" #include "util/file/file_reader.h" #include "util/misc/initialization_state_dcheck.h" @@ -90,6 +91,7 @@ class ModuleSnapshotMinidump final : public ModuleSnapshot { MINIDUMP_MODULE minidump_module_; std::vector annotations_vector_; std::map annotations_simple_map_; + std::vector annotation_objects_; InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotMinidump); diff --git a/snapshot/minidump/process_snapshot_minidump_test.cc b/snapshot/minidump/process_snapshot_minidump_test.cc index cb42c396..82e14a7a 100644 --- a/snapshot/minidump/process_snapshot_minidump_test.cc +++ b/snapshot/minidump/process_snapshot_minidump_test.cc @@ -21,6 +21,7 @@ #include #include "gtest/gtest.h" +#include "snapshot/minidump/minidump_annotation_reader.h" #include "snapshot/module_snapshot.h" #include "util/file/string_file.h" @@ -66,7 +67,7 @@ TEST(ProcessSnapshotMinidump, Empty) { } // Writes |string| to |writer| as a MinidumpUTF8String, and returns the file -// offst of the beginning of the string. +// offset of the beginning of the string. RVA WriteString(FileWriterInterface* writer, const std::string& string) { RVA rva = static_cast(writer->SeekGet()); @@ -131,6 +132,48 @@ void WriteMinidumpStringList(MINIDUMP_LOCATION_DESCRIPTOR* location, rvas.size() * sizeof(RVA)); } +// Writes |data| to |writer| as a MinidumpByteArray, and returns the file offset +// from the beginning of the string. +RVA WriteByteArray(FileWriterInterface* writer, + const std::vector data) { + auto rva = static_cast(writer->SeekGet()); + + auto length = static_cast(data.size()); + EXPECT_TRUE(writer->Write(&length, sizeof(length))); + EXPECT_TRUE(writer->Write(data.data(), length)); + + return rva; +} + +// Writes |annotations| to |writer| as a MinidumpAnnotationList, and populates +// |location| with a location descriptor identifying what was written. +void WriteMinidumpAnnotationList( + MINIDUMP_LOCATION_DESCRIPTOR* location, + FileWriterInterface* writer, + const std::vector& annotations) { + std::vector minidump_annotations; + for (const auto& it : annotations) { + MinidumpAnnotation annotation; + annotation.name = WriteString(writer, it.name); + annotation.type = it.type; + annotation.reserved = 0; + annotation.value = WriteByteArray(writer, it.value); + minidump_annotations.push_back(annotation); + } + + location->Rva = static_cast(writer->SeekGet()); + + auto count = static_cast(minidump_annotations.size()); + EXPECT_TRUE(writer->Write(&count, sizeof(count))); + + for (const auto& it : minidump_annotations) { + EXPECT_TRUE(writer->Write(&it, sizeof(MinidumpAnnotation))); + } + + location->DataSize = + sizeof(MinidumpAnnotationList) + count * sizeof(MinidumpAnnotation); +} + TEST(ProcessSnapshotMinidump, ClientID) { StringFile string_file; @@ -215,6 +258,28 @@ TEST(ProcessSnapshotMinidump, AnnotationsSimpleMap) { EXPECT_EQ(annotations_simple_map, dictionary); } +TEST(ProcessSnapshotMinidump, AnnotationObjects) { + StringFile string_file; + + MINIDUMP_HEADER header{}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + std::vector annotations; + annotations.emplace_back( + AnnotationSnapshot("name 1", 0xBBBB, {'t', 'e', '\0', 's', 't', '\0'})); + annotations.emplace_back( + AnnotationSnapshot("name 2", 0xABBA, {0xF0, 0x9F, 0x92, 0x83})); + + MINIDUMP_LOCATION_DESCRIPTOR location; + WriteMinidumpAnnotationList(&location, &string_file, annotations); + + std::vector read_annotations; + EXPECT_TRUE(internal::ReadMinidumpAnnotationList( + &string_file, location, &read_annotations)); + + EXPECT_EQ(read_annotations, annotations); +} + TEST(ProcessSnapshotMinidump, Modules) { StringFile string_file; @@ -222,7 +287,7 @@ TEST(ProcessSnapshotMinidump, Modules) { EXPECT_TRUE(string_file.Write(&header, sizeof(header))); MINIDUMP_MODULE minidump_module = {}; - uint32_t minidump_module_count = 3; + uint32_t minidump_module_count = 4; MINIDUMP_DIRECTORY minidump_module_list_directory = {}; minidump_module_list_directory.StreamType = kMinidumpStreamTypeModuleList; @@ -273,10 +338,26 @@ TEST(ProcessSnapshotMinidump, Modules) { crashpad_module_2_link.location.Rva = static_cast(string_file.SeekGet()); EXPECT_TRUE(string_file.Write(&crashpad_module_2, sizeof(crashpad_module_2))); + MinidumpModuleCrashpadInfo crashpad_module_4 = {}; + crashpad_module_4.version = MinidumpModuleCrashpadInfo::kVersion; + std::vector annotations_4{ + {"first one", 0xBADE, {'a', 'b', 'c'}}, + {"2", 0xEDD1, {0x11, 0x22, 0x33}}, + {"threeeeee", 0xDADA, {'f'}}, + }; + WriteMinidumpAnnotationList( + &crashpad_module_4.annotation_objects, &string_file, annotations_4); + + MinidumpModuleCrashpadInfoLink crashpad_module_4_link = {}; + crashpad_module_4_link.minidump_module_list_index = 3; + crashpad_module_4_link.location.DataSize = sizeof(crashpad_module_4); + crashpad_module_4_link.location.Rva = static_cast(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_module_4, sizeof(crashpad_module_4))); + MinidumpCrashpadInfo crashpad_info = {}; crashpad_info.version = MinidumpCrashpadInfo::kVersion; - uint32_t crashpad_module_count = 2; + uint32_t crashpad_module_count = 3; crashpad_info.module_list.DataSize = sizeof(MinidumpModuleCrashpadInfoList) + @@ -289,6 +370,8 @@ TEST(ProcessSnapshotMinidump, Modules) { sizeof(crashpad_module_0_link))); EXPECT_TRUE(string_file.Write(&crashpad_module_2_link, sizeof(crashpad_module_2_link))); + EXPECT_TRUE(string_file.Write(&crashpad_module_4_link, + sizeof(crashpad_module_4_link))); MINIDUMP_DIRECTORY crashpad_info_directory = {}; crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo; @@ -332,6 +415,9 @@ TEST(ProcessSnapshotMinidump, Modules) { annotations_vector = modules[2]->AnnotationsVector(); EXPECT_EQ(annotations_vector, list_annotations_2); + + auto annotation_objects = modules[3]->AnnotationObjects(); + EXPECT_EQ(annotation_objects, annotations_4); } } // namespace diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index 73a5da04..d50d1085 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -101,6 +101,8 @@ 'mac/thread_snapshot_mac.cc', 'mac/thread_snapshot_mac.h', 'memory_snapshot.h', + 'minidump/minidump_annotation_reader.cc', + 'minidump/minidump_annotation_reader.h', 'minidump/minidump_simple_string_dictionary_reader.cc', 'minidump/minidump_simple_string_dictionary_reader.h', 'minidump/minidump_string_list_reader.cc', From 2db30e035a0084493ef44bdcc5887a8147f74add Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Wed, 1 Nov 2017 20:27:19 -0400 Subject: [PATCH 005/326] win: Fix Clang -Wsign-compare warnings in new test code This test code appeared in 9609b7471676, and was missed by the similar warning cleanup of a51e912004a6, which was developed in parallel. Bug: crashpad:192, chromium:779790 Change-Id: I4ed88ed025e4be4410c98ceaca395218f00007be Reviewed-on: https://chromium-review.googlesource.com/750024 Reviewed-by: Robert Sesek --- snapshot/win/pe_image_annotations_reader_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapshot/win/pe_image_annotations_reader_test.cc b/snapshot/win/pe_image_annotations_reader_test.cc index f576394c..e1026008 100644 --- a/snapshot/win/pe_image_annotations_reader_test.cc +++ b/snapshot/win/pe_image_annotations_reader_test.cc @@ -102,7 +102,7 @@ void TestAnnotationsOnCrash(TestType type, EXPECT_EQ(all_annotations_simple_map["#TEST# empty_value"], ""); // Verify the typed annotation objects. - EXPECT_EQ(all_annotation_objects.size(), 3); + EXPECT_EQ(all_annotation_objects.size(), 3u); bool saw_same_name_3 = false, saw_same_name_4 = false; for (const auto& annotation : all_annotation_objects) { EXPECT_EQ(annotation.type, From 4d7d4dd56cf0fb2fae5a09ac2cf38259f43ba339 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Thu, 2 Nov 2017 11:31:32 -0400 Subject: [PATCH 006/326] Flip gtest _EQ parameter order in minidump_annotation_writer_test.cc Bug: crashpad:192 Change-Id: I82dddda5ba3d4fe5bf843572e6a793131cb6fa40 Reviewed-on: https://chromium-review.googlesource.com/751441 Reviewed-by: Mark Mentovai Commit-Queue: Robert Sesek --- minidump/minidump_annotation_writer_test.cc | 64 +++++++++++---------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/minidump/minidump_annotation_writer_test.cc b/minidump/minidump_annotation_writer_test.cc index 9b4e80de..1641d1d6 100644 --- a/minidump/minidump_annotation_writer_test.cc +++ b/minidump/minidump_annotation_writer_test.cc @@ -52,7 +52,7 @@ TEST(MinidumpAnnotationWriter, EmptyList) { auto* list = MinidumpAnnotationListAtStart(string_file.string(), 0); ASSERT_TRUE(list); - EXPECT_EQ(0u, list->count); + EXPECT_EQ(list->count, 0u); } TEST(MinidumpAnnotationWriter, OneItem) { @@ -80,15 +80,16 @@ TEST(MinidumpAnnotationWriter, OneItem) { 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(list->count, 1u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + list->objects[0].name), + kName); + EXPECT_EQ(list->objects[0].type, kType); + EXPECT_EQ(list->objects[0].reserved, 0u); EXPECT_EQ( - kValue, - MinidumpByteArrayAtRVA(string_file.string(), list->objects[0].value)); + + MinidumpByteArrayAtRVA(string_file.string(), list->objects[0].value), + kValue); } TEST(MinidumpAnnotationWriter, ThreeItems) { @@ -123,17 +124,18 @@ TEST(MinidumpAnnotationWriter, ThreeItems) { auto* list = MinidumpAnnotationListAtStart(string_file.string(), 3); ASSERT_TRUE(list); - EXPECT_EQ(3u, list->count); + EXPECT_EQ(list->count, 3u); 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(MinidumpUTF8StringAtRVAAsString(string_file.string(), + list->objects[i].name), + kNames[i]); + EXPECT_EQ(list->objects[i].type, kTypes[i]); + EXPECT_EQ(list->objects[i].reserved, 0u); EXPECT_EQ( - kValues[i], - MinidumpByteArrayAtRVA(string_file.string(), list->objects[i].value)); + + MinidumpByteArrayAtRVA(string_file.string(), list->objects[i].value), + kValues[i]); } } @@ -164,23 +166,25 @@ TEST(MinidumpAnnotationWriter, DuplicateNames) { auto* list = MinidumpAnnotationListAtStart(string_file.string(), 2); ASSERT_TRUE(list); - EXPECT_EQ(2u, list->count); + EXPECT_EQ(list->count, 2u); - EXPECT_EQ(kName, - MinidumpUTF8StringAtRVAAsString(string_file.string(), - list->objects[0].name)); - EXPECT_EQ(kType, list->objects[0].type); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + list->objects[0].name), + kName); + EXPECT_EQ(list->objects[0].type, kType); 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); + MinidumpByteArrayAtRVA(string_file.string(), list->objects[0].value), + kValue1); + + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + list->objects[1].name), + kName); + EXPECT_EQ(list->objects[1].type, kType); EXPECT_EQ( - kValue2, - MinidumpByteArrayAtRVA(string_file.string(), list->objects[1].value)); + + MinidumpByteArrayAtRVA(string_file.string(), list->objects[1].value), + kValue2); } } // namespace From 79e2dd843e1f86cf35f398dc578485f0c2410823 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Thu, 2 Nov 2017 11:55:44 -0400 Subject: [PATCH 007/326] Include string annotation objects when uploading crash reports. This extracts string annotation objects from the minidumps and includes them as form POST key-value pairs. This change also starts building a crashpad_handler_test binary on Mac. Bug: crashpad:192 Change-Id: I68cbf6fda6f1e57c1e621d5e3de8717cfaea65bf Reviewed-on: https://chromium-review.googlesource.com/749793 Commit-Queue: Robert Sesek Reviewed-by: Mark Mentovai --- build/run_tests.py | 5 +- handler/crash_report_upload_thread.cc | 69 ++----------- handler/handler.gyp | 2 + handler/handler_test.gyp | 56 ++++++----- handler/minidump_to_upload_parameters.cc | 86 ++++++++++++++++ handler/minidump_to_upload_parameters.h | 61 ++++++++++++ handler/minidump_to_upload_parameters_test.cc | 99 +++++++++++++++++++ 7 files changed, 289 insertions(+), 89 deletions(-) create mode 100644 handler/minidump_to_upload_parameters.cc create mode 100644 handler/minidump_to_upload_parameters.h create mode 100644 handler/minidump_to_upload_parameters_test.cc diff --git a/build/run_tests.py b/build/run_tests.py index 3eae77a5..10bd052d 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -46,16 +46,13 @@ def main(args): tests = [ 'crashpad_client_test', + 'crashpad_handler_test', 'crashpad_minidump_test', 'crashpad_snapshot_test', 'crashpad_test_test', 'crashpad_util_test', ] - if sys.platform == 'win32': - tests.append('crashpad_handler_test') - tests = sorted(tests) - for test in tests: print '-' * 80 print test diff --git a/handler/crash_report_upload_thread.cc b/handler/crash_report_upload_thread.cc index 7038108a..7505524b 100644 --- a/handler/crash_report_upload_thread.cc +++ b/handler/crash_report_upload_thread.cc @@ -27,6 +27,7 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "client/settings.h" +#include "handler/minidump_to_upload_parameters.h" #include "snapshot/minidump/process_snapshot_minidump.h" #include "snapshot/module_snapshot.h" #include "util/file/file_reader.h" @@ -46,68 +47,6 @@ namespace crashpad { namespace { -void InsertOrReplaceMapEntry(std::map* map, - const std::string& key, - const std::string& value) { - std::string old_value; - if (!MapInsertOrReplace(map, key, value, &old_value)) { - LOG(WARNING) << "duplicate key " << key << ", discarding value " - << old_value; - } -} - -// Given a minidump file readable by |minidump_file_reader|, returns a map of -// key-value pairs to use as HTTP form parameters for upload to a Breakpad -// server. The map is built by combining the process simple annotations map with -// each module’s simple annotations map. In the case of duplicate keys, the map -// will retain the first value found for any key, and will log a warning about -// discarded values. Each module’s annotations vector is also examined and built -// into a single string value, with distinct elements separated by newlines, and -// stored at the key named “list_annotations”, which supersedes any other key -// found by that name. The client ID stored in the minidump is converted to -// a string and stored at the key named “guid”, which supersedes any other key -// found by that name. -// -// In the event of an error reading the minidump file, a message will be logged. -std::map BreakpadHTTPFormParametersFromMinidump( - FileReaderInterface* minidump_file_reader) { - ProcessSnapshotMinidump minidump_process_snapshot; - if (!minidump_process_snapshot.Initialize(minidump_file_reader)) { - return std::map(); - } - - std::map parameters = - minidump_process_snapshot.AnnotationsSimpleMap(); - - std::string list_annotations; - for (const ModuleSnapshot* module : minidump_process_snapshot.Modules()) { - for (const auto& kv : module->AnnotationsSimpleMap()) { - if (!parameters.insert(kv).second) { - LOG(WARNING) << "duplicate key " << kv.first << ", discarding value " - << kv.second; - } - } - - for (std::string annotation : module->AnnotationsVector()) { - list_annotations.append(annotation); - list_annotations.append("\n"); - } - } - - if (!list_annotations.empty()) { - // Remove the final newline character. - list_annotations.resize(list_annotations.size() - 1); - - InsertOrReplaceMapEntry(¶meters, "list_annotations", list_annotations); - } - - UUID client_id; - minidump_process_snapshot.ClientID(&client_id); - InsertOrReplaceMapEntry(¶meters, "guid", client_id.ToString()); - - return parameters; -} - // Calls CrashReportDatabase::RecordUploadAttempt() with |successful| set to // false upon destruction unless disarmed by calling Fire() or Disarm(). Fire() // triggers an immediate call. Armed upon construction. @@ -359,7 +298,11 @@ CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport( // minidump file. This may result in its being uploaded with few or no // parameters, but as long as there’s a dump file, the server can decide what // to do with it. - parameters = BreakpadHTTPFormParametersFromMinidump(&minidump_file_reader); + ProcessSnapshotMinidump minidump_process_snapshot; + if (minidump_process_snapshot.Initialize(&minidump_file_reader)) { + parameters = + BreakpadHTTPFormParametersFromMinidump(&minidump_process_snapshot); + } if (!minidump_file_reader.SeekSet(start_offset)) { return UploadResult::kPermanentFailure; diff --git a/handler/handler.gyp b/handler/handler.gyp index aefd4de5..ff455dee 100644 --- a/handler/handler.gyp +++ b/handler/handler.gyp @@ -45,6 +45,8 @@ 'mac/exception_handler_server.h', 'mac/file_limit_annotation.cc', 'mac/file_limit_annotation.h', + 'minidump_to_upload_parameters.cc', + 'minidump_to_upload_parameters.h', 'prune_crash_reports_thread.cc', 'prune_crash_reports_thread.h', 'user_stream_data_source.cc', diff --git a/handler/handler_test.gyp b/handler/handler_test.gyp index 588305f8..4712c055 100644 --- a/handler/handler_test.gyp +++ b/handler/handler_test.gyp @@ -17,6 +17,40 @@ '../build/crashpad.gypi', ], 'targets': [ + { + 'target_name': 'crashpad_handler_test', + 'type': 'executable', + 'dependencies': [ + 'crashpad_handler_test_extended_handler', + 'handler.gyp:crashpad_handler_lib', + '../client/client.gyp:crashpad_client', + '../compat/compat.gyp:crashpad_compat', + '../snapshot/snapshot.gyp:crashpad_snapshot', + '../snapshot/snapshot_test.gyp:crashpad_snapshot_test_lib', + '../test/test.gyp:crashpad_gtest_main', + '../test/test.gyp:crashpad_test', + '../third_party/gtest/gtest.gyp:gtest', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'crashpad_handler_test.cc', + 'minidump_to_upload_parameters_test.cc', + ], + 'conditions': [ + ['OS!="win"', { + 'dependencies!': [ + 'crashpad_handler_test_extended_handler', + ], + 'sources!': [ + 'crashpad_handler_test.cc', + ], + }], + ], + }, { 'target_name': 'crashpad_handler_test_extended_handler', 'type': 'executable', @@ -52,28 +86,6 @@ 'win/crash_other_program.cc', ], }, - { - # The handler is only tested on Windows for now. - 'target_name': 'crashpad_handler_test', - 'type': 'executable', - 'dependencies': [ - 'crashpad_handler_test_extended_handler', - 'handler.gyp:crashpad_handler_lib', - '../client/client.gyp:crashpad_client', - '../compat/compat.gyp:crashpad_compat', - '../test/test.gyp:crashpad_gtest_main', - '../test/test.gyp:crashpad_test', - '../third_party/gtest/gtest.gyp:gtest', - '../third_party/mini_chromium/mini_chromium.gyp:base', - '../util/util.gyp:crashpad_util', - ], - 'include_dirs': [ - '..', - ], - 'sources': [ - 'crashpad_handler_test.cc', - ], - }, { 'target_name': 'crashy_program', 'type': 'executable', diff --git a/handler/minidump_to_upload_parameters.cc b/handler/minidump_to_upload_parameters.cc new file mode 100644 index 00000000..03c6fe14 --- /dev/null +++ b/handler/minidump_to_upload_parameters.cc @@ -0,0 +1,86 @@ +// 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 "handler/minidump_to_upload_parameters.h" + +#include "base/logging.h" +#include "client/annotation.h" +#include "snapshot/module_snapshot.h" +#include "util/stdlib/map_insert.h" + +namespace crashpad { + +namespace { + +void InsertOrReplaceMapEntry(std::map* map, + const std::string& key, + const std::string& value) { + std::string old_value; + if (!MapInsertOrReplace(map, key, value, &old_value)) { + LOG(WARNING) << "duplicate key " << key << ", discarding value " + << old_value; + } +} + +} // namespace + +std::map BreakpadHTTPFormParametersFromMinidump( + const ProcessSnapshot* process_snapshot) { + std::map parameters = + process_snapshot->AnnotationsSimpleMap(); + + std::string list_annotations; + for (const ModuleSnapshot* module : process_snapshot->Modules()) { + for (const auto& kv : module->AnnotationsSimpleMap()) { + if (!parameters.insert(kv).second) { + LOG(WARNING) << "duplicate key " << kv.first << ", discarding value " + << kv.second; + } + } + + for (std::string annotation : module->AnnotationsVector()) { + list_annotations.append(annotation); + list_annotations.append("\n"); + } + + for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) { + if (annotation.type != static_cast(Annotation::Type::kString)) { + continue; + } + + std::string value(reinterpret_cast(annotation.value.data()), + annotation.value.size()); + std::pair entry(annotation.name, value); + if (!parameters.insert(entry).second) { + LOG(WARNING) << "duplicate annotation name " << annotation.name + << ", discarding value " << value; + } + } + } + + if (!list_annotations.empty()) { + // Remove the final newline character. + list_annotations.resize(list_annotations.size() - 1); + + InsertOrReplaceMapEntry(¶meters, "list_annotations", list_annotations); + } + + UUID client_id; + process_snapshot->ClientID(&client_id); + InsertOrReplaceMapEntry(¶meters, "guid", client_id.ToString()); + + return parameters; +} + +} // namespace crashpad diff --git a/handler/minidump_to_upload_parameters.h b/handler/minidump_to_upload_parameters.h new file mode 100644 index 00000000..41056f70 --- /dev/null +++ b/handler/minidump_to_upload_parameters.h @@ -0,0 +1,61 @@ +// 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 HANDLER_MINIDUMP_TO_UPLOAD_PARAMETERS_H_ +#define HANDLER_MINIDUMP_TO_UPLOAD_PARAMETERS_H_ + +#include +#include + +#include "snapshot/process_snapshot.h" + +namespace crashpad { + +//! \brief Given a ProcessSnapshot, returns a map of key-value pairs to use as +//! HTTP form parameters for upload to a Breakpad crash report colleciton +//! server. +//! +//! The map is built by combining the process simple annotations map with +//! each module’s simple annotations map and annotation objects. +//! +//! In the case of duplicate simple map keys or annotation names, the map will +//! retain the first value found for any key, and will log a warning about +//! discarded values. The precedence rules for annotation names are: the two +//! reserved keys discussed below, process simple annotations, module simple +//! annotations, and module annotation objects. +//! +//! For annotation objects, only ones of that are Annotation::Type::kString are +//! included. +//! +//! Each module’s annotations vector is also examined and built into a single +//! string value, with distinct elements separated by newlines, and stored at +//! the key named “list_annotations”, which supersedes any other key found by +//! that name. +//! +//! The client ID stored in the minidump is converted to a string and stored at +//! the key named “guid”, which supersedes any other key found by that name. +//! +//! In the event of an error reading the minidump file, a message will be +//! logged. +//! +//! \param[in] process_snapshot The process snapshot from which annotations +//! will be extracted. +//! +//! \returns A string map of the annotations. +std::map BreakpadHTTPFormParametersFromMinidump( + const ProcessSnapshot* process_snapshot); + +} // namespace crashpad + +#endif // HANDLER_MINIDUMP_TO_UPLOAD_PARAMETERS_H_ diff --git a/handler/minidump_to_upload_parameters_test.cc b/handler/minidump_to_upload_parameters_test.cc new file mode 100644 index 00000000..0c3a0e7c --- /dev/null +++ b/handler/minidump_to_upload_parameters_test.cc @@ -0,0 +1,99 @@ +// 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 "handler/minidump_to_upload_parameters.h" + +#include "gtest/gtest.h" +#include "snapshot/test/test_module_snapshot.h" +#include "snapshot/test/test_process_snapshot.h" +#include "util/misc/uuid.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(MinidumpToUploadParameters, PrecedenceRules) { + const std::string guid = "00112233-4455-6677-8899-aabbccddeeff"; + UUID uuid; + ASSERT_TRUE(uuid.InitializeFromString(guid)); + + TestProcessSnapshot process_snapshot; + process_snapshot.SetClientID(uuid); + process_snapshot.SetAnnotationsSimpleMap({ + {"process-1", "abcdefg"}, + {"list_annotations", "BAD: process_annotations"}, + {"guid", "BAD: process_annotations"}, + {"first", "process"}, + }); + + auto module_snapshot_0 = std::make_unique(); + module_snapshot_0->SetAnnotationsVector( + {"list-module-0-1", "list-module-0-2"}); + module_snapshot_0->SetAnnotationsSimpleMap({ + {"module-0-1", "goat"}, + {"module-0-2", "doge"}, + {"list_annotations", "BAD: module 0"}, + {"guid", "BAD: module 0"}, + {"first", "BAD: module 0"}, + {"second", "module 0"}, + }); + module_snapshot_0->SetAnnotationObjects({ + {"module-0-3", 1, {'s', 't', 'a', 'r'}}, + {"module-0-4", 0xFFFA, {0x42}}, + {"guid", 1, {'B', 'A', 'D', '*', '0', '-', '0'}}, + {"list_annotations", 1, {'B', 'A', 'D', '*', '0', '-', '1'}}, + {"first", 1, {'B', 'A', 'D', '*', '0', '-', '2'}}, + }); + process_snapshot.AddModule(std::move(module_snapshot_0)); + + auto module_snapshot_1 = std::make_unique(); + module_snapshot_1->SetAnnotationsVector( + {"list-module-1-1", "list-module-1-2"}); + module_snapshot_1->SetAnnotationsSimpleMap({ + {"module-1-1", "bear"}, + {"list_annotations", "BAD: module 1"}, + {"guid", "BAD: module 1"}, + {"first", "BAD: module 1"}, + {"second", "BAD: module 1"}, + }); + module_snapshot_1->SetAnnotationObjects({ + {"module-1-3", 0xBEEF, {'a', 'b', 'c'}}, + {"module-1-4", 1, {'m', 'o', 'o', 'n'}}, + {"guid", 1, {'B', 'A', 'D', '*', '1', '-', '0'}}, + {"list_annotations", 1, {'B', 'A', 'D', '*', '1', '-', '1'}}, + {"second", 1, {'B', 'A', 'D', '*', '1', '-', '2'}}, + }); + process_snapshot.AddModule(std::move(module_snapshot_1)); + + auto upload_parameters = + BreakpadHTTPFormParametersFromMinidump(&process_snapshot); + + EXPECT_EQ(upload_parameters.size(), 10u); + EXPECT_EQ(upload_parameters["process-1"], "abcdefg"); + EXPECT_EQ(upload_parameters["first"], "process"); + EXPECT_EQ(upload_parameters["module-0-1"], "goat"); + EXPECT_EQ(upload_parameters["module-0-2"], "doge"); + EXPECT_EQ(upload_parameters["module-0-3"], "star"); + EXPECT_EQ(upload_parameters["second"], "module 0"); + EXPECT_EQ(upload_parameters["module-1-1"], "bear"); + EXPECT_EQ(upload_parameters["module-1-4"], "moon"); + EXPECT_EQ(upload_parameters["list_annotations"], + "list-module-0-1\nlist-module-0-2\n" + "list-module-1-1\nlist-module-1-2"); + EXPECT_EQ(upload_parameters["guid"], guid); +} + +} // namespace +} // namespace test +} // namespace crashpad From b6a3d913421eb1f0ad05cc5dce6fde0c1342f71c Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Wed, 1 Nov 2017 19:09:29 -0400 Subject: [PATCH 008/326] Read annotation objects from the client when producing snapshots. This wires up the annotation objects system of the client to the snapshot production and minidump writing facilities. Bug: crashpad:192 Change-Id: If7bb7625b140d71a15b84729372cbd0fd4bc63ef Reviewed-on: https://chromium-review.googlesource.com/749870 Reviewed-by: Mark Mentovai Commit-Queue: Robert Sesek --- snapshot/mac/module_snapshot_mac.cc | 4 +++- snapshot/win/module_snapshot_win.cc | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/snapshot/mac/module_snapshot_mac.cc b/snapshot/mac/module_snapshot_mac.cc index d04c0e2e..19c4759d 100644 --- a/snapshot/mac/module_snapshot_mac.cc +++ b/snapshot/mac/module_snapshot_mac.cc @@ -187,7 +187,9 @@ std::map ModuleSnapshotMac::AnnotationsSimpleMap() std::vector ModuleSnapshotMac::AnnotationObjects() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return {}; + MachOImageAnnotationsReader annotations_reader( + process_reader_, mach_o_image_reader_, name_); + return annotations_reader.AnnotationsList(); } std::set> ModuleSnapshotMac::ExtraMemoryRanges() const { diff --git a/snapshot/win/module_snapshot_win.cc b/snapshot/win/module_snapshot_win.cc index c3a83fdf..8b6bb646 100644 --- a/snapshot/win/module_snapshot_win.cc +++ b/snapshot/win/module_snapshot_win.cc @@ -192,7 +192,9 @@ std::map ModuleSnapshotWin::AnnotationsSimpleMap() std::vector ModuleSnapshotWin::AnnotationObjects() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return {}; + PEImageAnnotationsReader annotations_reader( + process_reader_, pe_image_reader_.get(), name_); + return annotations_reader.AnnotationsList(); } std::set> ModuleSnapshotWin::ExtraMemoryRanges() const { From d768538e39f3e53dbc1f22ba018737b80641b049 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Fri, 3 Nov 2017 08:54:54 -0700 Subject: [PATCH 009/326] Add ProcessSnapshotLinux Bug: crashpad:30 Change-Id: Ie03592aeb91741d957b98716e4d4bb19695a42cf Reviewed-on: https://chromium-review.googlesource.com/604627 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- snapshot/linux/process_snapshot_linux.cc | 185 +++++++++++++++++++++++ snapshot/linux/process_snapshot_linux.h | 127 ++++++++++++++++ snapshot/snapshot.gyp | 2 + util/linux/exception_information.h | 45 ++++++ util/util.gyp | 1 + 5 files changed, 360 insertions(+) create mode 100644 snapshot/linux/process_snapshot_linux.cc create mode 100644 snapshot/linux/process_snapshot_linux.h create mode 100644 util/linux/exception_information.h diff --git a/snapshot/linux/process_snapshot_linux.cc b/snapshot/linux/process_snapshot_linux.cc new file mode 100644 index 00000000..346da085 --- /dev/null +++ b/snapshot/linux/process_snapshot_linux.cc @@ -0,0 +1,185 @@ +// 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/linux/process_snapshot_linux.h" + +#include + +#include "base/logging.h" +#include "util/linux/exception_information.h" + +namespace crashpad { + +ProcessSnapshotLinux::ProcessSnapshotLinux() + : ProcessSnapshot(), + annotations_simple_map_(), + snapshot_time_(), + report_id_(), + client_id_(), + threads_(), + exception_(), + system_(), + process_reader_(), + initialized_() {} + +ProcessSnapshotLinux::~ProcessSnapshotLinux() {} + +bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (gettimeofday(&snapshot_time_, nullptr) != 0) { + PLOG(ERROR) << "gettimeofday"; + return false; + } + + if (!process_reader_.Initialize(connection)) { + return false; + } + + system_.Initialize(&process_reader_, &snapshot_time_); + InitializeThreads(); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessSnapshotLinux::InitializeException( + LinuxVMAddress exception_info_address) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + DCHECK(!exception_); + + ExceptionInformation info; + if (!process_reader_.Memory()->Read( + exception_info_address, sizeof(info), &info)) { + LOG(ERROR) << "Couldn't read exception info"; + return false; + } + + exception_.reset(new internal::ExceptionSnapshotLinux()); + if (!exception_->Initialize(&process_reader_, + info.siginfo_address, + info.context_address, + info.thread_id)) { + exception_.reset(); + return false; + } + + return true; +} + +pid_t ProcessSnapshotLinux::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.ProcessID(); +} + +pid_t ProcessSnapshotLinux::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.ParentProcessID(); +} + +void ProcessSnapshotLinux::SnapshotTime(timeval* snapshot_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *snapshot_time = snapshot_time_; +} + +void ProcessSnapshotLinux::ProcessStartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + process_reader_.StartTime(start_time); +} + +void ProcessSnapshotLinux::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + process_reader_.CPUTimes(user_time, system_time); +} + +void ProcessSnapshotLinux::ReportID(UUID* report_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *report_id = report_id_; +} + +void ProcessSnapshotLinux::ClientID(UUID* client_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *client_id = client_id_; +} + +const std::map& +ProcessSnapshotLinux::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +const SystemSnapshot* ProcessSnapshotLinux::System() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &system_; +} + +std::vector ProcessSnapshotLinux::Threads() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector threads; + for (const auto& thread : threads_) { + threads.push_back(thread.get()); + } + return threads; +} + +std::vector ProcessSnapshotLinux::Modules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(jperaza): do this. + LOG(ERROR) << "Not implemented"; + return std::vector(); +} + +std::vector ProcessSnapshotLinux::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(jperaza): Can this be implemented on Linux? + return std::vector(); +} + +const ExceptionSnapshot* ProcessSnapshotLinux::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_.get(); +} + +std::vector ProcessSnapshotLinux::MemoryMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(jperaza): do this. + return std::vector(); +} + +std::vector ProcessSnapshotLinux::Handles() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::vector ProcessSnapshotLinux::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +void ProcessSnapshotLinux::InitializeThreads() { + const std::vector& process_reader_threads = + process_reader_.Threads(); + for (const ProcessReader::Thread& process_reader_thread : + process_reader_threads) { + auto thread = std::make_unique(); + if (thread->Initialize(&process_reader_, process_reader_thread)) { + threads_.push_back(std::move(thread)); + } + } +} + +} // namespace crashpad diff --git a/snapshot/linux/process_snapshot_linux.h b/snapshot/linux/process_snapshot_linux.h new file mode 100644 index 00000000..446ddb54 --- /dev/null +++ b/snapshot/linux/process_snapshot_linux.h @@ -0,0 +1,127 @@ +// 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_LINUX_PROCESS_SNAPSHOT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_PROCESS_SNAPSHOT_LINUX_H_ + +#include +#include + +#include +#include +#include +#include + +#include "base/macros.h" +#include "snapshot/linux/exception_snapshot_linux.h" +#include "snapshot/linux/process_reader.h" +#include "snapshot/linux/system_snapshot_linux.h" +#include "snapshot/linux/thread_snapshot_linux.h" +#include "snapshot/memory_map_region_snapshot.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" +#include "util/linux/ptrace_connection.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +//! \brief A ProcessSnapshot of a running (or crashed) process running on a +//! Linux system. +class ProcessSnapshotLinux final : public ProcessSnapshot { + public: + ProcessSnapshotLinux(); + ~ProcessSnapshotLinux() override; + + //! \brief Initializes the object. + //! + //! \param[in] connection A connection to the process to snapshot. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(PtraceConnection* connection); + + //! \brief Initializes the object's exception. + //! + //! \param[in] exception_info The address of an ExceptionInformation in the + //! target process' address space. + bool InitializeException(LinuxVMAddress exception_info); + + //! \brief Sets the value to be returned by ReportID(). + //! + //! The crash report ID is under the control of the snapshot + //! producer, which may call this method to set the report ID. If this is not + //! done, ReportID() will return an identifier consisting entirely of zeroes. + void SetReportID(const UUID& report_id) { report_id_ = report_id; } + + //! \brief Sets the value to be returned by ClientID(). + //! + //! The client ID is under the control of the snapshot producer, + //! which may call this method to set the client ID. If this is not done, + //! ClientID() will return an identifier consisting entirely of zeroes. + void SetClientID(const UUID& client_id) { client_id_ = client_id; } + + //! \brief Sets the value to be returned by AnnotationsSimpleMap(). + //! + //! All process annotations are under the control of the snapshot + //! producer, which may call this method to establish these annotations. + //! Contrast this with module annotations, which are under the control of the + //! process being snapshotted. + void SetAnnotationsSimpleMap( + const std::map& annotations_simple_map) { + annotations_simple_map_ = annotations_simple_map; + } + + // 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; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector Threads() const override; + std::vector Modules() const override; + std::vector UnloadedModules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector MemoryMap() const override; + std::vector Handles() const override; + std::vector ExtraMemory() const override; + + private: + void InitializeThreads(); + + std::map annotations_simple_map_; + timeval snapshot_time_; + UUID report_id_; + UUID client_id_; + std::vector> threads_; + std::unique_ptr exception_; + internal::SystemSnapshotLinux system_; + ProcessReader process_reader_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotLinux); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_PROCESS_SNAPSHOT_LINUX_H_ diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index d50d1085..d2fc91f5 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -58,6 +58,8 @@ 'linux/memory_snapshot_linux.h', 'linux/process_reader.cc', 'linux/process_reader.h', + 'linux/process_snapshot_linux.cc', + 'linux/process_snapshot_linux.h', 'linux/signal_context.h', 'linux/system_snapshot_linux.cc', 'linux/system_snapshot_linux.h', diff --git a/util/linux/exception_information.h b/util/linux/exception_information.h new file mode 100644 index 00000000..d7dbe529 --- /dev/null +++ b/util/linux/exception_information.h @@ -0,0 +1,45 @@ +// 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_UTIL_LINUX_EXCEPTION_INFORMATION_H_ +#define CRASHPAD_UTIL_LINUX_EXCEPTION_INFORMATION_H_ + +#include + +#include "util/linux/address_types.h" + +namespace crashpad { + +#pragma pack(push, 1) + +//! \brief Structure read out of the client process by the crash handler when an +//! exception occurs. +struct ExceptionInformation { + //! \brief The address of the `siginfo_t` passed to the signal handler in the + //! crashed process. + LinuxVMAddress siginfo_address; + + //! \brief The address of the `ucontext_t` passed to the signal handler in the + //! crashed process. + LinuxVMAddress context_address; + + //! \brief The thread ID of the thread which received the signal. + pid_t thread_id; +}; + +#pragma pack(pop) + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_LINUX_EXCEPTION_INFORMATION_H_ diff --git a/util/util.gyp b/util/util.gyp index 2b463c62..eb0794ea 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -65,6 +65,7 @@ 'linux/ptrace_connection.h', 'linux/ptracer.cc', 'linux/ptracer.h', + 'linux/exception_information.h', 'linux/scoped_ptrace_attach.cc', 'linux/scoped_ptrace_attach.h', 'linux/thread_info.cc', From b851b2590b232a0072aa459ece281b2412e134cf Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Mon, 6 Nov 2017 14:49:40 -0500 Subject: [PATCH 010/326] Update mini_chromium to dd0c3e9680ae3c4c22f2221a2a75e48dd4a562ec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 7f523b111c8c win: Don’t define c16*() functions in base/strings/string16.{cc,h} dd0c3e9680ae Use ICU 60.1 as the basis for base/third_party/icu Change-Id: I21b921c36a3ee1f989fa9786f60d980724577f64 Reviewed-on: https://chromium-review.googlesource.com/755215 Reviewed-by: Robert Sesek Commit-Queue: Mark Mentovai --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 94dda8ea..26aa6bf4 100644 --- a/DEPS +++ b/DEPS @@ -38,7 +38,7 @@ deps = { 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '7d6697ceb5cb5ca02fde3813496f48b9b1d76d0c', + 'dd0c3e9680ae3c4c22f2221a2a75e48dd4a562ec', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', From 18726100ed9a2d766ffbad277712f43b0ca7c691 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Mon, 6 Nov 2017 14:01:04 -0800 Subject: [PATCH 011/326] Move win/time to misc/time and add more conversion functions This CL pulls together similar time conversion functions and adds conversions between `FILETIME`s and `timespec`s. Bug: crashpad:206 Change-Id: I1d9b1560884ffde2364af0092114f82e1534ad1c Reviewed-on: https://chromium-review.googlesource.com/752574 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- snapshot/win/process_reader_win.cc | 2 +- snapshot/win/process_snapshot_win.cc | 2 +- util/linux/proc_stat_reader.cc | 17 +--- util/misc/time.cc | 49 +++++++++ util/misc/time.h | 71 +++++++++++++ util/misc/time_test.cc | 126 ++++++++++++++++++++++++ util/{win/time.cc => misc/time_win.cc} | 42 ++++++-- util/synchronization/semaphore_posix.cc | 14 +-- util/util.gyp | 5 +- util/util_test.gyp | 2 +- util/win/time.h | 36 ------- util/win/time_test.cc | 34 ------- 12 files changed, 289 insertions(+), 111 deletions(-) create mode 100644 util/misc/time.cc create mode 100644 util/misc/time.h create mode 100644 util/misc/time_test.cc rename util/{win/time.cc => misc/time_win.cc} (54%) delete mode 100644 util/win/time.h delete mode 100644 util/win/time_test.cc diff --git a/snapshot/win/process_reader_win.cc b/snapshot/win/process_reader_win.cc index f8a2f928..6041ec2d 100644 --- a/snapshot/win/process_reader_win.cc +++ b/snapshot/win/process_reader_win.cc @@ -21,12 +21,12 @@ #include "base/numerics/safe_conversions.h" #include "base/strings/stringprintf.h" +#include "util/misc/time.h" #include "util/win/capture_context.h" #include "util/win/nt_internals.h" #include "util/win/ntstatus_logging.h" #include "util/win/process_structs.h" #include "util/win/scoped_handle.h" -#include "util/win/time.h" namespace crashpad { diff --git a/snapshot/win/process_snapshot_win.cc b/snapshot/win/process_snapshot_win.cc index 532527b4..180b2cb7 100644 --- a/snapshot/win/process_snapshot_win.cc +++ b/snapshot/win/process_snapshot_win.cc @@ -24,9 +24,9 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "util/misc/from_pointer_cast.h" +#include "util/misc/time.h" #include "util/win/nt_internals.h" #include "util/win/registration_protocol_win.h" -#include "util/win/time.h" namespace crashpad { diff --git a/util/linux/proc_stat_reader.cc b/util/linux/proc_stat_reader.cc index b4cc3ffd..b1dfe94f 100644 --- a/util/linux/proc_stat_reader.cc +++ b/util/linux/proc_stat_reader.cc @@ -22,27 +22,12 @@ #include "base/logging.h" #include "util/file/file_io.h" #include "util/misc/lexing.h" +#include "util/misc/time.h" namespace crashpad { namespace { -void SubtractTimespec(const timespec& t1, - const timespec& t2, - timespec* result) { - result->tv_sec = t1.tv_sec - t2.tv_sec; - result->tv_nsec = t1.tv_nsec - t2.tv_nsec; - if (result->tv_nsec < 0) { - result->tv_sec -= 1; - result->tv_nsec += static_cast(1E9); - } -} - -void TimespecToTimeval(const timespec& ts, timeval* tv) { - tv->tv_sec = ts.tv_sec; - tv->tv_usec = ts.tv_nsec / 1000; -} - long GetClockTicksPerSecond() { long clock_ticks_per_s = sysconf(_SC_CLK_TCK); if (clock_ticks_per_s <= 0) { diff --git a/util/misc/time.cc b/util/misc/time.cc new file mode 100644 index 00000000..e3a20aa4 --- /dev/null +++ b/util/misc/time.cc @@ -0,0 +1,49 @@ +// 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 "util/misc/time.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +void AddTimespec(const timespec& ts1, const timespec& ts2, timespec* result) { + result->tv_sec = ts1.tv_sec + ts2.tv_sec; + result->tv_nsec = ts1.tv_nsec + ts2.tv_nsec; + if (result->tv_nsec >= long{kNanosecondsPerSecond}) { + ++result->tv_sec; + result->tv_nsec -= kNanosecondsPerSecond; + } +} + +void SubtractTimespec(const timespec& t1, + const timespec& t2, + timespec* result) { + result->tv_sec = t1.tv_sec - t2.tv_sec; + result->tv_nsec = t1.tv_nsec - t2.tv_nsec; + if (result->tv_nsec < 0) { + result->tv_sec -= 1; + result->tv_nsec += kNanosecondsPerSecond; + } +} + +bool TimespecToTimeval(const timespec& ts, timeval* tv) { + tv->tv_usec = ts.tv_nsec / 1000; + + // timespec::tv_sec and timeval::tv_sec should generally both be of type + // time_t, however, on Windows, timeval::tv_sec is declared as a long, which + // may be smaller than a time_t. + return AssignIfInRange(&tv->tv_sec, ts.tv_sec); +} + +} // namespace crashpad diff --git a/util/misc/time.h b/util/misc/time.h new file mode 100644 index 00000000..f06c2aab --- /dev/null +++ b/util/misc/time.h @@ -0,0 +1,71 @@ +// 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_UTIL_MISC_TIME_H_ +#define CRASHPAD_UTIL_MISC_TIME_H_ + +#include +#include +#include + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#endif + +namespace crashpad { + +constexpr uint64_t kNanosecondsPerSecond = static_cast(1E9); + +//! \brief Add `timespec` \a ts1 and \a ts2 and return the result in \a result. +void AddTimespec(const timespec& ts1, const timespec& ts2, timespec* result); + +//! \brief Subtract `timespec` \a ts2 from \a ts1 and return the result in \a +//! result. +void SubtractTimespec(const timespec& ts1, + const timespec& ts2, + timespec* result); + +//! \brief Convert the timespec \a ts to a timeval \a tv. +//! \return `true` if the assignment is possible without truncation. +bool TimespecToTimeval(const timespec& ts, timeval* tv); + +#if defined(OS_WIN) || DOXYGEN + +//! \brief Convert a `timespec` to a Windows `FILETIME`, converting from POSIX +//! epoch to Windows epoch. +FILETIME TimespecToFiletimeEpoch(const timespec& ts); + +//! \brief Convert a Windows `FILETIME` to `timespec`, converting from Windows +//! epoch to POSIX epoch. +timespec FiletimeToTimespecEpoch(const FILETIME& filetime); + +//! \brief Convert Windows `FILETIME` to `timeval`, converting from Windows +//! epoch to POSIX epoch. +timeval FiletimeToTimevalEpoch(const FILETIME& filetime); + +//! \brief Convert Windows `FILETIME` to `timeval`, treating the values as +//! an interval of elapsed time. +timeval FiletimeToTimevalInterval(const FILETIME& filetime); + +//! \brief Similar to POSIX `gettimeofday()`, gets the current system time in +//! UTC. +void GetTimeOfDay(timeval* tv); + +#endif // OS_WIN + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_TIME_H_ diff --git a/util/misc/time_test.cc b/util/misc/time_test.cc new file mode 100644 index 00000000..2c276d51 --- /dev/null +++ b/util/misc/time_test.cc @@ -0,0 +1,126 @@ +// 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 "util/misc/time.h" + +#include + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(Time, TimespecArithmetic) { + timespec ts1, ts2, result; + ts1.tv_sec = ts2.tv_sec = 1; + ts1.tv_nsec = ts2.tv_nsec = kNanosecondsPerSecond / 2; + AddTimespec(ts1, ts2, &result); + EXPECT_EQ(result.tv_sec, 3); + EXPECT_EQ(result.tv_nsec, 0); + + ts1.tv_sec = 2; + ts1.tv_nsec = 0; + ts2.tv_sec = 1; + ts2.tv_nsec = 1; + SubtractTimespec(ts1, ts2, &result); + EXPECT_EQ(result.tv_sec, 0); + EXPECT_EQ(result.tv_nsec, long{kNanosecondsPerSecond - 1}); +} + +TEST(Time, TimeConversions) { + // On July 30th, 2014 at 9:15 PM GMT+0, the Crashpad git repository was born. + // (nanoseconds are approximate) + constexpr timespec kCrashpadBirthdate = { + /* .tv_sec= */ 1406754914, + /* .tv_nsec= */ 32487 + }; + + timeval timeval_birthdate; + ASSERT_TRUE(TimespecToTimeval(kCrashpadBirthdate, &timeval_birthdate)); + EXPECT_EQ(timeval_birthdate.tv_sec, kCrashpadBirthdate.tv_sec); + EXPECT_EQ(timeval_birthdate.tv_usec, kCrashpadBirthdate.tv_nsec / 1000); + + constexpr timespec kEndOfTime = { + /* .tv_sec= */ std::numeric_limits::max(), + /* .tv_nsec= */ 0 + }; + + timeval end_of_timeval; + if (std::numeric_limits::max() > + std::numeric_limits::max()) { + EXPECT_FALSE(TimespecToTimeval(kEndOfTime, &end_of_timeval)); + } else { + EXPECT_TRUE(TimespecToTimeval(kEndOfTime, &end_of_timeval)); + } + +#if defined(OS_WIN) + constexpr uint64_t kBirthdateFiletimeIntervals = 130512285140000324; + FILETIME filetime_birthdate; + filetime_birthdate.dwLowDateTime = 0xffffffff & kBirthdateFiletimeIntervals; + filetime_birthdate.dwHighDateTime = kBirthdateFiletimeIntervals >> 32; + + FILETIME filetime = TimespecToFiletimeEpoch(kCrashpadBirthdate); + EXPECT_EQ(filetime.dwLowDateTime, filetime_birthdate.dwLowDateTime); + EXPECT_EQ(filetime.dwHighDateTime, filetime_birthdate.dwHighDateTime); + + timespec timespec_birthdate = FiletimeToTimespecEpoch(filetime_birthdate); + EXPECT_EQ(timespec_birthdate.tv_sec, kCrashpadBirthdate.tv_sec); + EXPECT_EQ(timespec_birthdate.tv_nsec, + kCrashpadBirthdate.tv_nsec - kCrashpadBirthdate.tv_nsec % 100); + + timeval_birthdate = FiletimeToTimevalEpoch(filetime_birthdate); + EXPECT_EQ(timeval_birthdate.tv_sec, kCrashpadBirthdate.tv_sec); + EXPECT_EQ(timeval_birthdate.tv_usec, kCrashpadBirthdate.tv_nsec / 1000); + + FILETIME elapsed_filetime; + elapsed_filetime.dwLowDateTime = 0; + elapsed_filetime.dwHighDateTime = 0; + timeval elapsed_timeval = FiletimeToTimevalInterval(elapsed_filetime); + EXPECT_EQ(elapsed_timeval.tv_sec, 0); + EXPECT_EQ(elapsed_timeval.tv_usec, 0); + + elapsed_filetime.dwLowDateTime = 9; + elapsed_timeval = FiletimeToTimevalInterval(elapsed_filetime); + EXPECT_EQ(elapsed_timeval.tv_sec, 0); + EXPECT_EQ(elapsed_timeval.tv_usec, 0); + + elapsed_filetime.dwLowDateTime = 10; + elapsed_timeval = FiletimeToTimevalInterval(elapsed_filetime); + EXPECT_EQ(elapsed_timeval.tv_sec, 0); + EXPECT_EQ(elapsed_timeval.tv_usec, 1); + + elapsed_filetime.dwHighDateTime = 1; + elapsed_filetime.dwLowDateTime = 0; + elapsed_timeval = FiletimeToTimevalInterval(elapsed_filetime); + EXPECT_EQ(elapsed_timeval.tv_sec, 429); + EXPECT_EQ(elapsed_timeval.tv_usec, 496729); +#endif // OS_WIN +} + +#if defined(OS_WIN) + +TEST(Time, GetTimeOfDay) { + timeval t; + GetTimeOfDay(&t); + time_t approx_now = time(nullptr); + EXPECT_GE(approx_now, t.tv_sec); + EXPECT_LT(approx_now - 100, t.tv_sec); +} + +#endif // OS_WIN + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/util/win/time.cc b/util/misc/time_win.cc similarity index 54% rename from util/win/time.cc rename to util/misc/time_win.cc index f3c2360a..d9c4156b 100644 --- a/util/win/time.cc +++ b/util/misc/time_win.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "util/win/time.h" +#include "util/misc/time.h" #include @@ -23,11 +23,22 @@ namespace crashpad { namespace { constexpr uint64_t kMicrosecondsPerSecond = static_cast(1E6); +constexpr uint64_t kNanosecondsPerFiletimeInterval = static_cast(100); +constexpr uint64_t kFiletimeIntervalsPerSecond = + kNanosecondsPerSecond / kNanosecondsPerFiletimeInterval; +constexpr uint64_t kFiletimeIntervalsPerMicrosecond = + kFiletimeIntervalsPerSecond / kMicrosecondsPerSecond; + +// Windows epoch is 1601-01-01, and FILETIME ticks are 100 nanoseconds. +// 1601 to 1970 is 369 years + 89 leap days = 134774 days * 86400 seconds per +// day. It's not entirely clear, but it appears that these are solar seconds, +// not SI seconds, so there are no leap seconds to be considered. +constexpr uint64_t kNumSecondsFrom1601To1970 = (369 * 365 + 89) * 86400ULL; uint64_t FiletimeToMicroseconds(const FILETIME& filetime) { uint64_t t = (static_cast(filetime.dwHighDateTime) << 32) | filetime.dwLowDateTime; - return t / 10; // 100 nanosecond intervals to microseconds. + return t / kFiletimeIntervalsPerMicrosecond; } timeval MicrosecondsToTimeval(uint64_t microseconds) { @@ -39,14 +50,31 @@ timeval MicrosecondsToTimeval(uint64_t microseconds) { } // namespace +FILETIME TimespecToFiletimeEpoch(const timespec& ts) { + uint64_t intervals = + (kNumSecondsFrom1601To1970 + ts.tv_sec) * kFiletimeIntervalsPerSecond + + ts.tv_nsec / kNanosecondsPerFiletimeInterval; + FILETIME filetime; + filetime.dwLowDateTime = intervals & 0xffffffff; + filetime.dwHighDateTime = intervals >> 32; + return filetime; +} + +timespec FiletimeToTimespecEpoch(const FILETIME& filetime) { + uint64_t intervals = + (uint64_t{filetime.dwHighDateTime} << 32) | filetime.dwLowDateTime; + timespec result; + result.tv_sec = + (intervals / kFiletimeIntervalsPerSecond) - kNumSecondsFrom1601To1970; + result.tv_nsec = + static_cast(intervals % kFiletimeIntervalsPerSecond) * + kNanosecondsPerFiletimeInterval; + return result; +} + timeval FiletimeToTimevalEpoch(const FILETIME& filetime) { uint64_t microseconds = FiletimeToMicroseconds(filetime); - // Windows epoch is 1601-01-01, and FILETIME ticks are 100 nanoseconds. - // 1601 to 1970 is 369 years + 89 leap days = 134774 days * 86400 seconds per - // day. It's not entirely clear, but it appears that these are solar seconds, - // not SI seconds, so there are no leap seconds to be considered. - constexpr uint64_t kNumSecondsFrom1601To1970 = (369 * 365 + 89) * 86400ULL; DCHECK_GE(microseconds, kNumSecondsFrom1601To1970 * kMicrosecondsPerSecond); microseconds -= kNumSecondsFrom1601To1970 * kMicrosecondsPerSecond; return MicrosecondsToTimeval(microseconds); diff --git a/util/synchronization/semaphore_posix.cc b/util/synchronization/semaphore_posix.cc index f6e8bea1..59ed71f2 100644 --- a/util/synchronization/semaphore_posix.cc +++ b/util/synchronization/semaphore_posix.cc @@ -20,24 +20,12 @@ #include "base/logging.h" #include "base/posix/eintr_wrapper.h" +#include "util/misc/time.h" namespace crashpad { #if !defined(OS_MACOSX) -namespace { - -void AddTimespec(const timespec& ts1, const timespec& ts2, timespec* result) { - result->tv_sec = ts1.tv_sec + ts2.tv_sec; - result->tv_nsec = ts1.tv_nsec + ts2.tv_nsec; - if (result->tv_nsec > static_cast(1E9)) { - ++result->tv_sec; - result->tv_nsec -= static_cast(1E9); - } -} - -} // namespace - Semaphore::Semaphore(int value) { PCHECK(sem_init(&semaphore_, 0, value) == 0) << "sem_init"; } diff --git a/util/util.gyp b/util/util.gyp index eb0794ea..ce966b78 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -144,6 +144,9 @@ 'misc/scoped_forbid_return.cc', 'misc/scoped_forbid_return.h', 'misc/symbolic_constants_common.h', + 'misc/time.cc', + 'misc/time.h', + 'misc/time_win.cc', 'misc/tri_state.h', 'misc/uuid.cc', 'misc/uuid.h', @@ -257,8 +260,6 @@ 'win/session_end_watcher.cc', 'win/session_end_watcher.h', 'win/termination_codes.h', - 'win/time.cc', - 'win/time.h', 'win/xp_compat.h', ], 'conditions': [ diff --git a/util/util_test.gyp b/util/util_test.gyp index 152c981a..30d86001 100644 --- a/util/util_test.gyp +++ b/util/util_test.gyp @@ -74,6 +74,7 @@ 'misc/scoped_forbid_return_test.cc', 'misc/random_string_test.cc', 'misc/reinterpret_bytes_test.cc', + 'misc/time_test.cc', 'misc/uuid_test.cc', 'net/http_body_gzip_test.cc', 'net/http_body_test.cc', @@ -115,7 +116,6 @@ 'win/safe_terminate_process_test.cc', 'win/scoped_process_suspend_test.cc', 'win/session_end_watcher_test.cc', - 'win/time_test.cc', ], 'conditions': [ ['OS=="mac"', { diff --git a/util/win/time.h b/util/win/time.h deleted file mode 100644 index 7cc0094f..00000000 --- a/util/win/time.h +++ /dev/null @@ -1,36 +0,0 @@ -// 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_UTIL_WIN_TIME_H_ -#define CRASHPAD_UTIL_WIN_TIME_H_ - -#include -#include - -namespace crashpad { - -//! \brief Convert Windows `FILETIME` to `timeval`, converting from Windows -//! epoch to POSIX epoch. -timeval FiletimeToTimevalEpoch(const FILETIME& filetime); - -//! \brief Convert Windows `FILETIME` to `timeval`, treating the values as -//! an interval of elapsed time. -timeval FiletimeToTimevalInterval(const FILETIME& filetime); - -//! \brief Similar to POSIX gettimeofday(), gets the current system time in UTC. -void GetTimeOfDay(timeval* tv); - -} // namespace crashpad - -#endif // CRASHPAD_UTIL_WIN_TIME_H_ diff --git a/util/win/time_test.cc b/util/win/time_test.cc deleted file mode 100644 index ad0771e3..00000000 --- a/util/win/time_test.cc +++ /dev/null @@ -1,34 +0,0 @@ -// 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 "util/win/time.h" - -#include "gtest/gtest.h" - -namespace crashpad { -namespace test { -namespace { - -TEST(Time, Reasonable) { - timeval t; - GetTimeOfDay(&t); - // Assume that time's time_t return is seconds from 1970. - time_t approx_now = time(nullptr); - EXPECT_GE(approx_now, t.tv_sec); - EXPECT_LT(approx_now - 100, t.tv_sec); -} - -} // namespace -} // namespace test -} // namespace crashpad From 34f5e8d513a8ad762d3f142922aadb993f567b67 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Mon, 6 Nov 2017 18:09:27 -0500 Subject: [PATCH 012/326] Only disable -Wconstant-conversion for Clang, not GCC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The warning suppression was recently added in a51e912004a6, and I don’t know what I was thinking. I went out of my way to make it apply to both Clang and GCC, but GCC doesn’t recognize this warning at all, nor does it need any other warning suppressed. Change-Id: I50341bfe81ee4799b3f6278d2e31ec31741952ac Reviewed-on: https://chromium-review.googlesource.com/755654 Reviewed-by: Joshua Peraza Commit-Queue: Mark Mentovai --- minidump/minidump_file_writer_test.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/minidump/minidump_file_writer_test.cc b/minidump/minidump_file_writer_test.cc index e41ed75f..00f7a7bf 100644 --- a/minidump/minidump_file_writer_test.cc +++ b/minidump/minidump_file_writer_test.cc @@ -21,7 +21,6 @@ #include #include "base/compiler_specific.h" -#include "build/build_config.h" #include "gtest/gtest.h" #include "minidump/minidump_stream_writer.h" #include "minidump/minidump_user_extension_stream_data_source.h" @@ -396,16 +395,16 @@ TEST(MinidumpFileWriter, InitializeFromSnapshot_Exception) { // In a 32-bit environment, this will give a “timestamp out of range” warning, // but the test should complete without failure. constexpr uint32_t kSnapshotTime = 0xfd469ab8; -#if defined(COMPILER_GCC) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconstant-conversion" +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconstant-conversion" #define DISABLED_WCONSTANT_CONVERSION -#endif // COMPILER_GCC || __clang__ +#endif // __clang__ MSVC_SUPPRESS_WARNING(4309); // Truncation of constant value. MSVC_SUPPRESS_WARNING(4838); // Narrowing conversion. constexpr timeval kSnapshotTimeval = {static_cast(kSnapshotTime), 0}; #if defined(DISABLED_WCONSTANT_CONVERSION) -#pragma GCC diagnostic pop +#pragma clang diagnostic pop #undef DISABLED_WCONSTANT_CONVERSION #endif // DISABLED_WCONSTANT_CONVERSION From d3b7463c7a48e83180a2619d00614a175641fb4e Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Tue, 7 Nov 2017 11:46:54 -0500 Subject: [PATCH 013/326] win: Recognize nsi.dll presenting as VFT_DRV/VFT2_DRV_NETWORK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was previously proposed at https://chromium-review.googlesource.com/c/crashpad/crashpad/+/339103/2/util/win/pe_image_reader_test.cc#84. It didn’t land because the change was abandoned for other reasons, but the fix was valid. nsi.dll is not VFT_APP or VFT_DLL, and if it’s loaded, crashpad_snapshot_test PEImageReader.VSFixedFileInfo_AllModules fails. Although I can’t reproduce nsi.dll being loaded spontaneously in local testing or on trybots, it occurred in the monolithic crashpad_tests at https://build.chromium.org/p/chromium.win/builders/Win7%20Tests%20%28dbg%29%281%29/builds/64492: [ RUN ] PEImageReader.VSFixedFileInfo_AllModules ../../third_party/crashpad/crashpad/snapshot/win/pe_image_reader_test.cc(90): error: Value of: observed.dwFileType == VFT_APP || observed.dwFileType == VFT_DLL Actual: false Expected: true Google Test trace: ../../third_party/crashpad/crashpad/snapshot/win/pe_image_reader_test.cc(164): C:\Windows\syswow64\NSI.dll [ FAILED ] PEImageReader.VSFixedFileInfo_AllModules (11 ms) I can also reproduce locally by calling LoadLibrary(L"nsi.dll"). Bug: chromium:779790, chromium:782011 Test: crashpad_snapshot_test PEImageReader.VSFixedFileInfo_AllModules Change-Id: I361c7d6521645913277a441ce38779aaa4a182c2 Reviewed-on: https://chromium-review.googlesource.com/757077 Reviewed-by: Scott Graham Commit-Queue: Mark Mentovai --- snapshot/win/pe_image_reader_test.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/snapshot/win/pe_image_reader_test.cc b/snapshot/win/pe_image_reader_test.cc index 3192a446..23e74dc2 100644 --- a/snapshot/win/pe_image_reader_test.cc +++ b/snapshot/win/pe_image_reader_test.cc @@ -18,6 +18,7 @@ #include #include "base/files/file_path.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "gtest/gtest.h" #include "snapshot/win/process_reader_win.h" @@ -86,8 +87,17 @@ void TestVSFixedFileInfo(ProcessReaderWin* process_reader, EXPECT_EQ(observed.dwFileType, static_cast(VFT_DLL)); } else { EXPECT_NE(observed.dwFileOS & VOS_NT_WINDOWS32, 0u); + + // VFT_DRV/VFT2_DRV_NETWORK is for nsi.dll, “network service interface.” + // It’s not normally loaded, but has been observed to be loaded in some + // cases. EXPECT_TRUE(observed.dwFileType == VFT_APP || - observed.dwFileType == VFT_DLL); + observed.dwFileType == VFT_DLL || + (observed.dwFileType == VFT_DRV && + observed.dwFileSubtype == VFT2_DRV_NETWORK)) + << base::StringPrintf("type 0x%x, subtype 0x%x", + observed.dwFileType, + observed.dwFileSubtype); } } From 2d077a2c571bbe5861ce4cea71650cfea2994cce Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Tue, 7 Nov 2017 14:39:38 -0500 Subject: [PATCH 014/326] =?UTF-8?q?win:=20Use=20=E2=80=9Clong=E2=80=9D=20f?= =?UTF-8?q?ormat=20modifier=20for=20DWORD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ibecedd195224ea53ff36f376897a6ff3c4e773d2 Reviewed-on: https://chromium-review.googlesource.com/757085 Reviewed-by: Scott Graham Commit-Queue: Mark Mentovai --- snapshot/win/pe_image_reader_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapshot/win/pe_image_reader_test.cc b/snapshot/win/pe_image_reader_test.cc index 23e74dc2..d9680b82 100644 --- a/snapshot/win/pe_image_reader_test.cc +++ b/snapshot/win/pe_image_reader_test.cc @@ -95,7 +95,7 @@ void TestVSFixedFileInfo(ProcessReaderWin* process_reader, observed.dwFileType == VFT_DLL || (observed.dwFileType == VFT_DRV && observed.dwFileSubtype == VFT2_DRV_NETWORK)) - << base::StringPrintf("type 0x%x, subtype 0x%x", + << base::StringPrintf("type 0x%lx, subtype 0x%lx", observed.dwFileType, observed.dwFileSubtype); } From 6f6f8a144d89857f67a7826187766f6a1b850ff7 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 8 Nov 2017 21:39:08 -0800 Subject: [PATCH 015/326] Add FileModificationTime FileModificationTime gets the last write time for files, directories, or symbolic links. Symbolic links may point to files, directories, or be dangling. Bug: crashpad:206 Change-Id: Ic83b5a7d318502ad5db5c01731d06c8624925e15 Reviewed-on: https://chromium-review.googlesource.com/744298 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- test/filesystem.cc | 60 ++++++++++++++++++++ test/filesystem.h | 18 +++++- util/file/filesystem.h | 11 +++- util/file/filesystem_posix.cc | 16 ++++++ util/file/filesystem_test.cc | 104 ++++++++++++++++++++++++++++++++++ util/file/filesystem_win.cc | 31 ++++++++++ 6 files changed, 238 insertions(+), 2 deletions(-) diff --git a/test/filesystem.cc b/test/filesystem.cc index a9a73f6a..c1a912d3 100644 --- a/test/filesystem.cc +++ b/test/filesystem.cc @@ -15,7 +15,9 @@ #include "test/filesystem.h" #include +#include #include +#include #include #include "base/logging.h" @@ -25,6 +27,7 @@ #include "test/scoped_temp_dir.h" #include "util/file/file_io.h" #include "util/file/filesystem.h" +#include "util/misc/time.h" #if defined(OS_POSIX) #include @@ -116,6 +119,63 @@ bool PathExists(const base::FilePath& path) { #endif } +bool SetFileModificationTime(const base::FilePath& path, + const timespec& mtime) { +#if defined(OS_MACOSX) + // utimensat() isn't available on macOS until 10.13, so lutimes() is used + // instead. + struct stat st; + if (lstat(path.value().c_str(), &st) != 0) { + PLOG(ERROR) << "lstat " << path.value(); + return false; + } + timeval times[2]; + EXPECT_TRUE(TimespecToTimeval(st.st_atimespec, ×[0])); + EXPECT_TRUE(TimespecToTimeval(mtime, ×[1])); + if (lutimes(path.value().c_str(), times) != 0) { + PLOG(ERROR) << "lutimes " << path.value(); + return false; + } + return true; +#elif defined(OS_POSIX) + timespec times[2]; + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_OMIT; + times[1] = mtime; + if (utimensat(AT_FDCWD, path.value().c_str(), times, AT_SYMLINK_NOFOLLOW) != + 0) { + PLOG(ERROR) << "utimensat " << path.value(); + return false; + } + return true; +#elif defined(OS_WIN) + DWORD flags = FILE_FLAG_OPEN_REPARSE_POINT; + if (IsDirectory(path, true)) { + // required for directory handles + flags |= FILE_FLAG_BACKUP_SEMANTICS; + } + + ScopedFileHandle handle(::CreateFile(path.value().c_str(), + GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + flags, + nullptr)); + if (!handle.is_valid()) { + PLOG(ERROR) << "CreateFile " << base::UTF16ToUTF8(path.value()); + return false; + } + + FILETIME filetime = TimespecToFiletimeEpoch(mtime); + if (!SetFileTime(handle.get(), nullptr, nullptr, &filetime)) { + PLOG(ERROR) << "SetFileTime " << base::UTF16ToUTF8(path.value()); + return false; + } + return true; +#endif // OS_MACOSX +} + #if !defined(OS_FUCHSIA) bool CanCreateSymbolicLinks() { diff --git a/test/filesystem.h b/test/filesystem.h index 7e658456..516335bb 100644 --- a/test/filesystem.h +++ b/test/filesystem.h @@ -17,15 +17,26 @@ #include "base/files/file_path.h" +#include + #include "build/build_config.h" namespace crashpad { namespace test { -bool CreateFile(const base::FilePath& file); +//! \brief Creates an empty file at path \a filepath. +bool CreateFile(const base::FilePath& filepath); +//! \brief Returns `true` if a filesystem node exists at path \a path. bool PathExists(const base::FilePath& path); +//! \brief Sets the modification time for a file, directory, or symbolic link. +//! +//! \param[in] path The path to the file to set the modification time for. +//! \param[in] mtime The new modification time for the file. +//! \return `true` on success. Otherwise `false` with a message logged. +bool SetFileModificationTime(const base::FilePath& path, const timespec& mtime); + #if !defined(OS_FUCHSIA) || DOXYGEN // There are no symbolic links on Fuchsia. Don’t bother declaring or defining // symbolic link-related functions at all, because it’s an error to even pretend @@ -48,6 +59,11 @@ bool PathExists(const base::FilePath& path); //! in Windows 10! bool CanCreateSymbolicLinks(); +//! \brief Creates a new symbolic link. +//! +//! \param[in] target_path The target for the link. +//! \param[in] symlink_path The name for the new link. +//! \return `true` on success. Otherwise `false` with a message logged. bool CreateSymbolicLink(const base::FilePath& target_path, const base::FilePath& symlink_path); diff --git a/util/file/filesystem.h b/util/file/filesystem.h index acdd8d81..27a4d1de 100644 --- a/util/file/filesystem.h +++ b/util/file/filesystem.h @@ -15,12 +15,21 @@ #ifndef CRASHPAD_UTIL_FILE_FILESYSTEM_H_ #define CRASHPAD_UTIL_FILE_FILESYSTEM_H_ -#include "base/files/file_path.h" +#include +#include "base/files/file_path.h" #include "util/file/file_io.h" namespace crashpad { +//! \brief Determines the modification time for a file, directory, or symbolic +//! link, logging a message on failure. +//! +//! \param[in] path The file to get the modification time for. +//! \param[out] mtime The modification time as seconds since the POSIX Epoch. +//! \return `true` on success. `false` on failure with a message logged. +bool FileModificationTime(const base::FilePath& path, timespec* mtime); + //! \brief Creates a directory, logging a message on failure. //! //! \param[in] path The path to the directory to create. diff --git a/util/file/filesystem_posix.cc b/util/file/filesystem_posix.cc index a35db1cd..888dfb7f 100644 --- a/util/file/filesystem_posix.cc +++ b/util/file/filesystem_posix.cc @@ -21,9 +21,25 @@ #include #include "base/logging.h" +#include "build/build_config.h" namespace crashpad { +bool FileModificationTime(const base::FilePath& path, timespec* mtime) { + struct stat st; + if (lstat(path.value().c_str(), &st) != 0) { + PLOG(ERROR) << "lstat " << path.value(); + return false; + } + +#if defined(OS_MACOSX) + *mtime = st.st_mtimespec; +#else + *mtime = st.st_mtim; +#endif + return true; +} + bool LoggingCreateDirectory(const base::FilePath& path, FilePermissions permissions, bool may_reuse) { diff --git a/util/file/filesystem_test.cc b/util/file/filesystem_test.cc index 7e28892e..593b478d 100644 --- a/util/file/filesystem_test.cc +++ b/util/file/filesystem_test.cc @@ -17,6 +17,7 @@ #include "base/logging.h" #include "build/build_config.h" #include "gtest/gtest.h" +#include "test/errors.h" #include "test/filesystem.h" #include "test/gtest_disabled.h" #include "test/scoped_temp_dir.h" @@ -25,6 +26,109 @@ namespace crashpad { namespace test { namespace { +bool CurrentTime(timespec* now) { +#if defined(OS_POSIX) + int res = clock_gettime(CLOCK_REALTIME, now); + if (res != 0) { + EXPECT_EQ(res, 0) << ErrnoMessage("clock_gettime"); + return false; + } + return true; +#else + int res = timespec_get(now, TIME_UTC); + if (res != TIME_UTC) { + EXPECT_EQ(res, TIME_UTC); + return false; + } + return true; +#endif +} + +TEST(Filesystem, FileModificationTime) { + timespec expected_time_start, expected_time_end; + + ASSERT_TRUE(CurrentTime(&expected_time_start)); + ScopedTempDir temp_dir; + ASSERT_TRUE(CurrentTime(&expected_time_end)); + + timespec dir_mtime; + ASSERT_TRUE(FileModificationTime(temp_dir.path(), &dir_mtime)); + EXPECT_GE(dir_mtime.tv_sec, expected_time_start.tv_sec - 2); + EXPECT_LE(dir_mtime.tv_sec, expected_time_end.tv_sec + 2); + + base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file"))); + ASSERT_TRUE(CurrentTime(&expected_time_start)); + ASSERT_TRUE(CreateFile(file)); + ASSERT_TRUE(CurrentTime(&expected_time_end)); + + timespec file_mtime; + ASSERT_TRUE(FileModificationTime(file, &file_mtime)); + EXPECT_GE(file_mtime.tv_sec, expected_time_start.tv_sec - 2); + EXPECT_LE(file_mtime.tv_sec, expected_time_end.tv_sec + 2); + + timespec file_mtime_again; + ASSERT_TRUE(FileModificationTime(file, &file_mtime_again)); + EXPECT_EQ(file_mtime.tv_sec, file_mtime_again.tv_sec); + EXPECT_EQ(file_mtime.tv_nsec, file_mtime_again.tv_nsec); + + timespec mtime; + EXPECT_FALSE(FileModificationTime(base::FilePath(), &mtime)); + EXPECT_FALSE(FileModificationTime( + temp_dir.path().Append(FILE_PATH_LITERAL("notafile")), &mtime)); +} + +#if !defined(OS_FUCHSIA) + +TEST(Filesystem, FileModificationTime_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + ScopedTempDir temp_dir; + + const base::FilePath dir_link( + temp_dir.path().Append(FILE_PATH_LITERAL("dir_link"))); + + timespec expected_time_start, expected_time_end; + ASSERT_TRUE(CurrentTime(&expected_time_start)); + ASSERT_TRUE(CreateSymbolicLink(temp_dir.path(), dir_link)); + ASSERT_TRUE(CurrentTime(&expected_time_end)); + + timespec mtime, mtime2; + ASSERT_TRUE(FileModificationTime(temp_dir.path(), &mtime)); + mtime.tv_sec -= 100; + ASSERT_TRUE(SetFileModificationTime(temp_dir.path(), mtime)); + ASSERT_TRUE(FileModificationTime(temp_dir.path(), &mtime2)); + EXPECT_GE(mtime2.tv_sec, mtime.tv_sec - 2); + EXPECT_LE(mtime2.tv_sec, mtime.tv_sec + 2); + + ASSERT_TRUE(FileModificationTime(dir_link, &mtime)); + EXPECT_GE(mtime.tv_sec, expected_time_start.tv_sec - 2); + EXPECT_LE(mtime.tv_sec, expected_time_end.tv_sec + 2); + + base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file"))); + ASSERT_TRUE(CreateFile(file)); + + base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link"))); + + ASSERT_TRUE(CurrentTime(&expected_time_start)); + ASSERT_TRUE(CreateSymbolicLink(file, link)); + ASSERT_TRUE(CurrentTime(&expected_time_end)); + + ASSERT_TRUE(FileModificationTime(file, &mtime)); + mtime.tv_sec -= 100; + ASSERT_TRUE(SetFileModificationTime(file, mtime)); + ASSERT_TRUE(FileModificationTime(file, &mtime2)); + EXPECT_GE(mtime2.tv_sec, mtime.tv_sec - 2); + EXPECT_LE(mtime2.tv_sec, mtime.tv_sec + 2); + + ASSERT_TRUE(FileModificationTime(link, &mtime)); + EXPECT_GE(mtime.tv_sec, expected_time_start.tv_sec - 2); + EXPECT_LE(mtime.tv_sec, expected_time_end.tv_sec + 2); +} + +#endif // !OS_FUCHSIA + TEST(Filesystem, CreateDirectory) { ScopedTempDir temp_dir; diff --git a/util/file/filesystem_win.cc b/util/file/filesystem_win.cc index b874ed8c..a465eacc 100644 --- a/util/file/filesystem_win.cc +++ b/util/file/filesystem_win.cc @@ -14,10 +14,12 @@ #include "util/file/filesystem.h" +#include #include #include "base/logging.h" #include "base/strings/utf_string_conversions.h" +#include "util/misc/time.h" namespace crashpad { @@ -50,6 +52,35 @@ bool LoggingRemoveDirectoryImpl(const base::FilePath& path) { } // namespace +bool FileModificationTime(const base::FilePath& path, timespec* mtime) { + DWORD flags = FILE_FLAG_OPEN_REPARSE_POINT; + if (IsDirectory(path, true)) { + // required for directory handles + flags |= FILE_FLAG_BACKUP_SEMANTICS; + } + + ScopedFileHandle handle( + ::CreateFile(path.value().c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, + OPEN_EXISTING, + flags, + nullptr)); + if (!handle.is_valid()) { + PLOG(ERROR) << "CreateFile " << base::UTF16ToUTF8(path.value()); + return false; + } + + FILETIME file_mtime; + if (!GetFileTime(handle.get(), nullptr, nullptr, &file_mtime)) { + PLOG(ERROR) << "GetFileTime " << base::UTF16ToUTF8(path.value()); + return false; + } + *mtime = FiletimeToTimespecEpoch(file_mtime); + return true; +} + bool LoggingCreateDirectory(const base::FilePath& path, FilePermissions permissions, bool may_reuse) { From 13e17bf90f1ebacbd5098395c3fe62bd9e33f71f Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 9 Nov 2017 10:37:58 -0800 Subject: [PATCH 016/326] Fix FileModificationTime for Android Traditional NDK headers provide the nanoseconds field of modification time as st_mtime_nsec, rather than contained in a timespec st_mtim. Unified headers do provide the timespec st_mtim. Bug: crashpad:206 Change-Id: I701ac2d5e357a13855a2a674f1355f2ea125ee4e Reviewed-on: https://chromium-review.googlesource.com/760618 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- util/file/filesystem_posix.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/file/filesystem_posix.cc b/util/file/filesystem_posix.cc index 888dfb7f..b1f19f62 100644 --- a/util/file/filesystem_posix.cc +++ b/util/file/filesystem_posix.cc @@ -34,6 +34,10 @@ bool FileModificationTime(const base::FilePath& path, timespec* mtime) { #if defined(OS_MACOSX) *mtime = st.st_mtimespec; +#elif defined(OS_ANDROID) + // This is needed to compile with traditional NDK headers. + mtime->tv_sec = st.st_mtime; + mtime->tv_nsec = st.st_mtime_nsec; #else *mtime = st.st_mtim; #endif From e2b9ab3ed20227422936a0446068fd443bfc4b19 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Thu, 9 Nov 2017 14:33:15 -0500 Subject: [PATCH 017/326] =?UTF-8?q?win:=20Tests=20shouldn=E2=80=99t=20frea?= =?UTF-8?q?k=20out=20when=20CodeView=20PDB=20links=20are=20absent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit crashpad_snapshot_test PEImageReader.DebugDirectory was hanging when crashpad_snapshot_test_image_reader.exe did not have a CodeView PDB link. This occurred when linked by Lexan ld-link.exe without /DEBUG. Bug: chromium:782781 Change-Id: I8fbc4d8decf6ac5e19f7ffeb230fd15d7c40fd51 Reviewed-on: https://chromium-review.googlesource.com/761320 Reviewed-by: Leonard Mosescu --- snapshot/win/pe_image_reader_test.cc | 2 +- snapshot/win/process_snapshot_win_test.cc | 5 ++- util/util.gyp | 2 + util/win/scoped_set_event.cc | 40 +++++++++++++++++++ util/win/scoped_set_event.h | 48 +++++++++++++++++++++++ util/win/session_end_watcher.cc | 16 +------- 6 files changed, 96 insertions(+), 17 deletions(-) create mode 100644 util/win/scoped_set_event.cc create mode 100644 util/win/scoped_set_event.h diff --git a/snapshot/win/pe_image_reader_test.cc b/snapshot/win/pe_image_reader_test.cc index d9680b82..c4aa5f0c 100644 --- a/snapshot/win/pe_image_reader_test.cc +++ b/snapshot/win/pe_image_reader_test.cc @@ -53,7 +53,7 @@ TEST(PEImageReader, DebugDirectory) { UUID uuid; DWORD age; std::string pdbname; - EXPECT_TRUE(pe_image_reader.DebugDirectoryInformation(&uuid, &age, &pdbname)); + ASSERT_TRUE(pe_image_reader.DebugDirectoryInformation(&uuid, &age, &pdbname)); std::string self_name = base::UTF16ToUTF8( TestPaths::ExpectedExecutableBasename(L"crashpad_snapshot_test") .RemoveFinalExtension() diff --git a/snapshot/win/process_snapshot_win_test.cc b/snapshot/win/process_snapshot_win_test.cc index b2448fcf..8a3e6a49 100644 --- a/snapshot/win/process_snapshot_win_test.cc +++ b/snapshot/win/process_snapshot_win_test.cc @@ -26,6 +26,7 @@ #include "util/file/file_io.h" #include "util/win/scoped_handle.h" #include "util/win/scoped_process_suspend.h" +#include "util/win/scoped_set_event.h" namespace crashpad { namespace test { @@ -46,6 +47,8 @@ void TestImageReaderChild(const TestPaths::Architecture architecture) { ChildLauncher child(child_test_executable, done_uuid.ToString16()); ASSERT_NO_FATAL_FAILURE(child.Start()); + ScopedSetEvent set_done(done.get()); + char c; ASSERT_TRUE( LoggingReadFileExactly(child.stdout_read_handle(), &c, sizeof(c))); @@ -105,7 +108,7 @@ void TestImageReaderChild(const TestPaths::Architecture architecture) { } // Tell the child it can terminate. - EXPECT_TRUE(SetEvent(done.get())) << ErrorMessage("SetEvent"); + EXPECT_TRUE(set_done.Set()); EXPECT_EQ(child.WaitForExit(), 0u); } diff --git a/util/util.gyp b/util/util.gyp index ce966b78..63314834 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -257,6 +257,8 @@ 'win/scoped_local_alloc.h', 'win/scoped_process_suspend.cc', 'win/scoped_process_suspend.h', + 'win/scoped_set_event.cc', + 'win/scoped_set_event.h', 'win/session_end_watcher.cc', 'win/session_end_watcher.h', 'win/termination_codes.h', diff --git a/util/win/scoped_set_event.cc b/util/win/scoped_set_event.cc new file mode 100644 index 00000000..7fb16474 --- /dev/null +++ b/util/win/scoped_set_event.cc @@ -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. + +#include "util/win/scoped_set_event.h" + +#include "base/logging.h" + +namespace crashpad { + +ScopedSetEvent::ScopedSetEvent(HANDLE event) : event_(event) { + DCHECK(event_); +} + +ScopedSetEvent::~ScopedSetEvent() { + if (event_) { + Set(); + } +} + +bool ScopedSetEvent::Set() { + bool rv = !!SetEvent(event_); + if (!rv) { + PLOG(ERROR) << "SetEvent"; + } + event_ = nullptr; + return rv; +} + +} // namespace crashpad diff --git a/util/win/scoped_set_event.h b/util/win/scoped_set_event.h new file mode 100644 index 00000000..82a1b314 --- /dev/null +++ b/util/win/scoped_set_event.h @@ -0,0 +1,48 @@ +// 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_UTIL_WIN_SCOPED_SET_EVENT_H_ +#define CRASHPAD_UTIL_WIN_SCOPED_SET_EVENT_H_ + +#include + +#include "base/macros.h" + +namespace crashpad { + +//! \brief Calls `SetEvent()` on destruction at latest. +//! +//! Does not assume ownership of the event handle. Use ScopedKernelHANDLE for +//! ownership. +class ScopedSetEvent { + public: + explicit ScopedSetEvent(HANDLE event); + ~ScopedSetEvent(); + + //! \brief Calls `SetEvent()` immediately. + //! + //! `SetEvent()` will not be called on destruction. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool Set(); + + private: + HANDLE event_; // weak + + DISALLOW_COPY_AND_ASSIGN(ScopedSetEvent); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_SCOPED_SET_EVENT_H_ diff --git a/util/win/session_end_watcher.cc b/util/win/session_end_watcher.cc index e795f22a..1d470899 100644 --- a/util/win/session_end_watcher.cc +++ b/util/win/session_end_watcher.cc @@ -16,6 +16,7 @@ #include "base/logging.h" #include "base/scoped_generic.h" +#include "util/win/scoped_set_event.h" extern "C" { extern IMAGE_DOS_HEADER __ImageBase; @@ -25,21 +26,6 @@ namespace crashpad { namespace { -class ScopedSetEvent { - public: - explicit ScopedSetEvent(HANDLE event) : event_(event) {} - ~ScopedSetEvent() { - if (!SetEvent(event_)) { - PLOG(ERROR) << "SetEvent"; - } - } - - private: - HANDLE event_; - - DISALLOW_COPY_AND_ASSIGN(ScopedSetEvent); -}; - // ScopedWindowClass and ScopedWindow operate on ATOM* and HWND*, respectively, // instead of ATOM and HWND, so that the actual storage can exist as a local // variable or a member variable, and the scoper can be responsible for From 0e3c38a4ca521a7db430ba2fce96536b79c1ae3d Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Thu, 9 Nov 2017 15:27:25 -0500 Subject: [PATCH 018/326] win: Make ProcessSnapshotTest.CrashpadInfoChild use a loaded module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When this test examines a module that doesn’t have a CodeView PDB link, it will fail. Such a link may be missing when linking with Lexan ld-link.exe without /DEBUG. The test had been examining the executable as its module. Since it’s easier to provide a single small module linked with /DEBUG than it is to require that the test executable always be linked with /DEBUG, the test is revised to always load a module and operate on it. The module used is the existing crashpad_snapshot_test_image_reader_module.dll. It was chosen because it’s also used by PEImageReader.DebugDirectory, which also requires a CodeView PDB link. It’s the build system’s responsibility to ensure that crashpad_snapshot_test_image_reader_module.dll is linked appropriately. Crashpad’s own GYP-based build always links with /DEBUG. Chrome’s GN-based Crashpad build will require additional attention at symbol_level = 0. Bug: chromium:782781 Change-Id: I0dda8cd13278b82842263e76bcc46362bd3998df Reviewed-on: https://chromium-review.googlesource.com/761501 Reviewed-by: Leonard Mosescu --- snapshot/win/pe_image_reader_test.cc | 40 +++++++++++++++++----------- test/scoped_module_handle.h | 3 +++ 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/snapshot/win/pe_image_reader_test.cc b/snapshot/win/pe_image_reader_test.cc index c4aa5f0c..ac456e2d 100644 --- a/snapshot/win/pe_image_reader_test.cc +++ b/snapshot/win/pe_image_reader_test.cc @@ -23,42 +23,52 @@ #include "gtest/gtest.h" #include "snapshot/win/process_reader_win.h" #include "test/errors.h" +#include "test/scoped_module_handle.h" #include "test/test_paths.h" #include "util/misc/from_pointer_cast.h" #include "util/win/get_module_information.h" #include "util/win/module_version.h" #include "util/win/process_info.h" -extern "C" IMAGE_DOS_HEADER __ImageBase; - namespace crashpad { namespace test { namespace { TEST(PEImageReader, DebugDirectory) { + base::FilePath module_path = + TestPaths::BuildArtifact(L"snapshot", + L"image_reader_module", + TestPaths::FileType::kLoadableModule); + ScopedModuleHandle module_handle(LoadLibrary(module_path.value().c_str())); + ASSERT_TRUE(module_handle.valid()) << ErrorMessage("LoadLibrary"); + PEImageReader pe_image_reader; ProcessReaderWin process_reader; ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), ProcessSuspensionState::kRunning)); - HMODULE self = reinterpret_cast(&__ImageBase); + MODULEINFO module_info; - ASSERT_TRUE(CrashpadGetModuleInformation( - GetCurrentProcess(), self, &module_info, sizeof(module_info))) + ASSERT_TRUE(CrashpadGetModuleInformation(GetCurrentProcess(), + module_handle.get(), + &module_info, + sizeof(module_info))) << ErrorMessage("GetModuleInformation"); - EXPECT_EQ(module_info.lpBaseOfDll, self); - ASSERT_TRUE(pe_image_reader.Initialize(&process_reader, - FromPointerCast(self), - module_info.SizeOfImage, - "self")); + EXPECT_EQ(module_info.lpBaseOfDll, module_handle.get()); + + base::FilePath module_basename = module_path.BaseName(); + ASSERT_TRUE(pe_image_reader.Initialize( + &process_reader, + FromPointerCast(module_handle.get()), + module_info.SizeOfImage, + base::UTF16ToUTF8(module_basename.value()))); + UUID uuid; DWORD age; std::string pdbname; ASSERT_TRUE(pe_image_reader.DebugDirectoryInformation(&uuid, &age, &pdbname)); - std::string self_name = base::UTF16ToUTF8( - TestPaths::ExpectedExecutableBasename(L"crashpad_snapshot_test") - .RemoveFinalExtension() - .value()); - EXPECT_NE(pdbname.find(self_name), std::string::npos); + std::string module_name = + base::UTF16ToUTF8(module_basename.RemoveFinalExtension().value()); + EXPECT_NE(pdbname.find(module_name), std::string::npos); const std::string suffix(".pdb"); EXPECT_EQ( pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix), diff --git a/test/scoped_module_handle.h b/test/scoped_module_handle.h index 3863cad3..20b5e153 100644 --- a/test/scoped_module_handle.h +++ b/test/scoped_module_handle.h @@ -59,6 +59,9 @@ class ScopedModuleHandle { explicit ScopedModuleHandle(ModuleHandle handle); ~ScopedModuleHandle(); + //! \return The module handle being managed. + ModuleHandle get() const { return handle_; } + //! \return `true` if this object manages a valid loadable module handle. bool valid() const { return handle_ != nullptr; } From b8f61fdc8b1ee3930afe9373e9a83f24a1c369f3 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 9 Nov 2017 17:05:43 -0800 Subject: [PATCH 019/326] Add TimevalToTimespec and use it to get CurrentTime on macOS A follow-up to: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/744298/35/util/file/filesystem_test.cc#31 Bug: crashpad:206 Change-Id: I2f711839ae7746104f1fee9685f2ab9059d9c2b3 Reviewed-on: https://chromium-review.googlesource.com/762479 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- util/file/filesystem_test.cc | 14 +++++++++++++- util/misc/time.cc | 5 +++++ util/misc/time.h | 3 +++ util/misc/time_test.cc | 8 +++++++- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/util/file/filesystem_test.cc b/util/file/filesystem_test.cc index 593b478d..28d2d88a 100644 --- a/util/file/filesystem_test.cc +++ b/util/file/filesystem_test.cc @@ -14,6 +14,8 @@ #include "util/file/filesystem.h" +#include + #include "base/logging.h" #include "build/build_config.h" #include "gtest/gtest.h" @@ -21,13 +23,23 @@ #include "test/filesystem.h" #include "test/gtest_disabled.h" #include "test/scoped_temp_dir.h" +#include "util/misc/time.h" namespace crashpad { namespace test { namespace { bool CurrentTime(timespec* now) { -#if defined(OS_POSIX) +#if defined(OS_MACOSX) + timeval now_tv; + int res = gettimeofday(&now_tv, nullptr); + if (res != 0) { + EXPECT_EQ(res, 0) << ErrnoMessage("gettimeofday"); + return false; + } + TimevalToTimespec(now_tv, now); + return true; +#elif defined(OS_POSIX) int res = clock_gettime(CLOCK_REALTIME, now); if (res != 0) { EXPECT_EQ(res, 0) << ErrnoMessage("clock_gettime"); diff --git a/util/misc/time.cc b/util/misc/time.cc index e3a20aa4..549b26ad 100644 --- a/util/misc/time.cc +++ b/util/misc/time.cc @@ -46,4 +46,9 @@ bool TimespecToTimeval(const timespec& ts, timeval* tv) { return AssignIfInRange(&tv->tv_sec, ts.tv_sec); } +void TimevalToTimespec(const timeval& tv, timespec* ts) { + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000; +} + } // namespace crashpad diff --git a/util/misc/time.h b/util/misc/time.h index f06c2aab..aabe8e64 100644 --- a/util/misc/time.h +++ b/util/misc/time.h @@ -42,6 +42,9 @@ void SubtractTimespec(const timespec& ts1, //! \return `true` if the assignment is possible without truncation. bool TimespecToTimeval(const timespec& ts, timeval* tv); +//! \brief Convert the timeval \a tv to a timespec \a ts. +void TimevalToTimespec(const timeval& tv, timespec* ts); + #if defined(OS_WIN) || DOXYGEN //! \brief Convert a `timespec` to a Windows `FILETIME`, converting from POSIX diff --git a/util/misc/time_test.cc b/util/misc/time_test.cc index 2c276d51..87e28faa 100644 --- a/util/misc/time_test.cc +++ b/util/misc/time_test.cc @@ -52,6 +52,12 @@ TEST(Time, TimeConversions) { EXPECT_EQ(timeval_birthdate.tv_sec, kCrashpadBirthdate.tv_sec); EXPECT_EQ(timeval_birthdate.tv_usec, kCrashpadBirthdate.tv_nsec / 1000); + timespec timespec_birthdate; + TimevalToTimespec(timeval_birthdate, ×pec_birthdate); + EXPECT_EQ(timespec_birthdate.tv_sec, kCrashpadBirthdate.tv_sec); + EXPECT_EQ(timespec_birthdate.tv_nsec, + kCrashpadBirthdate.tv_nsec - (kCrashpadBirthdate.tv_nsec % 1000)); + constexpr timespec kEndOfTime = { /* .tv_sec= */ std::numeric_limits::max(), /* .tv_nsec= */ 0 @@ -75,7 +81,7 @@ TEST(Time, TimeConversions) { EXPECT_EQ(filetime.dwLowDateTime, filetime_birthdate.dwLowDateTime); EXPECT_EQ(filetime.dwHighDateTime, filetime_birthdate.dwHighDateTime); - timespec timespec_birthdate = FiletimeToTimespecEpoch(filetime_birthdate); + timespec_birthdate = FiletimeToTimespecEpoch(filetime_birthdate); EXPECT_EQ(timespec_birthdate.tv_sec, kCrashpadBirthdate.tv_sec); EXPECT_EQ(timespec_birthdate.tv_nsec, kCrashpadBirthdate.tv_nsec - kCrashpadBirthdate.tv_nsec % 100); From 22e8c33b21e83e48adb1bc914c8298083130fa0d Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Mon, 13 Nov 2017 17:03:32 -0500 Subject: [PATCH 020/326] linux: Provide PTRACE_GET_THREAD_AREA for 32-bit x86 with glibc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit glibc’s own should provide this but doesn’t. See https://sourceware.org/bugzilla/show_bug.cgi?id=22433. The copy in compat provided it when targeting x86-64 and using glibc. util/linux/ptracer.cc uses it when targeting both 32-bit x86 and x86-64, so the compat definition must be made to apply to 32-bit x86 too. This also provides a #define using the same name as the constant, which is what glibc’s does for other constants. Bug: crashpad:30 Change-Id: I5a0734a236d1c25398fb69e66f58dfe118658b68 Reviewed-on: https://chromium-review.googlesource.com/765257 Commit-Queue: Mark Mentovai Reviewed-by: Joshua Peraza --- compat/linux/sys/ptrace.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compat/linux/sys/ptrace.h b/compat/linux/sys/ptrace.h index 57c5c5ba..e68125b5 100644 --- a/compat/linux/sys/ptrace.h +++ b/compat/linux/sys/ptrace.h @@ -19,9 +19,12 @@ #include -#if defined(__GLIBC__) && defined(__x86_64__) +// https://sourceware.org/bugzilla/show_bug.cgi?id=22433 +#if !defined(PTRACE_GET_THREAD_AREA) && \ + defined(__GLIBC__) && (defined(__i386__) || defined(__x86_64__)) static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = static_cast<__ptrace_request>(25); -#endif // __GLIBC__ && __x86_64__ +#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA +#endif // !PTRACE_GET_THREAD_AREA && __GLIBC__ && (__i386__ || __x86_64__) #endif // CRASHPAD_COMPAT_LINUX_SYS_PTRACE_H_ From d7798a4e284456702ce154e00e8adc2846f3a4f3 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Wed, 15 Nov 2017 12:43:44 -0500 Subject: [PATCH 021/326] Tolerate safe size mismatches in the CrashpadInfo struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The handler will now be less strict about checking CrashpadInfo struct sizes. Assuming the signature and version fields match: - If the handler sees a struct smaller than it’s expecting, the module was likely built with an earlier version of the client library, and it’s safe to treat the unknown fields as though they were zero or other suitable default values. - If the handler sees a struct larger than it’s expecting, the module was likely built with a later version of the client library. In that case, actions desired by the client will not be performed, but this is not otherwise an error condition. The CrashpadInfo struct must always be at least large enough to contain at least the size field. The signature and version fields are always checked. The section size must be at least as large as the size carried within the struct. To account for possible section padding, strict equality is not required. Bug: chromium:784427 Test: crashpad_snapshot_test CrashpadInfoSizes_ClientOptions/*.* Change-Id: Ibb0690ca6ed5e7619d1278a68ba7e893d55f19fb Reviewed-on: https://chromium-review.googlesource.com/767709 Commit-Queue: Mark Mentovai Reviewed-by: Robert Sesek --- client/crashpad_info.cc | 13 +- client/crashpad_info.h | 12 +- snapshot/crashpad_info_client_options_test.cc | 96 +++++++++++++++ snapshot/crashpad_info_size_test_module.cc | 113 ++++++++++++++++++ .../mac/mach_o_image_annotations_reader.cc | 14 +-- snapshot/mac/mach_o_image_reader.cc | 27 ++++- snapshot/mac/process_types.cc | 69 +++++++++++ snapshot/mac/process_types.h | 19 ++- .../mac/process_types/crashpad_info.proctype | 18 +++ .../crashreporterclient.proctype | 4 +- snapshot/mac/process_types/custom.cc | 105 +++++++++++++--- snapshot/snapshot_test.gyp | 28 +++++ snapshot/win/module_snapshot_win.cc | 7 +- snapshot/win/pe_image_annotations_reader.cc | 14 +-- snapshot/win/pe_image_reader.cc | 55 ++++++--- 15 files changed, 516 insertions(+), 78 deletions(-) create mode 100644 snapshot/crashpad_info_size_test_module.cc diff --git a/client/crashpad_info.cc b/client/crashpad_info.cc index 9452382b..7ead7445 100644 --- a/client/crashpad_info.cc +++ b/client/crashpad_info.cc @@ -25,6 +25,11 @@ namespace { +// Don’t change this when simply adding fields. Readers will size-check the +// structure and ignore fields they’re aware of when not present, as well as +// fields they’re not aware of. Only change this when introducing an +// incompatible layout, with the understanding that existing readers will not +// understand new versions. constexpr uint32_t kCrashpadInfoVersion = 1; } // namespace @@ -106,13 +111,7 @@ CrashpadInfo::CrashpadInfo() extra_memory_ranges_(nullptr), simple_annotations_(nullptr), user_data_minidump_stream_head_(nullptr), - annotations_list_(nullptr) -#if !defined(NDEBUG) && defined(OS_WIN) - , - invalid_read_detection_(0xbadc0de) -#endif -{ -} + annotations_list_(nullptr) {} void CrashpadInfo::AddUserDataMinidumpStream(uint32_t stream_type, const void* data, diff --git a/client/crashpad_info.h b/client/crashpad_info.h index fc391bf5..5db9a6cd 100644 --- a/client/crashpad_info.h +++ b/client/crashpad_info.h @@ -233,10 +233,10 @@ struct CrashpadInfo { #pragma clang diagnostic ignored "-Wunused-private-field" #endif - // Fields present in version 1: + // Fields present in version 1, subject to a check of the size_ field: uint32_t signature_; // kSignature uint32_t size_; // The size of the entire CrashpadInfo structure. - uint32_t version_; // kVersion + uint32_t version_; // kCrashpadInfoVersion uint32_t indirectly_referenced_memory_cap_; uint32_t padding_0_; TriState crashpad_handler_behavior_; @@ -248,9 +248,11 @@ struct CrashpadInfo { internal::UserDataMinidumpStreamListEntry* user_data_minidump_stream_head_; AnnotationList* annotations_list_; // weak -#if !defined(NDEBUG) && defined(OS_WIN) - uint32_t invalid_read_detection_; -#endif + // It’s generally safe to add new fields without changing + // kCrashpadInfoVersion, because readers should check size_ and ignore fields + // that aren’t present, as well as unknown fields. + // + // Adding fields? Consider snapshot/crashpad_info_size_test_module.cc too. #if defined(__clang__) #pragma clang diagnostic pop diff --git a/snapshot/crashpad_info_client_options_test.cc b/snapshot/crashpad_info_client_options_test.cc index 45f3f17b..4d6f185f 100644 --- a/snapshot/crashpad_info_client_options_test.cc +++ b/snapshot/crashpad_info_client_options_test.cc @@ -14,6 +14,7 @@ #include "snapshot/crashpad_info_client_options.h" +#include "base/auto_reset.h" #include "base/files/file_path.h" #include "base/macros.h" #include "base/strings/utf_string_conversions.h" @@ -229,6 +230,101 @@ TEST(CrashpadInfoClientOptions, TwoModules) { } } +class CrashpadInfoSizes_ClientOptions + : public testing::TestWithParam {}; + +TEST_P(CrashpadInfoSizes_ClientOptions, DifferentlySizedStruct) { + base::FilePath::StringType artifact(FILE_PATH_LITERAL("module_")); + artifact += GetParam(); + + // Open the module, which has a CrashpadInfo-like structure that’s smaller or + // larger than the current version’s CrashpadInfo structure defined in the + // client library. + base::FilePath module_path = + TestPaths::BuildArtifact(FILE_PATH_LITERAL("snapshot"), + artifact, + TestPaths::FileType::kLoadableModule); +#if defined(OS_MACOSX) + ScopedModuleHandle module( + dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL)); + ASSERT_TRUE(module.valid()) + << "dlopen " << module_path.value() << ": " << dlerror(); +#elif defined(OS_WIN) + ScopedModuleHandle module(LoadLibrary(module_path.value().c_str())); + ASSERT_TRUE(module.valid()) + << "LoadLibrary " << base::UTF16ToUTF8(module_path.value()) << ": " + << ErrorMessage(); +#else +#error Port. +#endif // OS_MACOSX + + // Get the function pointer from the module. + CrashpadInfo* (*TestModule_GetCrashpadInfo)() = + module.LookUpSymbol("TestModule_GetCrashpadInfo"); + ASSERT_TRUE(TestModule_GetCrashpadInfo); + + auto options = SelfProcessSnapshotAndGetCrashpadOptions(); + + // Make sure that the initial state has all values unset. + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset); + + // Get the remote CrashpadInfo structure. + CrashpadInfo* remote_crashpad_info = TestModule_GetCrashpadInfo(); + ASSERT_TRUE(remote_crashpad_info); + + { + ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); + + // Make sure that a change in the remote structure can be read back out, + // even though it’s a different size. + remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); + remote_crashpad_info->set_system_crash_reporter_forwarding( + TriState::kDisabled); + + options = SelfProcessSnapshotAndGetCrashpadOptions(); + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kEnabled); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kDisabled); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset); + } + + { + ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); + + // Make sure that the portion of the remote structure lying beyond its + // declared size reads as zero. + + // 4 = offsetof(CrashpadInfo, size_), but it’s private. + uint32_t* size = reinterpret_cast( + reinterpret_cast(remote_crashpad_info) + 4); + + // 21 = offsetof(CrashpadInfo, system_crash_reporter_forwarding_, but it’s + // private. + base::AutoReset reset_size(size, 21); + + // system_crash_reporter_forwarding_ is now beyond the struct’s declared + // size. Storage has actually been allocated for it, so it’s safe to set + // here. + remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); + remote_crashpad_info->set_system_crash_reporter_forwarding( + TriState::kDisabled); + + // Since system_crash_reporter_forwarding_ is beyond the struct’s declared + // size, it should read as 0 (TriState::kUnset), even though it was set to + // a different value above. + options = SelfProcessSnapshotAndGetCrashpadOptions(); + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kEnabled); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset); + } +} + +INSTANTIATE_TEST_CASE_P(CrashpadInfoSizes_ClientOptions, + CrashpadInfoSizes_ClientOptions, + testing::Values(FILE_PATH_LITERAL("small"), + FILE_PATH_LITERAL("large"))); + } // namespace } // namespace test } // namespace crashpad diff --git a/snapshot/crashpad_info_size_test_module.cc b/snapshot/crashpad_info_size_test_module.cc new file mode 100644 index 00000000..dbb22746 --- /dev/null +++ b/snapshot/crashpad_info_size_test_module.cc @@ -0,0 +1,113 @@ +// 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 + +#include "build/build_config.h" + +#if defined(OS_MACOSX) +#include +#elif defined(OS_WIN) +#include +#endif // OS_MACOSX + +namespace crashpad { +namespace { + +#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL) == \ + defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE) +#error Define exactly one of these macros +#endif + +// This module contains a CrashpadInfo structure that’s either smaller or larger +// than the one defined in the client library, depending on which macro is +// defined when it’s compiled. This tests the snapshot layer’s ability to read +// smaller structures (as might be found in modules built with older versions of +// the client library than a handler’s snapshot library) and larger ones (the +// “vice-versa” situation). This needs to be done without taking a dependency on +// the client library, which would bring with it a correct copy of the +// CrashpadInfo structure. As a result, all types have been simplified to +// fixed-size integers and void* pointers. +struct TestCrashpadInfo { + uint32_t signature_; + uint32_t size_; + uint32_t version_; + uint32_t indirectly_referenced_memory_cap_; + uint32_t padding_0_; + uint8_t crashpad_handler_behavior_; + uint8_t system_crash_reporter_forwarding_; + uint8_t gather_indirectly_referenced_memory_; + uint8_t padding_1_; + void* extra_memory_ranges_; + void* simple_annotations_; +#if !defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL) + void* user_data_minidump_stream_head_; + void* annotations_list_; +#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL +#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE) + uint8_t trailer_[64 * 1024]; +#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE +}; + +// Put it in the correct section. +// +// The initializer also duplicates constants from the client library, sufficient +// to get this test version to be interpreted as a genuine CrashpadInfo +// structure. The size is set to the actual size of this structure (that’s kind +// of the point of this test). +#if defined(OS_POSIX) +__attribute__(( +#if defined(OS_MACOSX) + section(SEG_DATA ",crashpad_info"), +#elif defined(OS_LINUX) || defined(OS_ANDROID) + section("crashpad_info"), +#else // !defined(OS_MACOSX) && !defined(OS_LINUX) && !defined(OS_ANDROID) +#error Port +#endif // !defined(OS_MACOSX) && !defined(OS_LINUX) && !defined(OS_ANDROID) +#if defined(ADDRESS_SANITIZER) + aligned(64), +#endif // defined(ADDRESS_SANITIZER) + used, + visibility("hidden"))) +#elif defined(OS_WIN) +#pragma section("CPADinfo", read, write) +__declspec(allocate("CPADinfo")) +#else // !defined(OS_POSIX) && !defined(OS_WIN) +#error Port +#endif // !defined(OS_POSIX) && !defined(OS_WIN) +TestCrashpadInfo g_test_crashpad_info = {'CPad', sizeof(TestCrashpadInfo), 1}; + +} // namespace +} // namespace crashpad + +extern "C" { + +#if defined(OS_POSIX) +__attribute__((visibility("default"))) +#elif defined(OS_WIN) +__declspec(dllexport) +#else +#error Port +#endif // OS_POSIX +crashpad::TestCrashpadInfo* TestModule_GetCrashpadInfo() { + return &crashpad::g_test_crashpad_info; +} + +} // extern "C" + +#if defined(OS_WIN) +BOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID reserved) { + return TRUE; +} +#endif // OS_WIN diff --git a/snapshot/mac/mach_o_image_annotations_reader.cc b/snapshot/mac/mach_o_image_annotations_reader.cc index 1a74d812..bb8f7e2b 100644 --- a/snapshot/mac/mach_o_image_annotations_reader.cc +++ b/snapshot/mac/mach_o_image_annotations_reader.cc @@ -153,11 +153,8 @@ void MachOImageAnnotationsReader::ReadDyldErrorStringAnnotation( void MachOImageAnnotationsReader::ReadCrashpadSimpleAnnotations( std::map* simple_map_annotations) const { process_types::CrashpadInfo crashpad_info; - if (!image_reader_->GetCrashpadInfo(&crashpad_info)) { - return; - } - - if (!crashpad_info.simple_annotations) { + if (!image_reader_->GetCrashpadInfo(&crashpad_info) || + !crashpad_info.simple_annotations) { return; } @@ -189,11 +186,8 @@ void MachOImageAnnotationsReader::ReadCrashpadSimpleAnnotations( void MachOImageAnnotationsReader::ReadCrashpadAnnotationsList( std::vector* annotations) const { process_types::CrashpadInfo crashpad_info; - if (!image_reader_->GetCrashpadInfo(&crashpad_info)) { - return; - } - - if (!crashpad_info.annotations_list) { + if (!image_reader_->GetCrashpadInfo(&crashpad_info) || + !crashpad_info.annotations_list) { return; } diff --git a/snapshot/mac/mach_o_image_reader.cc b/snapshot/mac/mach_o_image_reader.cc index cd8f125e..8a657264 100644 --- a/snapshot/mac/mach_o_image_reader.cc +++ b/snapshot/mac/mach_o_image_reader.cc @@ -481,24 +481,43 @@ bool MachOImageReader::GetCrashpadInfo( } if (crashpad_info_section->size < - crashpad_info->ExpectedSize(process_reader_)) { + crashpad_info->MinimumSize(process_reader_)) { LOG(WARNING) << "small crashpad info section size " << crashpad_info_section->size << module_info_; return false; } + // This Read() will zero out anything beyond the structure’s declared size. if (!crashpad_info->Read(process_reader_, crashpad_info_address)) { LOG(WARNING) << "could not read crashpad info" << module_info_; return false; } if (crashpad_info->signature != CrashpadInfo::kSignature || - crashpad_info->size != crashpad_info_section->size || - crashpad_info->version < 1) { - LOG(WARNING) << "unexpected crashpad info data" << module_info_; + crashpad_info->version != 1) { + LOG(WARNING) << base::StringPrintf( + "unexpected crashpad info signature 0x%x, version %u%s", + crashpad_info->signature, + crashpad_info->version, + module_info_.c_str()); return false; } + // Don’t require strict equality, to leave wiggle room for sloppy linkers. + if (crashpad_info->size > crashpad_info_section->size) { + LOG(WARNING) << "crashpad info struct size " << crashpad_info->size + << " large for section size " << crashpad_info_section->size + << module_info_; + return false; + } + + if (crashpad_info->size > crashpad_info->ExpectedSize(process_reader_)) { + // This isn’t strictly a problem, because unknown fields will simply be + // ignored, but it may be of diagnostic interest. + LOG(INFO) << "large crashpad info size " << crashpad_info->size + << module_info_; + } + return true; } diff --git a/snapshot/mac/process_types.cc b/snapshot/mac/process_types.cc index 90638fbb..35d81db1 100644 --- a/snapshot/mac/process_types.cc +++ b/snapshot/mac/process_types.cc @@ -14,6 +14,7 @@ #include "snapshot/mac/process_types.h" +#include #include #include @@ -140,6 +141,8 @@ inline void Assign(UInt64Array4* destination, #define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) + #define PROCESS_TYPE_STRUCT_END(struct_name) \ } \ } /* namespace internal */ \ @@ -151,6 +154,7 @@ inline void Assign(UInt64Array4* destination, #undef PROCESS_TYPE_STRUCT_BEGIN #undef PROCESS_TYPE_STRUCT_MEMBER #undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_SIZED #undef PROCESS_TYPE_STRUCT_END #undef PROCESS_TYPE_STRUCT_IMPLEMENT @@ -183,6 +187,8 @@ inline void Assign(UInt64Array4* destination, #define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) + #define PROCESS_TYPE_STRUCT_END(struct_name) #include "snapshot/mac/process_types/all.proctype" @@ -190,6 +196,7 @@ inline void Assign(UInt64Array4* destination, #undef PROCESS_TYPE_STRUCT_BEGIN #undef PROCESS_TYPE_STRUCT_MEMBER #undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_SIZED #undef PROCESS_TYPE_STRUCT_END #undef PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO @@ -254,6 +261,8 @@ inline void Assign(UInt64Array4* destination, #define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) + #define PROCESS_TYPE_STRUCT_END(struct_name) #include "snapshot/mac/process_types/all.proctype" @@ -261,6 +270,7 @@ inline void Assign(UInt64Array4* destination, #undef PROCESS_TYPE_STRUCT_BEGIN #undef PROCESS_TYPE_STRUCT_MEMBER #undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_SIZED #undef PROCESS_TYPE_STRUCT_END #undef PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY @@ -297,6 +307,8 @@ inline void Assign(UInt64Array4* destination, } /* namespace process_types */ \ } /* namespace crashpad */ +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) + #define PROCESS_TYPE_STRUCT_END(struct_name) #include "snapshot/mac/process_types/all.proctype" @@ -304,5 +316,62 @@ inline void Assign(UInt64Array4* destination, #undef PROCESS_TYPE_STRUCT_BEGIN #undef PROCESS_TYPE_STRUCT_MEMBER #undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_SIZED #undef PROCESS_TYPE_STRUCT_END #undef PROCESS_TYPE_STRUCT_IMPLEMENT_VERSIONED + +// Implement the generic crashpad::process_types::struct_name MinimumSize() and +// its templatized equivalent. The generic version delegates to the templatized +// one, which returns the minimum size of a sized structure. This can be used to +// ensure that enough of a sized structure is available to interpret its size +// field. This is only implemented for structures that use +// PROCESS_TYPE_STRUCT_SIZED(). +#define PROCESS_TYPE_STRUCT_IMPLEMENT_SIZED 1 + +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) + +#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) + +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) + +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) \ + namespace crashpad { \ + namespace process_types { \ + \ + namespace internal { \ + \ + /* static */ \ + template \ + size_t struct_name::MinimumSize() { \ + return offsetof(struct_name, size_field) + \ + sizeof(struct_name::size_field); \ + } \ + \ + /* Explicit instantiations of the above. */ \ + template size_t struct_name::MinimumSize(); \ + template size_t struct_name::MinimumSize(); \ + \ + } /* namespace internal */ \ + \ + /* static */ \ + size_t struct_name::MinimumSize(ProcessReader* process_reader) { \ + if (!process_reader->Is64Bit()) { \ + return internal::struct_name::MinimumSize(); \ + } else { \ + return internal::struct_name::MinimumSize(); \ + } \ + } \ + \ + } /* namespace process_types */ \ + } /* namespace crashpad */ + +#define PROCESS_TYPE_STRUCT_END(struct_name) + +#include "snapshot/mac/process_types/all.proctype" + +#undef PROCESS_TYPE_STRUCT_BEGIN +#undef PROCESS_TYPE_STRUCT_MEMBER +#undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_SIZED +#undef PROCESS_TYPE_STRUCT_END +#undef PROCESS_TYPE_STRUCT_IMPLEMENT_SIZED diff --git a/snapshot/mac/process_types.h b/snapshot/mac/process_types.h index eb397b9b..350b4060 100644 --- a/snapshot/mac/process_types.h +++ b/snapshot/mac/process_types.h @@ -94,12 +94,14 @@ DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64) \ /* Returns the size of the object that was read. This is the size of the \ * storage in the process that the data is read from, and is not the same \ - * as the size of the generic struct. */ \ + * as the size of the generic struct. For versioned and sized structures, \ + * the size of the full structure is returned. */ \ size_t Size() const { return size_; } \ \ /* Similar to Size(), but computes the expected size of a structure based \ * on the process’ bitness. This can be used prior to reading any data \ - * from a process. */ \ + * from a process. For versioned and sized structures, \ + * ExpectedSizeForVersion() and MinimumSize() may also be useful. */ \ static size_t ExpectedSize(ProcessReader* process_reader); #define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \ @@ -114,6 +116,13 @@ DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64) ProcessReader* process_reader, \ decltype(struct_name::version_field) version); +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) \ + /* Similar to ExpectedSize(), but computes the minimum size of a \ + * structure based on the process’ bitness, typically including enough of \ + * a structure to contain its size field. This can be used prior to \ + * reading any data from a process. */ \ + static size_t MinimumSize(ProcessReader* process_reader); + #define PROCESS_TYPE_STRUCT_END(struct_name) \ private: \ /* The static form of Read(). Populates the struct at |generic|. */ \ @@ -140,6 +149,7 @@ DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64) #undef PROCESS_TYPE_STRUCT_BEGIN #undef PROCESS_TYPE_STRUCT_MEMBER #undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_SIZED #undef PROCESS_TYPE_STRUCT_END #undef PROCESS_TYPE_STRUCT_DECLARE @@ -195,6 +205,10 @@ DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64) static size_t ExpectedSizeForVersion( \ decltype(struct_name::version_field) version); +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) \ + /* MinimumSize() is as in the generic user-visible struct above. */ \ + static size_t MinimumSize(); + #define PROCESS_TYPE_STRUCT_END(struct_name) \ private: \ /* ReadInto() is as in the generic user-visible struct above. */ \ @@ -211,6 +225,7 @@ DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64) #undef PROCESS_TYPE_STRUCT_BEGIN #undef PROCESS_TYPE_STRUCT_MEMBER #undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_SIZED #undef PROCESS_TYPE_STRUCT_END #undef PROCESS_TYPE_STRUCT_DECLARE_INTERNAL diff --git a/snapshot/mac/process_types/crashpad_info.proctype b/snapshot/mac/process_types/crashpad_info.proctype index 41af5fc9..7c077ca7 100644 --- a/snapshot/mac/process_types/crashpad_info.proctype +++ b/snapshot/mac/process_types/crashpad_info.proctype @@ -23,10 +23,25 @@ // Client Mach-O images will contain a __DATA,crashpad_info section formatted // according to this structure. +// +// CrashpadInfo is variable-length. Its length dictated by its |size| field +// which is always present. A custom implementation of the flavored +// ReadSpecificInto function that understands how to use this field is provided +// in snapshot/mac/process_types/custom.cc. No implementation of ReadArrayInto +// is provided because CrashpadInfo structs are singletons in a module and are +// never present in arrays, so the functionality is unnecessary. + +#if !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO) && \ + !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY) + PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo) PROCESS_TYPE_STRUCT_MEMBER(uint32_t, signature) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, size) + PROCESS_TYPE_STRUCT_SIZED(CrashpadInfo, size) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, version) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, indirectly_referenced_memory_cap) PROCESS_TYPE_STRUCT_MEMBER(uint32_t, padding_0) PROCESS_TYPE_STRUCT_MEMBER(uint8_t, crashpad_handler_behavior) // TriState @@ -51,3 +66,6 @@ PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo) // AnnotationList* PROCESS_TYPE_STRUCT_MEMBER(Pointer, annotations_list) PROCESS_TYPE_STRUCT_END(CrashpadInfo) + +#endif // ! PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO && + // ! PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY diff --git a/snapshot/mac/process_types/crashreporterclient.proctype b/snapshot/mac/process_types/crashreporterclient.proctype index 409782c9..fb518522 100644 --- a/snapshot/mac/process_types/crashreporterclient.proctype +++ b/snapshot/mac/process_types/crashreporterclient.proctype @@ -33,8 +33,8 @@ // flavored ReadSpecificInto function that understands how to map this field to // the structure’s actual size is provided in // snapshot/mac/process_types/custom.cc. No implementation of ReadArrayInto is -// provided because dyld_all_image_infos structs are singletons in a process and -// are never present in arrays, so the functionality is unnecessary. +// provided because crashreporter_annotations_t structs are singletons in a +// module and are never present in arrays, so the functionality is unnecessary. #if !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO) && \ !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY) diff --git a/snapshot/mac/process_types/custom.cc b/snapshot/mac/process_types/custom.cc index 25ecf3da..b9cb0e79 100644 --- a/snapshot/mac/process_types/custom.cc +++ b/snapshot/mac/process_types/custom.cc @@ -18,9 +18,12 @@ #include #include +#include #include #include "base/logging.h" +#include "base/numerics/safe_math.h" +#include "base/strings/stringprintf.h" #include "snapshot/mac/process_types/internal.h" #include "util/mach/task_memory.h" @@ -33,32 +36,88 @@ namespace internal { namespace { template -bool ReadIntoVersioned(ProcessReader* process_reader, - mach_vm_address_t address, - T* specific) { - TaskMemory* task_memory = process_reader->Memory(); - if (!task_memory->Read( - address, sizeof(specific->version), &specific->version)) { - return false; - } - - mach_vm_size_t size = T::ExpectedSizeForVersion(specific->version); +bool ReadIntoAndZero(TaskMemory* task_memory, + mach_vm_address_t address, + mach_vm_size_t size, + T* specific) { + DCHECK_LE(size, sizeof(*specific)); if (!task_memory->Read(address, size, specific)) { return false; } // Zero out the rest of the structure in case anything accesses fields without - // checking the version. - size_t remaining = sizeof(*specific) - size; + // checking the version or size. + const size_t remaining = sizeof(*specific) - size; if (remaining > 0) { - char* start = reinterpret_cast(specific) + size; + char* const start = reinterpret_cast(specific) + size; memset(start, 0, remaining); } return true; } +template +bool FieldAddressIfInRange(mach_vm_address_t address, + size_t offset, + mach_vm_address_t* field_address) { + base::CheckedNumeric checked_field_address(address); + checked_field_address += offset; + typename T::Pointer local_field_address; + if (!checked_field_address.AssignIfValid(&local_field_address)) { + LOG(ERROR) << base::StringPrintf( + "address 0x%llx + offset 0x%zx out of range", address, offset); + return false; + } + + *field_address = local_field_address; + return true; +} + +template +bool ReadIntoVersioned(ProcessReader* process_reader, + mach_vm_address_t address, + T* specific) { + mach_vm_address_t field_address; + if (!FieldAddressIfInRange( + address, offsetof(T, version), &field_address)) { + return false; + } + + TaskMemory* task_memory = process_reader->Memory(); + decltype(specific->version) version; + if (!task_memory->Read(field_address, sizeof(version), &version)) { + return false; + } + + const size_t size = T::ExpectedSizeForVersion(version); + return ReadIntoAndZero(task_memory, address, size, specific); +} + +template +bool ReadIntoSized(ProcessReader* process_reader, + mach_vm_address_t address, + T* specific) { + mach_vm_address_t field_address; + if (!FieldAddressIfInRange(address, offsetof(T, size), &field_address)) { + return false; + } + + TaskMemory* task_memory = process_reader->Memory(); + decltype(specific->size) size; + if (!task_memory->Read(address + offsetof(T, size), sizeof(size), &size)) { + return false; + } + + if (size < T::MinimumSize()) { + LOG(ERROR) << "small size " << size; + return false; + } + + size = std::min(static_cast(size), sizeof(*specific)); + return ReadIntoAndZero(task_memory, address, size, specific); +} + } // namespace // static @@ -125,22 +184,32 @@ bool crashreporter_annotations_t::ReadInto( return ReadIntoVersioned(process_reader, address, specific); } +// static +template +bool CrashpadInfo::ReadInto(ProcessReader* process_reader, + mach_vm_address_t address, + CrashpadInfo* specific) { + return ReadIntoSized(process_reader, address, specific); +} + // Explicit template instantiation of the above. #define PROCESS_TYPE_FLAVOR_TRAITS(lp_bits) \ template size_t \ - dyld_all_image_infos::ExpectedSizeForVersion( \ - decltype(dyld_all_image_infos::version)); \ + dyld_all_image_infos::ExpectedSizeForVersion( \ + decltype(dyld_all_image_infos::version)); \ template bool dyld_all_image_infos::ReadInto( \ ProcessReader*, \ mach_vm_address_t, \ dyld_all_image_infos*); \ template size_t \ - crashreporter_annotations_t::ExpectedSizeForVersion( \ - decltype(crashreporter_annotations_t::version)); \ + crashreporter_annotations_t::ExpectedSizeForVersion( \ + decltype(crashreporter_annotations_t::version)); \ template bool crashreporter_annotations_t::ReadInto( \ ProcessReader*, \ mach_vm_address_t, \ - crashreporter_annotations_t*); + crashreporter_annotations_t*); \ + template bool CrashpadInfo::ReadInto( \ + ProcessReader*, mach_vm_address_t, CrashpadInfo*); #include "snapshot/mac/process_types/flavors.h" diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index d720a10e..228fd5c9 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -53,6 +53,8 @@ 'type': 'executable', 'dependencies': [ 'crashpad_snapshot_test_module', + 'crashpad_snapshot_test_module_large', + 'crashpad_snapshot_test_module_small', 'snapshot.gyp:crashpad_snapshot', 'snapshot.gyp:crashpad_snapshot_api', '../client/client.gyp:crashpad_client', @@ -161,6 +163,32 @@ 'crashpad_info_client_options_test_module.cc', ], }, + { + 'target_name': 'crashpad_snapshot_test_module_large', + 'type': 'loadable_module', + 'dependencies': [ + '../third_party/mini_chromium/mini_chromium.gyp:base', + ], + 'defines': [ + 'CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE=1', + ], + 'sources': [ + 'crashpad_info_size_test_module.cc', + ], + }, + { + 'target_name': 'crashpad_snapshot_test_module_small', + 'type': 'loadable_module', + 'dependencies': [ + '../third_party/mini_chromium/mini_chromium.gyp:base', + ], + 'defines': [ + 'CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL=1', + ], + 'sources': [ + 'crashpad_info_size_test_module.cc', + ], + }, ], 'conditions': [ ['OS=="mac"', { diff --git a/snapshot/win/module_snapshot_win.cc b/snapshot/win/module_snapshot_win.cc index 8b6bb646..325e2db1 100644 --- a/snapshot/win/module_snapshot_win.cc +++ b/snapshot/win/module_snapshot_win.cc @@ -272,11 +272,10 @@ template void ModuleSnapshotWin::GetCrashpadExtraMemoryRanges( std::set>* ranges) const { process_types::CrashpadInfo crashpad_info; - if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info)) - return; - - if (!crashpad_info.extra_address_ranges) + if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info) || + !crashpad_info.extra_address_ranges) { return; + } std::vector simple_ranges( SimpleAddressRangeBag::num_entries); diff --git a/snapshot/win/pe_image_annotations_reader.cc b/snapshot/win/pe_image_annotations_reader.cc index 4ef8a1ea..67961457 100644 --- a/snapshot/win/pe_image_annotations_reader.cc +++ b/snapshot/win/pe_image_annotations_reader.cc @@ -85,11 +85,10 @@ template void PEImageAnnotationsReader::ReadCrashpadSimpleAnnotations( std::map* simple_map_annotations) const { process_types::CrashpadInfo crashpad_info; - if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info)) - return; - - if (!crashpad_info.simple_annotations) + if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info) || + !crashpad_info.simple_annotations) { return; + } std::vector simple_annotations(SimpleStringDictionary::num_entries); @@ -122,11 +121,8 @@ template void PEImageAnnotationsReader::ReadCrashpadAnnotationsList( std::vector* vector_annotations) const { process_types::CrashpadInfo crashpad_info; - if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info)) { - return; - } - - if (!crashpad_info.annotations_list) { + if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info) || + !crashpad_info.annotations_list) { return; } diff --git a/snapshot/win/pe_image_reader.cc b/snapshot/win/pe_image_reader.cc index 824f525b..aba7f588 100644 --- a/snapshot/win/pe_image_reader.cc +++ b/snapshot/win/pe_image_reader.cc @@ -17,9 +17,11 @@ #include #include +#include #include #include "base/logging.h" +#include "base/strings/stringprintf.h" #include "client/crashpad_info.h" #include "snapshot/win/pe_image_resource_reader.h" #include "util/misc/from_pointer_cast.h" @@ -80,39 +82,58 @@ bool PEImageReader::GetCrashpadInfo( return false; } - if (section.Misc.VirtualSize < sizeof(process_types::CrashpadInfo)) { + if (section.Misc.VirtualSize < + offsetof(process_types::CrashpadInfo, size) + + sizeof(crashpad_info->size)) { LOG(WARNING) << "small crashpad info section size " << section.Misc.VirtualSize << ", " << module_subrange_reader_.name(); return false; } - ProcessSubrangeReader crashpad_info_subrange_reader; const WinVMAddress crashpad_info_address = Address() + section.VirtualAddress; - if (!crashpad_info_subrange_reader.InitializeSubrange( - module_subrange_reader_, - crashpad_info_address, - section.Misc.VirtualSize, - "crashpad_info")) { - return false; - } - - if (!crashpad_info_subrange_reader.ReadMemory( - crashpad_info_address, - sizeof(process_types::CrashpadInfo), - crashpad_info)) { + const WinVMSize crashpad_info_size = + std::min(static_cast(sizeof(*crashpad_info)), + static_cast(section.Misc.VirtualSize)); + if (!module_subrange_reader_.ReadMemory( + crashpad_info_address, crashpad_info_size, crashpad_info)) { LOG(WARNING) << "could not read crashpad info from " << module_subrange_reader_.name(); return false; } + if (crashpad_info->size < sizeof(*crashpad_info)) { + // Zero out anything beyond the structure’s declared size. + memset(reinterpret_cast(crashpad_info) + crashpad_info->size, + 0, + sizeof(*crashpad_info) - crashpad_info->size); + } + if (crashpad_info->signature != CrashpadInfo::kSignature || - crashpad_info->version < 1) { - LOG(WARNING) << "unexpected crashpad info data in " - << module_subrange_reader_.name(); + crashpad_info->version != 1) { + LOG(WARNING) << base::StringPrintf( + "unexpected crashpad info signature 0x%x, version %u in %s", + crashpad_info->signature, + crashpad_info->version, + module_subrange_reader_.name().c_str()); return false; } + // Don’t require strict equality, to leave wiggle room for sloppy linkers. + if (crashpad_info->size > section.Misc.VirtualSize) { + LOG(WARNING) << "crashpad info struct size " << crashpad_info->size + << " large for section size " << section.Misc.VirtualSize + << " in " << module_subrange_reader_.name(); + return false; + } + + if (crashpad_info->size > sizeof(*crashpad_info)) { + // This isn’t strictly a problem, because unknown fields will simply be + // ignored, but it may be of diagnostic interest. + LOG(INFO) << "large crashpad info size " << crashpad_info->size << ", " + << module_subrange_reader_.name(); + } + return true; } From 6dd2be7c44a9c8bbea5df918e7ebe46d76da97df Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Wed, 15 Nov 2017 18:43:46 -0500 Subject: [PATCH 022/326] Stop providing unimplemented stubs for util/mach/exc_server_variants.cc. Rather than providing these stubs to make the linker happy, use the mig.py script to modify the _Xserver_routine functions to not even call server_routine. Change-Id: I5a2f5cd228462e38dddbf899d0ad8033a6f817bd Reviewed-on: https://chromium-review.googlesource.com/773359 Commit-Queue: Robert Sesek Reviewed-by: Mark Mentovai --- util/mach/exc_server_variants.cc | 92 -------------------------------- util/mach/mig.py | 12 +++++ 2 files changed, 12 insertions(+), 92 deletions(-) diff --git a/util/mach/exc_server_variants.cc b/util/mach/exc_server_variants.cc index ff918065..e7174ac7 100644 --- a/util/mach/exc_server_variants.cc +++ b/util/mach/exc_server_variants.cc @@ -30,98 +30,6 @@ #include "util/mach/mach_excServer.h" #include "util/mach/mach_message.h" -extern "C" { - -// These six functions are not used, and are in fact obsoleted by the other -// functionality implemented in this file. The standard MIG-generated exc_server -// (in excServer.c) and mach_exc_server (in mach_excServer.c) server dispatch -// routines usable with the standard mach_msg_server() function call out to -// these functions. exc_server() and mach_exc_server() are unused and are -// replaced by the more flexible ExcServer and MachExcServer, but the linker -// still needs to see these six function definitions. - -kern_return_t catch_exception_raise(exception_handler_t exception_port, - thread_t thread, - task_t task, - exception_type_t exception, - exception_data_t code, - mach_msg_type_number_t code_count) { - NOTREACHED(); - return KERN_FAILURE; -} - -kern_return_t catch_exception_raise_state( - exception_handler_t exception_port, - exception_type_t exception, - exception_data_t code, - mach_msg_type_number_t code_count, - thread_state_flavor_t* flavor, - thread_state_t old_state, - mach_msg_type_number_t old_state_count, - thread_state_t new_state, - mach_msg_type_number_t* new_state_count) { - NOTREACHED(); - return KERN_FAILURE; -} - -kern_return_t catch_exception_raise_state_identity( - exception_handler_t exception_port, - thread_t thread, - task_t task, - exception_type_t exception, - exception_data_t code, - mach_msg_type_number_t code_count, - thread_state_flavor_t* flavor, - thread_state_t old_state, - mach_msg_type_number_t old_state_count, - thread_state_t new_state, - mach_msg_type_number_t* new_state_count) { - NOTREACHED(); - return KERN_FAILURE; -} - -kern_return_t catch_mach_exception_raise(exception_handler_t exception_port, - thread_t thread, - task_t task, - exception_type_t exception, - mach_exception_data_t code, - mach_msg_type_number_t code_count) { - NOTREACHED(); - return KERN_FAILURE; -} - -kern_return_t catch_mach_exception_raise_state( - exception_handler_t exception_port, - exception_type_t exception, - mach_exception_data_t code, - mach_msg_type_number_t code_count, - thread_state_flavor_t* flavor, - thread_state_t old_state, - mach_msg_type_number_t old_state_count, - thread_state_t new_state, - mach_msg_type_number_t* new_state_count) { - NOTREACHED(); - return KERN_FAILURE; -} - -kern_return_t catch_mach_exception_raise_state_identity( - exception_handler_t exception_port, - thread_t thread, - task_t task, - exception_type_t exception, - mach_exception_data_t code, - mach_msg_type_number_t code_count, - thread_state_flavor_t* flavor, - thread_state_t old_state, - mach_msg_type_number_t old_state_count, - thread_state_t new_state, - mach_msg_type_number_t* new_state_count) { - NOTREACHED(); - return KERN_FAILURE; -} - -} // extern "C" - namespace crashpad { namespace { diff --git a/util/mach/mig.py b/util/mach/mig.py index 992f3e1a..9833c8c5 100755 --- a/util/mach/mig.py +++ b/util/mach/mig.py @@ -73,6 +73,18 @@ def FixServerImplementation(implementation): # Rewrite the declarations in this file as “mig_external”. contents = declaration_pattern.sub(r'mig_external \1', contents); + # Crashpad never implements the mach_msg_server() MIG callouts. To avoid + # needing to provide stub implementations, set KERN_FAILURE as the RetCode + # and abort(). + routine_callout_pattern = re.compile( + r'OutP->RetCode = (([a-zA-Z0-9_]+)\(.+\));') + routine_callouts = routine_callout_pattern.findall(contents) + for routine in routine_callouts: + contents = contents.replace(routine[0], 'KERN_FAILURE; abort()') + + # Include the header for abort(). + contents = '#include \n' + contents + file.seek(0) file.truncate() file.write(contents) From f09257b17c36b8e35d315c8e7b5c65ac2f8725f1 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Thu, 16 Nov 2017 10:52:15 -0500 Subject: [PATCH 023/326] Remove more unneeded MIG server stubs. These were missed with the mig.py change in 6dd2be7c44a9c8bbea5df918e7ebe46d76da97df. Change-Id: I7ad066cd9425cab26e56a8b3dfb90f5f54a6648d Reviewed-on: https://chromium-review.googlesource.com/774999 Reviewed-by: Mark Mentovai Commit-Queue: Robert Sesek --- util/mach/child_port_server.cc | 19 ---------------- util/mach/notify_server.cc | 40 ---------------------------------- 2 files changed, 59 deletions(-) diff --git a/util/mach/child_port_server.cc b/util/mach/child_port_server.cc index 9a34d431..a3223028 100644 --- a/util/mach/child_port_server.cc +++ b/util/mach/child_port_server.cc @@ -18,25 +18,6 @@ #include "util/mach/child_portServer.h" #include "util/mach/mach_message.h" -extern "C" { - -// This function is not used, and is in fact obsoleted by the other -// functionality implemented in this file. The standard MIG-generated -// child_port_server() (in child_portServer.c) server dispatch routine usable -// with the standard mach_msg_server() function calls out to this function. -// child_port_server() is unused and is replaced by the more flexible -// ChildPortServer, but the linker still needs to see this function definition. - -kern_return_t handle_child_port_check_in(child_port_server_t server, - child_port_token_t token, - mach_port_t port, - mach_msg_type_name_t right_type) { - NOTREACHED(); - return KERN_FAILURE; -} - -} // extern "C" - namespace { // There is no predefined constant for this. diff --git a/util/mach/notify_server.cc b/util/mach/notify_server.cc index 2dbdf73e..711529b1 100644 --- a/util/mach/notify_server.cc +++ b/util/mach/notify_server.cc @@ -18,46 +18,6 @@ #include "util/mach/mach_message.h" #include "util/mach/notifyServer.h" -extern "C" { - -// These five functions are not used, and are in fact obsoleted by the other -// functionality implemented in this file. The standard MIG-generated -// notify_server() (in notifyServer.c) server dispatch routine usable with the -// standard mach_msg_server() function calls out to this function. -// notify_server() is unused and is replaced by the more flexible NotifyServer, -// but the linker still needs to see these five function definitions. - -kern_return_t do_mach_notify_port_deleted(notify_port_t notify, - mach_port_name_t name) { - NOTREACHED(); - return KERN_FAILURE; -} - -kern_return_t do_mach_notify_port_destroyed(notify_port_t notify, - mach_port_t rights) { - NOTREACHED(); - return KERN_FAILURE; -} - -kern_return_t do_mach_notify_no_senders(notify_port_t notify, - mach_port_mscount_t mscount) { - NOTREACHED(); - return KERN_FAILURE; -} - -kern_return_t do_mach_notify_send_once(notify_port_t notify) { - NOTREACHED(); - return KERN_FAILURE; -} - -kern_return_t do_mach_notify_dead_name(notify_port_t notify, - mach_port_name_t name) { - NOTREACHED(); - return KERN_FAILURE; -} - -} // extern "C" - namespace { // The MIG-generated __MIG_check__Request__*() functions are not declared as From a7453394d6ed712a56c000dafa465857f9eee970 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Thu, 16 Nov 2017 15:10:08 -0500 Subject: [PATCH 024/326] Provide a StringPiece getter and setter for StringAnnotation. Bug: crashpad:192 Change-Id: Ia8957a1b6f0076257ef385a9299d9b5895cc17be Reviewed-on: https://chromium-review.googlesource.com/775140 Commit-Queue: Robert Sesek Reviewed-by: Mark Mentovai --- client/annotation.h | 17 +++++++++++++++++ client/annotation_test.cc | 14 +++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/client/annotation.h b/client/annotation.h index f76ddf68..34d0d566 100644 --- a/client/annotation.h +++ b/client/annotation.h @@ -25,6 +25,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/numerics/safe_conversions.h" +#include "base/strings/string_piece.h" #include "build/build_config.h" namespace crashpad { @@ -210,6 +211,22 @@ class StringAnnotation : public Annotation { std::min(MaxSize, base::saturated_cast(strlen(value)))); } + //! \brief Sets the Annotation's string value. + //! + //! \param[in] value The string value. + void Set(base::StringPiece string) { + Annotation::ValueSizeType size = + std::min(MaxSize, base::saturated_cast(string.size())); + memcpy(value_, string.data(), size); + // Check for no embedded `NUL` characters. + DCHECK(!memchr(value_, '\0', size)); + SetSize(size); + } + + const base::StringPiece value() const { + return base::StringPiece(value_, size()); + } + private: // This value is not `NUL`-terminated, since the size is stored by the base // annotation. diff --git a/client/annotation_test.cc b/client/annotation_test.cc index 25530715..083b309b 100644 --- a/client/annotation_test.cc +++ b/client/annotation_test.cc @@ -19,6 +19,7 @@ #include "client/annotation_list.h" #include "client/crashpad_info.h" #include "gtest/gtest.h" +#include "test/gtest_death_check.h" namespace crashpad { namespace test { @@ -81,14 +82,13 @@ TEST_F(Annotation, Basics) { TEST_F(Annotation, StringType) { crashpad::StringAnnotation<5> annotation("name"); - const char* value_ptr = static_cast(annotation.value()); EXPECT_FALSE(annotation.is_set()); EXPECT_EQ(crashpad::Annotation::Type::kString, annotation.type()); EXPECT_EQ(0u, annotation.size()); EXPECT_EQ(std::string("name"), annotation.name()); - EXPECT_EQ(0u, strlen(value_ptr)); + EXPECT_EQ(0u, annotation.value().size()); annotation.Set("test"); @@ -96,15 +96,19 @@ TEST_F(Annotation, StringType) { EXPECT_EQ(1u, AnnotationsCount()); EXPECT_EQ(4u, annotation.size()); - EXPECT_EQ(std::string("test"), value_ptr); + EXPECT_EQ("test", annotation.value()); - annotation.Set("loooooooooooong"); + annotation.Set(std::string("loooooooooooong")); EXPECT_TRUE(annotation.is_set()); EXPECT_EQ(1u, AnnotationsCount()); EXPECT_EQ(5u, annotation.size()); - EXPECT_EQ(std::string("loooo"), std::string(value_ptr, annotation.size())); + EXPECT_EQ("loooo", annotation.value()); + +#if DCHECK_IS_ON() + EXPECT_DEATH_CHECK(annotation.Set(std::string("te\0st", 5)), ""); +#endif } } // namespace From d5ead4d70f0ba11eb2aa2fbeb76133b3136ddc22 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 17 Nov 2017 17:48:22 -0800 Subject: [PATCH 025/326] Upstream lightly modified Chromium BUILD.gn files Unreferenced, and not working at all in Crashpad-standalone. Copied from Chromium at 52a9831d81f2099ef9f50fcdaca5853019262c35 to have a point where a roll back into Chromium should be a no-op (with Chromium's build/secondary/third_party/crashpad/... removed). I'm not sure what we want to do about the various gni references into Chromium (e.g. //build/config/sanitizers/sanitizers.gni, //testing/test.gni, etc.) but I guess the sooner they live in Crashpad rather than in Chromium the sooner we can figure out the sort of knobs and dials we need. Bug: crashpad:79 Change-Id: Id99c29123bcd4174ee2bcc128c2be87e3c94fa3f Reviewed-on: https://chromium-review.googlesource.com/777819 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- BUILD.gn | 31 ++ client/BUILD.gn | 107 +++++++ compat/BUILD.gn | 82 +++++ handler/BUILD.gn | 164 ++++++++++ minidump/BUILD.gn | 169 +++++++++++ snapshot/BUILD.gn | 408 +++++++++++++++++++++++++ snapshot/test/BUILD.gn | 155 ++++++++++ test/BUILD.gn | 155 ++++++++++ third_party/apple_cctools/BUILD.gn | 25 ++ third_party/getopt/BUILD.gn | 20 ++ third_party/zlib/BUILD.gn | 24 ++ tools/BUILD.gn | 161 ++++++++++ util/BUILD.gn | 470 +++++++++++++++++++++++++++++ 13 files changed, 1971 insertions(+) create mode 100644 BUILD.gn create mode 100644 client/BUILD.gn create mode 100644 compat/BUILD.gn create mode 100644 handler/BUILD.gn create mode 100644 minidump/BUILD.gn create mode 100644 snapshot/BUILD.gn create mode 100644 snapshot/test/BUILD.gn create mode 100644 test/BUILD.gn create mode 100644 third_party/apple_cctools/BUILD.gn create mode 100644 third_party/getopt/BUILD.gn create mode 100644 third_party/zlib/BUILD.gn create mode 100644 tools/BUILD.gn create mode 100644 util/BUILD.gn diff --git a/BUILD.gn b/BUILD.gn new file mode 100644 index 00000000..5c3918ef --- /dev/null +++ b/BUILD.gn @@ -0,0 +1,31 @@ +# 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. + +import("//testing/test.gni") + +config("crashpad_config") { + include_dirs = [ "//third_party/crashpad/crashpad" ] +} + +test("crashpad_tests") { + deps = [ + "client:client_test", + "handler:handler_test", + "minidump:minidump_test", + "snapshot:snapshot_test", + "test:gmock_main", + "test:test_test", + "util:util_test", + ] +} diff --git a/client/BUILD.gn b/client/BUILD.gn new file mode 100644 index 00000000..3e24d6dd --- /dev/null +++ b/client/BUILD.gn @@ -0,0 +1,107 @@ +# 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. + +import("//testing/test.gni") + +static_library("client") { + sources = [ + "annotation.cc", + "annotation.h", + "annotation_list.cc", + "annotation_list.h", + "crash_report_database.cc", + "crash_report_database.h", + "crash_report_database_mac.mm", + "crash_report_database_win.cc", + "crashpad_client.h", + "crashpad_client_mac.cc", + "crashpad_client_win.cc", + "crashpad_info.cc", + "crashpad_info.h", + "prune_crash_reports.cc", + "prune_crash_reports.h", + "settings.cc", + "settings.h", + "simple_address_range_bag.h", + "simple_string_dictionary.h", + "simulate_crash.h", + "simulate_crash_mac.cc", + "simulate_crash_mac.h", + "simulate_crash_win.h", + ] + + if (is_mac) { + sources += [ + "capture_context_mac.S", + "capture_context_mac.h", + ] + } + + public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + + deps = [ + "//base", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] + + if (is_win) { + libs = [ "rpcrt4.lib" ] + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + } +} + +source_set("client_test") { + testonly = true + + sources = [ + "annotation_list_test.cc", + "annotation_test.cc", + "crash_report_database_test.cc", + "prune_crash_reports_test.cc", + "settings_test.cc", + "simple_address_range_bag_test.cc", + "simple_string_dictionary_test.cc", + ] + + if (is_mac) { + sources += [ + "capture_context_mac_test.cc", + "simulate_crash_mac_test.cc", + ] + } + + if (is_win) { + sources += [ "crashpad_client_win_test.cc" ] + } + + deps = [ + ":client", + "//base", + "//testing/gmock", + "//testing/gtest", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/test", + "//third_party/crashpad/crashpad/util", + ] + + data_deps = [ + "//third_party/crashpad/crashpad/handler:crashpad_handler", + ] + + if (is_win) { + data_deps += + [ "//third_party/crashpad/crashpad/handler:crashpad_handler_console" ] + } +} diff --git a/compat/BUILD.gn b/compat/BUILD.gn new file mode 100644 index 00000000..ddeb53d7 --- /dev/null +++ b/compat/BUILD.gn @@ -0,0 +1,82 @@ +# 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. + +config("compat_config") { + include_dirs = [] + + if (is_win) { + include_dirs += [ "win" ] + } else { + include_dirs += [ "non_win" ] + } + + if (is_mac) { + include_dirs += [ "mac" ] + } +} + +static_library("compat") { + sources = [] + + if (is_mac) { + sources += [ + "mac/AvailabilityMacros.h", + "mac/kern/exc_resource.h", + "mac/mach-o/getsect.cc", + "mac/mach-o/getsect.h", + "mac/mach-o/loader.h", + "mac/mach/mach.h", + "mac/sys/resource.h", + ] + } else { + sources += [ "non_mac/mach/mach.h" ] + } + + if (is_win) { + sources += [ + "win/getopt.h", + "win/strings.cc", + "win/strings.h", + "win/sys/types.h", + "win/time.cc", + "win/time.h", + "win/winbase.h", + "win/winnt.h", + "win/winternl.h", + ] + } else { + sources += [ + "non_win/dbghelp.h", + "non_win/minwinbase.h", + "non_win/timezoneapi.h", + "non_win/verrsrc.h", + "non_win/windows.h", + "non_win/winnt.h", + ] + } + + public_configs = [ + ":compat_config", + "//third_party/crashpad/crashpad:crashpad_config", + ] + + deps = [] + + if (is_mac) { + deps += [ "//third_party/crashpad/crashpad/third_party/apple_cctools" ] + } + if (is_win) { + deps += [ "//third_party/crashpad/crashpad/third_party/getopt" ] + } +} diff --git a/handler/BUILD.gn b/handler/BUILD.gn new file mode 100644 index 00000000..2c17f3d9 --- /dev/null +++ b/handler/BUILD.gn @@ -0,0 +1,164 @@ +# 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. + +import("//testing/test.gni") + +static_library("handler") { + sources = [ + "crash_report_upload_thread.cc", + "crash_report_upload_thread.h", + "handler_main.cc", + "handler_main.h", + "mac/crash_report_exception_handler.cc", + "mac/crash_report_exception_handler.h", + "mac/exception_handler_server.cc", + "mac/exception_handler_server.h", + "mac/file_limit_annotation.cc", + "mac/file_limit_annotation.h", + "minidump_to_upload_parameters.cc", + "minidump_to_upload_parameters.h", + "prune_crash_reports_thread.cc", + "prune_crash_reports_thread.h", + "user_stream_data_source.cc", + "user_stream_data_source.h", + "win/crash_report_exception_handler.cc", + "win/crash_report_exception_handler.h", + ] + + public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + + deps = [ + "//base", + "//third_party/crashpad/crashpad/client", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/minidump", + "//third_party/crashpad/crashpad/snapshot", + "//third_party/crashpad/crashpad/tools:tool_support", + "//third_party/crashpad/crashpad/util", + ] + + if (is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + } +} + +source_set("handler_test") { + testonly = true + + sources = [ + "minidump_to_upload_parameters_test.cc", + ] + + deps = [ + ":handler", + "//base", + "//testing/gtest", + "//third_party/crashpad/crashpad/client", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/snapshot", + "//third_party/crashpad/crashpad/test", + "//third_party/crashpad/crashpad/util", + ] + + if (is_win) { + sources += [ "crashpad_handler_test.cc" ] + + data_deps = [ + ":crashpad_handler_test_extended_handler", + ] + } +} + +executable("crashpad_handler") { + sources = [ + "main.cc", + ] + + deps = [ + ":handler", + "//base", + "//build/win:default_exe_manifest", + "//third_party/crashpad/crashpad/compat", + ] + + if (is_mac && is_component_build) { + ldflags = [ + # The handler is in + # Chromium.app/Contents/Versions/X/Chromium Framework.framework/Versions/A/Helpers/ + # so set rpath up to the base. + "-rpath", + "@loader_path/../../../../../../../..", + + # The handler is also in + # Content Shell.app/Contents/Frameworks/Content Shell Framework.framework/Helpers/ + # so set the rpath for that too. + "-rpath", + "@loader_path/../../../../..", + ] + } + + if (is_win) { + configs -= [ "//build/config/win:console" ] + configs += [ "//build/config/win:windowed" ] + } +} + +executable("crashpad_handler_test_extended_handler") { + testonly = true + + sources = [ + "crashpad_handler_test_extended_handler.cc", + ] + + deps = [ + ":handler", + "//base", + "//build/win:default_exe_manifest", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/minidump:test_support", + "//third_party/crashpad/crashpad/tools:tool_support", + ] +} + +if (is_win) { + executable("crashpad_handler_com") { + sources = [ + "main.cc", + ] + + # Avoid .exp, .ilk, and .lib file collisions with crashpad_handler.exe by + # having this target produce crashpad_handler_com.com. Don’t use this target + # directly. Instead, use crashpad_handler_console. + output_extension = "com" + + deps = [ + ":handler", + "//base", + "//build/win:default_exe_manifest", + "//third_party/crashpad/crashpad/compat", + ] + } + + copy("crashpad_handler_console") { + deps = [ + ":crashpad_handler_com", + ] + sources = [ + "$root_out_dir/crashpad_handler_com.com", + ] + outputs = [ + "$root_out_dir/crashpad_handler.com", + ] + } +} diff --git a/minidump/BUILD.gn b/minidump/BUILD.gn new file mode 100644 index 00000000..42f8c3e1 --- /dev/null +++ b/minidump/BUILD.gn @@ -0,0 +1,169 @@ +# 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. + +import("//testing/test.gni") + +static_library("minidump") { + sources = [ + "minidump_annotation_writer.cc", + "minidump_annotation_writer.h", + "minidump_byte_array_writer.cc", + "minidump_byte_array_writer.h", + "minidump_context.h", + "minidump_context_writer.cc", + "minidump_context_writer.h", + "minidump_crashpad_info_writer.cc", + "minidump_crashpad_info_writer.h", + "minidump_exception_writer.cc", + "minidump_exception_writer.h", + "minidump_extensions.cc", + "minidump_extensions.h", + "minidump_file_writer.cc", + "minidump_file_writer.h", + "minidump_handle_writer.cc", + "minidump_handle_writer.h", + "minidump_memory_info_writer.cc", + "minidump_memory_info_writer.h", + "minidump_memory_writer.cc", + "minidump_memory_writer.h", + "minidump_misc_info_writer.cc", + "minidump_misc_info_writer.h", + "minidump_module_crashpad_info_writer.cc", + "minidump_module_crashpad_info_writer.h", + "minidump_module_writer.cc", + "minidump_module_writer.h", + "minidump_rva_list_writer.cc", + "minidump_rva_list_writer.h", + "minidump_simple_string_dictionary_writer.cc", + "minidump_simple_string_dictionary_writer.h", + "minidump_stream_writer.cc", + "minidump_stream_writer.h", + "minidump_string_writer.cc", + "minidump_string_writer.h", + "minidump_system_info_writer.cc", + "minidump_system_info_writer.h", + "minidump_thread_id_map.cc", + "minidump_thread_id_map.h", + "minidump_thread_writer.cc", + "minidump_thread_writer.h", + "minidump_unloaded_module_writer.cc", + "minidump_unloaded_module_writer.h", + "minidump_user_extension_stream_data_source.cc", + "minidump_user_extension_stream_data_source.h", + "minidump_user_stream_writer.cc", + "minidump_user_stream_writer.h", + "minidump_writable.cc", + "minidump_writable.h", + "minidump_writer_util.cc", + "minidump_writer_util.h", + ] + + public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + + public_deps = [ + "//third_party/crashpad/crashpad/compat", + ] + + deps = [ + "//base", + "//third_party/crashpad/crashpad/snapshot", + "//third_party/crashpad/crashpad/util", + ] + + if (is_win) { + cflags = [ + "/wd4201", # nonstandard extension used : nameless struct/union + "/wd4324", # 'struct' : structure was padded due to __declspec(align()) + ] + } +} + +static_library("test_support") { + testonly = true + + sources = [ + "test/minidump_byte_array_writer_test_util.cc", + "test/minidump_byte_array_writer_test_util.h", + "test/minidump_context_test_util.cc", + "test/minidump_context_test_util.h", + "test/minidump_file_writer_test_util.cc", + "test/minidump_file_writer_test_util.h", + "test/minidump_memory_writer_test_util.cc", + "test/minidump_memory_writer_test_util.h", + "test/minidump_rva_list_test_util.cc", + "test/minidump_rva_list_test_util.h", + "test/minidump_string_writer_test_util.cc", + "test/minidump_string_writer_test_util.h", + "test/minidump_user_extension_stream_util.cc", + "test/minidump_user_extension_stream_util.h", + "test/minidump_writable_test_util.cc", + "test/minidump_writable_test_util.h", + ] + + public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + + public_deps = [ + ":minidump", + ] + + deps = [ + "//base", + "//testing/gtest", + ] + + if (is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + } +} + +source_set("minidump_test") { + testonly = true + + sources = [ + "minidump_annotation_writer_test.cc", + "minidump_byte_array_writer_test.cc", + "minidump_context_writer_test.cc", + "minidump_crashpad_info_writer_test.cc", + "minidump_exception_writer_test.cc", + "minidump_file_writer_test.cc", + "minidump_handle_writer_test.cc", + "minidump_memory_info_writer_test.cc", + "minidump_memory_writer_test.cc", + "minidump_misc_info_writer_test.cc", + "minidump_module_crashpad_info_writer_test.cc", + "minidump_module_writer_test.cc", + "minidump_rva_list_writer_test.cc", + "minidump_simple_string_dictionary_writer_test.cc", + "minidump_string_writer_test.cc", + "minidump_system_info_writer_test.cc", + "minidump_thread_id_map_test.cc", + "minidump_thread_writer_test.cc", + "minidump_unloaded_module_writer_test.cc", + "minidump_user_stream_writer_test.cc", + "minidump_writable_test.cc", + ] + + deps = [ + ":test_support", + "//base", + "//testing/gtest", + "//third_party/crashpad/crashpad/snapshot:test_support", + "//third_party/crashpad/crashpad/test", + "//third_party/crashpad/crashpad/util", + ] + + if (is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + } +} diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn new file mode 100644 index 00000000..49402fe9 --- /dev/null +++ b/snapshot/BUILD.gn @@ -0,0 +1,408 @@ +# 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. + +import("//build/config/compiler/compiler.gni") +import("//testing/test.gni") + +static_library("snapshot") { + sources = [ + "annotation_snapshot.cc", + "annotation_snapshot.h", + "capture_memory.cc", + "capture_memory.h", + "cpu_architecture.h", + "cpu_context.cc", + "cpu_context.h", + "crashpad_info_client_options.cc", + "crashpad_info_client_options.h", + "exception_snapshot.h", + "handle_snapshot.cc", + "handle_snapshot.h", + "mac/cpu_context_mac.cc", + "mac/cpu_context_mac.h", + "mac/exception_snapshot_mac.cc", + "mac/exception_snapshot_mac.h", + "mac/mach_o_image_annotations_reader.cc", + "mac/mach_o_image_annotations_reader.h", + "mac/mach_o_image_reader.cc", + "mac/mach_o_image_reader.h", + "mac/mach_o_image_segment_reader.cc", + "mac/mach_o_image_segment_reader.h", + "mac/mach_o_image_symbol_table_reader.cc", + "mac/mach_o_image_symbol_table_reader.h", + "mac/memory_snapshot_mac.cc", + "mac/memory_snapshot_mac.h", + "mac/module_snapshot_mac.cc", + "mac/module_snapshot_mac.h", + "mac/process_reader.cc", + "mac/process_reader.h", + "mac/process_snapshot_mac.cc", + "mac/process_snapshot_mac.h", + "mac/process_types.cc", + "mac/process_types.h", + "mac/process_types/all.proctype", + "mac/process_types/annotation.proctype", + "mac/process_types/crashpad_info.proctype", + "mac/process_types/crashreporterclient.proctype", + "mac/process_types/custom.cc", + "mac/process_types/dyld_images.proctype", + "mac/process_types/flavors.h", + "mac/process_types/internal.h", + "mac/process_types/loader.proctype", + "mac/process_types/nlist.proctype", + "mac/process_types/traits.h", + "mac/system_snapshot_mac.cc", + "mac/system_snapshot_mac.h", + "mac/thread_snapshot_mac.cc", + "mac/thread_snapshot_mac.h", + "memory_snapshot.h", + "minidump/minidump_annotation_reader.cc", + "minidump/minidump_annotation_reader.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", + "module_snapshot.h", + "posix/timezone.cc", + "posix/timezone.h", + "process_snapshot.h", + "snapshot_constants.h", + "system_snapshot.h", + "thread_snapshot.h", + "unloaded_module_snapshot.cc", + "unloaded_module_snapshot.h", + "win/capture_memory_delegate_win.cc", + "win/capture_memory_delegate_win.h", + "win/cpu_context_win.cc", + "win/cpu_context_win.h", + "win/exception_snapshot_win.cc", + "win/exception_snapshot_win.h", + "win/memory_map_region_snapshot_win.cc", + "win/memory_map_region_snapshot_win.h", + "win/memory_snapshot_win.cc", + "win/memory_snapshot_win.h", + "win/module_snapshot_win.cc", + "win/module_snapshot_win.h", + "win/pe_image_annotations_reader.cc", + "win/pe_image_annotations_reader.h", + "win/pe_image_reader.cc", + "win/pe_image_reader.h", + "win/pe_image_resource_reader.cc", + "win/pe_image_resource_reader.h", + "win/process_reader_win.cc", + "win/process_reader_win.h", + "win/process_snapshot_win.cc", + "win/process_snapshot_win.h", + "win/process_subrange_reader.cc", + "win/process_subrange_reader.h", + "win/system_snapshot_win.cc", + "win/system_snapshot_win.h", + "win/thread_snapshot_win.cc", + "win/thread_snapshot_win.h", + ] + + if (target_cpu == "x86" || target_cpu == "x64") { + sources += [ + "x86/cpuid_reader.cc", + "x86/cpuid_reader.h", + ] + } + + public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + + deps = [ + "//base", + "//third_party/crashpad/crashpad/client", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] + + if (is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + libs = [ "powrprof.lib" ] + } +} + +if (is_win) { + static_library("snapshot_api") { + sources = [ + "api/module_annotations_win.cc", + "api/module_annotations_win.h", + ] + + public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + + cflags = [ "/wd4201" ] + + deps = [ + ":snapshot", + "//base", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] + } +} else { + group("snapshot_api") { + } +} + +static_library("test_support") { + testonly = true + + sources = [ + "test/test_cpu_context.cc", + "test/test_cpu_context.h", + "test/test_exception_snapshot.cc", + "test/test_exception_snapshot.h", + "test/test_memory_map_region_snapshot.cc", + "test/test_memory_map_region_snapshot.h", + "test/test_memory_snapshot.cc", + "test/test_memory_snapshot.h", + "test/test_module_snapshot.cc", + "test/test_module_snapshot.h", + "test/test_process_snapshot.cc", + "test/test_process_snapshot.h", + "test/test_system_snapshot.cc", + "test/test_system_snapshot.h", + "test/test_thread_snapshot.cc", + "test/test_thread_snapshot.h", + ] + + public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + + public_deps = [ + ":snapshot", + ] + + deps = [ + "//base", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] + + if (is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + } +} + +source_set("snapshot_test") { + testonly = true + + sources = [ + "cpu_context_test.cc", + "crashpad_info_client_options_test.cc", + "mac/cpu_context_mac_test.cc", + "mac/mach_o_image_annotations_reader_test.cc", + "mac/mach_o_image_reader_test.cc", + "mac/mach_o_image_segment_reader_test.cc", + "mac/process_reader_test.cc", + "mac/process_types_test.cc", + "mac/system_snapshot_mac_test.cc", + "minidump/process_snapshot_minidump_test.cc", + "win/cpu_context_win_test.cc", + "win/exception_snapshot_win_test.cc", + "win/extra_memory_ranges_test.cc", + "win/pe_image_annotations_reader_test.cc", + "win/pe_image_reader_test.cc", + "win/process_reader_win_test.cc", + "win/process_snapshot_win_test.cc", + "win/system_snapshot_win_test.cc", + ] + + if (is_win) { + sources += [ "api/module_annotations_win_test.cc" ] + } else { + sources += [ "posix/timezone_test.cc" ] + } + + deps = [ + ":snapshot_api", + ":test_support", + "//base", + "//testing/gtest", + "//third_party/crashpad/crashpad/client", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/test", + "//third_party/crashpad/crashpad/util", + ] + + data_deps = [ + ":crashpad_snapshot_test_module", + ":crashpad_snapshot_test_module_large", + ":crashpad_snapshot_test_module_small", + ] + + if (is_mac) { + libs = [ "OpenCL.framework" ] + + data_deps += [ + ":crashpad_snapshot_test_module_crashy_initializer", + ":crashpad_snapshot_test_no_op", + ] + } + + if (is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + + data_deps += [ + ":crashpad_snapshot_test_annotations", + ":crashpad_snapshot_test_crashing_child", + ":crashpad_snapshot_test_dump_without_crashing", + ":crashpad_snapshot_test_extra_memory_ranges", + ":crashpad_snapshot_test_image_reader", + ":crashpad_snapshot_test_image_reader_module", + ] + } +} + +loadable_module("crashpad_snapshot_test_module") { + testonly = true + sources = [ + "crashpad_info_client_options_test_module.cc", + ] + deps = [ + "//base", + "//third_party/crashpad/crashpad/client", + ] +} + +loadable_module("crashpad_snapshot_test_module_large") { + testonly = true + sources = [ + "crashpad_info_size_test_module.cc", + ] + defines = [ "CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE" ] + deps = [ + "//base", + ] +} + +loadable_module("crashpad_snapshot_test_module_small") { + testonly = true + sources = [ + "crashpad_info_size_test_module.cc", + ] + defines = [ "CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL" ] + deps = [ + "//base", + ] +} + +if (is_mac) { + loadable_module("crashpad_snapshot_test_module_crashy_initializer") { + testonly = true + sources = [ + "mac/mach_o_image_annotations_reader_test_module_crashy_initializer.cc", + ] + } + + executable("crashpad_snapshot_test_no_op") { + testonly = true + sources = [ + "mac/mach_o_image_annotations_reader_test_no_op.cc", + ] + } +} + +if (is_win) { + executable("crashpad_snapshot_test_annotations") { + testonly = true + sources = [ + "win/crashpad_snapshot_test_annotations.cc", + ] + deps = [ + "//base", + "//third_party/crashpad/crashpad/client", + "//third_party/crashpad/crashpad/compat", + ] + } + + executable("crashpad_snapshot_test_crashing_child") { + testonly = true + sources = [ + "win/crashpad_snapshot_test_crashing_child.cc", + ] + deps = [ + "//base", + "//third_party/crashpad/crashpad/client", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] + } + + executable("crashpad_snapshot_test_dump_without_crashing") { + testonly = true + sources = [ + "win/crashpad_snapshot_test_dump_without_crashing.cc", + ] + deps = [ + "//base", + "//third_party/crashpad/crashpad/client", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] + } + + executable("crashpad_snapshot_test_extra_memory_ranges") { + testonly = true + sources = [ + "win/crashpad_snapshot_test_extra_memory_ranges.cc", + ] + deps = [ + "//base", + "//third_party/crashpad/crashpad/client", + "//third_party/crashpad/crashpad/compat", + ] + } + + executable("crashpad_snapshot_test_image_reader") { + testonly = true + sources = [ + "win/crashpad_snapshot_test_image_reader.cc", + ] + deps = [ + "//base", + "//third_party/crashpad/crashpad/client", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] + if (symbol_level == 0) { + # The tests that use this executable rely on at least minimal debug info. + configs -= [ "//build/config/compiler:default_symbols" ] + configs += [ "//build/config/compiler:minimal_symbols" ] + } + } + + loadable_module("crashpad_snapshot_test_image_reader_module") { + testonly = true + sources = [ + "win/crashpad_snapshot_test_image_reader_module.cc", + ] + deps = [ + "//base", + "//third_party/crashpad/crashpad/client", + ] + if (symbol_level == 0) { + # The tests that use this module rely on at least minimal debug info. + configs -= [ "//build/config/compiler:default_symbols" ] + configs += [ "//build/config/compiler:minimal_symbols" ] + } + } +} diff --git a/snapshot/test/BUILD.gn b/snapshot/test/BUILD.gn new file mode 100644 index 00000000..5b9b745f --- /dev/null +++ b/snapshot/test/BUILD.gn @@ -0,0 +1,155 @@ +# 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. + +import("//testing/test.gni") + +static_library("test") { + testonly = true + + sources = [ + "errors.cc", + "errors.h", + "file.cc", + "file.h", + "filesystem.cc", + "filesystem.h", + "gtest_death_check.h", + "gtest_disabled.cc", + "gtest_disabled.h", + "hex_string.cc", + "hex_string.h", + "mac/dyld.cc", + "mac/dyld.h", + "mac/mach_errors.cc", + "mac/mach_errors.h", + "mac/mach_multiprocess.cc", + "mac/mach_multiprocess.h", + "main_arguments.cc", + "main_arguments.h", + "multiprocess.h", + "multiprocess_exec.h", + "multiprocess_exec_posix.cc", + "multiprocess_exec_win.cc", + "multiprocess_posix.cc", + "scoped_module_handle.cc", + "scoped_module_handle.h", + "scoped_temp_dir.cc", + "scoped_temp_dir.h", + "scoped_temp_dir_posix.cc", + "scoped_temp_dir_win.cc", + "test_paths.cc", + "test_paths.h", + "win/child_launcher.cc", + "win/child_launcher.h", + "win/win_child_process.cc", + "win/win_child_process.h", + "win/win_multiprocess.cc", + "win/win_multiprocess.h", + "win/win_multiprocess_with_temp_dir.cc", + "win/win_multiprocess_with_temp_dir.h", + ] + + public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + + defines = [ "CRASHPAD_IN_CHROMIUM" ] + + data = [ + "test_paths_test_data_root.txt", + ] + + deps = [ + "//base", + "//testing/gtest", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/snapshot", + "//third_party/crashpad/crashpad/util", + ] + + if (is_mac) { + libs = [ "bsm" ] + } +} + +source_set("test_test") { + testonly = true + + sources = [ + "hex_string_test.cc", + "mac/mach_multiprocess_test.cc", + "main_arguments_test.cc", + "multiprocess_exec_test.cc", + "scoped_temp_dir_test.cc", + "test_paths_test.cc", + "win/win_child_process_test.cc", + "win/win_multiprocess_test.cc", + ] + + if (!is_win) { + sources += [ "multiprocess_posix_test.cc" ] + } + + deps = [ + ":test", + "//base", + "//testing/gmock", + "//testing/gtest", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] + + data_deps = [ + ":crashpad_test_test_multiprocess_exec_test_child", + ] +} + +executable("crashpad_test_test_multiprocess_exec_test_child") { + sources = [ + "multiprocess_exec_test_child.cc", + ] +} + +static_library("gmock_main") { + testonly = true + sources = [ + "gtest_main.cc", + ] + defines = [ + "CRASHPAD_IN_CHROMIUM", + "CRASHPAD_TEST_LAUNCHER_GMOCK", + ] + deps = [ + ":test", + "//base", + "//base/test:test_support", + "//testing/gmock", + "//testing/gtest", + ] +} + +static_library("gtest_main") { + testonly = true + sources = [ + "gtest_main.cc", + ] + defines = [ + "CRASHPAD_IN_CHROMIUM", + "CRASHPAD_TEST_LAUNCHER_GTEST", + ] + deps = [ + ":test", + "//base", + "//base/test:test_support", + "//testing/gtest", + ] +} diff --git a/test/BUILD.gn b/test/BUILD.gn new file mode 100644 index 00000000..5b9b745f --- /dev/null +++ b/test/BUILD.gn @@ -0,0 +1,155 @@ +# 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. + +import("//testing/test.gni") + +static_library("test") { + testonly = true + + sources = [ + "errors.cc", + "errors.h", + "file.cc", + "file.h", + "filesystem.cc", + "filesystem.h", + "gtest_death_check.h", + "gtest_disabled.cc", + "gtest_disabled.h", + "hex_string.cc", + "hex_string.h", + "mac/dyld.cc", + "mac/dyld.h", + "mac/mach_errors.cc", + "mac/mach_errors.h", + "mac/mach_multiprocess.cc", + "mac/mach_multiprocess.h", + "main_arguments.cc", + "main_arguments.h", + "multiprocess.h", + "multiprocess_exec.h", + "multiprocess_exec_posix.cc", + "multiprocess_exec_win.cc", + "multiprocess_posix.cc", + "scoped_module_handle.cc", + "scoped_module_handle.h", + "scoped_temp_dir.cc", + "scoped_temp_dir.h", + "scoped_temp_dir_posix.cc", + "scoped_temp_dir_win.cc", + "test_paths.cc", + "test_paths.h", + "win/child_launcher.cc", + "win/child_launcher.h", + "win/win_child_process.cc", + "win/win_child_process.h", + "win/win_multiprocess.cc", + "win/win_multiprocess.h", + "win/win_multiprocess_with_temp_dir.cc", + "win/win_multiprocess_with_temp_dir.h", + ] + + public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + + defines = [ "CRASHPAD_IN_CHROMIUM" ] + + data = [ + "test_paths_test_data_root.txt", + ] + + deps = [ + "//base", + "//testing/gtest", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/snapshot", + "//third_party/crashpad/crashpad/util", + ] + + if (is_mac) { + libs = [ "bsm" ] + } +} + +source_set("test_test") { + testonly = true + + sources = [ + "hex_string_test.cc", + "mac/mach_multiprocess_test.cc", + "main_arguments_test.cc", + "multiprocess_exec_test.cc", + "scoped_temp_dir_test.cc", + "test_paths_test.cc", + "win/win_child_process_test.cc", + "win/win_multiprocess_test.cc", + ] + + if (!is_win) { + sources += [ "multiprocess_posix_test.cc" ] + } + + deps = [ + ":test", + "//base", + "//testing/gmock", + "//testing/gtest", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] + + data_deps = [ + ":crashpad_test_test_multiprocess_exec_test_child", + ] +} + +executable("crashpad_test_test_multiprocess_exec_test_child") { + sources = [ + "multiprocess_exec_test_child.cc", + ] +} + +static_library("gmock_main") { + testonly = true + sources = [ + "gtest_main.cc", + ] + defines = [ + "CRASHPAD_IN_CHROMIUM", + "CRASHPAD_TEST_LAUNCHER_GMOCK", + ] + deps = [ + ":test", + "//base", + "//base/test:test_support", + "//testing/gmock", + "//testing/gtest", + ] +} + +static_library("gtest_main") { + testonly = true + sources = [ + "gtest_main.cc", + ] + defines = [ + "CRASHPAD_IN_CHROMIUM", + "CRASHPAD_TEST_LAUNCHER_GTEST", + ] + deps = [ + ":test", + "//base", + "//base/test:test_support", + "//testing/gtest", + ] +} diff --git a/third_party/apple_cctools/BUILD.gn b/third_party/apple_cctools/BUILD.gn new file mode 100644 index 00000000..c6277281 --- /dev/null +++ b/third_party/apple_cctools/BUILD.gn @@ -0,0 +1,25 @@ +# 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. + +config("apple_cctools_config") { + include_dirs = [ "../.." ] +} + +source_set("apple_cctools") { + sources = [ + "cctools/include/mach-o/getsect.h", + "cctools/libmacho/getsecbyname.c", + ] + public_configs = [ ":apple_cctools_config" ] +} diff --git a/third_party/getopt/BUILD.gn b/third_party/getopt/BUILD.gn new file mode 100644 index 00000000..573d844b --- /dev/null +++ b/third_party/getopt/BUILD.gn @@ -0,0 +1,20 @@ +# Copyright 2014 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. + +source_set("getopt") { + sources = [ + "getopt.cc", + "getopt.h", + ] +} diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn new file mode 100644 index 00000000..db158c7d --- /dev/null +++ b/third_party/zlib/BUILD.gn @@ -0,0 +1,24 @@ +# 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. + +config("zlib_config") { + defines = [ "CRASHPAD_ZLIB_SOURCE_CHROMIUM" ] +} + +group("zlib") { + public_configs = [ ":zlib_config" ] + public_deps = [ + "//third_party/zlib:zlib", + ] +} diff --git a/tools/BUILD.gn b/tools/BUILD.gn new file mode 100644 index 00000000..e2a82d39 --- /dev/null +++ b/tools/BUILD.gn @@ -0,0 +1,161 @@ +# 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. + +source_set("tool_support") { + sources = [ + "tool_support.cc", + "tool_support.h", + ] + + public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + + deps = [ + "//base", + ] +} + +executable("crashpad_database_util") { + sources = [ + "crashpad_database_util.cc", + ] + + deps = [ + ":tool_support", + "//base", + "//build/win:default_exe_manifest", + "//third_party/crashpad/crashpad/client", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] +} + +executable("crashpad_http_upload") { + sources = [ + "crashpad_http_upload.cc", + ] + + deps = [ + ":tool_support", + "//base", + "//build/win:default_exe_manifest", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] +} + +executable("generate_dump") { + sources = [ + "generate_dump.cc", + ] + + deps = [ + ":tool_support", + "//base", + "//build/win:default_exe_manifest", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/minidump", + "//third_party/crashpad/crashpad/snapshot", + "//third_party/crashpad/crashpad/util", + ] + + if (is_mac) { + # This would be better as a config so that it could be shared with + # exception_port_tool, but configs can’t alter “inputs”. + inputs = [ + rebase_path("mac/sectaskaccess_info.plist"), + ] + ldflags = [ + "-sectcreate", + "__TEXT", + "__info_plist", + inputs[0], + ] + } + + if (is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + } +} + +if (is_mac) { + executable("catch_exception_tool") { + sources = [ + "mac/catch_exception_tool.cc", + ] + + deps = [ + ":tool_support", + "//base", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] + } + + executable("exception_port_tool") { + sources = [ + "mac/exception_port_tool.cc", + ] + + # This would be better as a config so that it could be shared with + # generate_dump, but configs can’t alter “inputs”. + inputs = [ + rebase_path("mac/sectaskaccess_info.plist"), + ] + ldflags = [ + "-sectcreate", + "__TEXT", + "__info_plist", + inputs[0], + ] + + deps = [ + ":tool_support", + "//base", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] + } + + executable("on_demand_service_tool") { + sources = [ + "mac/on_demand_service_tool.mm", + ] + + libs = [ + "CoreFoundation.framework", + "Foundation.framework", + ] + + deps = [ + ":tool_support", + "//base", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] + } + + executable("run_with_crashpad") { + sources = [ + "mac/run_with_crashpad.cc", + ] + + deps = [ + ":tool_support", + "//base", + "//third_party/crashpad/crashpad/client", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/util", + ] + } +} diff --git a/util/BUILD.gn b/util/BUILD.gn new file mode 100644 index 00000000..b61a76e3 --- /dev/null +++ b/util/BUILD.gn @@ -0,0 +1,470 @@ +# 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. + +import("//build/config/sanitizers/sanitizers.gni") +import("//build/toolchain/toolchain.gni") +import("//testing/test.gni") + +if (is_mac) { + import("//build/config/sysroot.gni") +} + +if (is_mac) { + action_foreach("mig") { + script = "mach/mig.py" + + sources = [ + "$sysroot/usr/include/mach/exc.defs", + "$sysroot/usr/include/mach/mach_exc.defs", + "$sysroot/usr/include/mach/notify.defs", + "mach/child_port.defs", + ] + + outputs = [ + "$target_gen_dir/mach/{{source_name_part}}User.c", + "$target_gen_dir/mach/{{source_name_part}}Server.c", + "$target_gen_dir/mach/{{source_name_part}}.h", + "$target_gen_dir/mach/{{source_name_part}}Server.h", + ] + + args = [ "{{source}}" ] + args += rebase_path(outputs, root_build_dir) + if (!use_system_xcode) { + args += [ + "--developer-dir", + hermetic_xcode_path, + ] + } + args += [ + "--sdk", + mac_sdk_path, + "--include", + rebase_path("../compat/mac", root_build_dir), + ] + } +} + +static_library("util") { + sources = [ + "file/delimited_file_reader.cc", + "file/delimited_file_reader.h", + "file/directory_reader.h", + "file/directory_reader_posix.cc", + "file/directory_reader_win.cc", + "file/file_io.cc", + "file/file_io.h", + "file/file_io_posix.cc", + "file/file_io_win.cc", + "file/file_reader.cc", + "file/file_reader.h", + "file/file_seeker.cc", + "file/file_seeker.h", + "file/file_writer.cc", + "file/file_writer.h", + "file/filesystem.h", + "file/filesystem_posix.cc", + "file/filesystem_win.cc", + "file/scoped_remove_file.cc", + "file/scoped_remove_file.h", + "file/string_file.cc", + "file/string_file.h", + "mac/checked_mach_address_range.h", + "mac/launchd.h", + "mac/launchd.mm", + "mac/mac_util.cc", + "mac/mac_util.h", + "mac/service_management.cc", + "mac/service_management.h", + "mac/xattr.cc", + "mac/xattr.h", + "misc/address_sanitizer.h", + "misc/address_types.h", + "misc/arraysize_unsafe.h", + "misc/as_underlying_type.h", + "misc/clock.h", + "misc/clock_mac.cc", + "misc/clock_posix.cc", + "misc/clock_win.cc", + "misc/from_pointer_cast.h", + "misc/implicit_cast.h", + "misc/initialization_state.h", + "misc/initialization_state_dcheck.cc", + "misc/initialization_state_dcheck.h", + "misc/lexing.cc", + "misc/lexing.h", + "misc/metrics.cc", + "misc/metrics.h", + "misc/paths.h", + "misc/paths_mac.cc", + "misc/paths_win.cc", + "misc/pdb_structures.cc", + "misc/pdb_structures.h", + "misc/random_string.cc", + "misc/random_string.h", + "misc/reinterpret_bytes.cc", + "misc/reinterpret_bytes.h", + "misc/scoped_forbid_return.cc", + "misc/scoped_forbid_return.h", + "misc/symbolic_constants_common.h", + "misc/time.cc", + "misc/time.h", + "misc/time_win.cc", + "misc/tri_state.h", + "misc/uuid.cc", + "misc/uuid.h", + "misc/zlib.cc", + "misc/zlib.h", + "net/http_body.cc", + "net/http_body.h", + "net/http_body_gzip.cc", + "net/http_body_gzip.h", + "net/http_headers.h", + "net/http_multipart_builder.cc", + "net/http_multipart_builder.h", + "net/http_transport.cc", + "net/http_transport.h", + "net/http_transport_mac.mm", + "net/http_transport_win.cc", + "net/url.cc", + "net/url.h", + "numeric/checked_address_range.cc", + "numeric/checked_address_range.h", + "numeric/checked_range.h", + "numeric/checked_vm_address_range.h", + "numeric/in_range_cast.h", + "numeric/int128.h", + "numeric/safe_assignment.h", + "posix/close_multiple.cc", + "posix/close_multiple.h", + "posix/close_stdio.cc", + "posix/close_stdio.h", + "posix/drop_privileges.cc", + "posix/drop_privileges.h", + "posix/process_info.h", + "posix/process_info_mac.cc", + "posix/scoped_dir.cc", + "posix/scoped_dir.h", + "posix/scoped_mmap.cc", + "posix/scoped_mmap.h", + "posix/signals.cc", + "posix/signals.h", + "posix/symbolic_constants_posix.cc", + "posix/symbolic_constants_posix.h", + "stdlib/aligned_allocator.cc", + "stdlib/aligned_allocator.h", + "stdlib/map_insert.h", + "stdlib/objc.h", + "stdlib/string_number_conversion.cc", + "stdlib/string_number_conversion.h", + "stdlib/strlcpy.cc", + "stdlib/strlcpy.h", + "stdlib/strnlen.cc", + "stdlib/strnlen.h", + "stdlib/thread_safe_vector.h", + "string/split_string.cc", + "string/split_string.h", + "synchronization/semaphore.h", + "synchronization/semaphore_mac.cc", + "synchronization/semaphore_posix.cc", + "synchronization/semaphore_win.cc", + "thread/thread.cc", + "thread/thread.h", + "thread/thread_log_messages.cc", + "thread/thread_log_messages.h", + "thread/thread_posix.cc", + "thread/thread_win.cc", + "thread/worker_thread.cc", + "thread/worker_thread.h", + "win/address_types.h", + "win/capture_context.h", + "win/checked_win_address_range.h", + "win/command_line.cc", + "win/command_line.h", + "win/critical_section_with_debug_info.cc", + "win/critical_section_with_debug_info.h", + "win/exception_handler_server.cc", + "win/exception_handler_server.h", + "win/get_function.cc", + "win/get_function.h", + "win/get_module_information.cc", + "win/get_module_information.h", + "win/handle.cc", + "win/handle.h", + "win/initial_client_data.cc", + "win/initial_client_data.h", + "win/module_version.cc", + "win/module_version.h", + "win/nt_internals.cc", + "win/nt_internals.h", + "win/ntstatus_logging.cc", + "win/ntstatus_logging.h", + "win/process_info.cc", + "win/process_info.h", + "win/process_structs.h", + "win/registration_protocol_win.cc", + "win/registration_protocol_win.h", + "win/safe_terminate_process.h", + "win/scoped_handle.cc", + "win/scoped_handle.h", + "win/scoped_local_alloc.cc", + "win/scoped_local_alloc.h", + "win/scoped_process_suspend.cc", + "win/scoped_process_suspend.h", + "win/scoped_set_event.cc", + "win/scoped_set_event.h", + "win/session_end_watcher.cc", + "win/session_end_watcher.h", + "win/termination_codes.h", + "win/xp_compat.h", + ] + + if (is_mac) { + # mach/ are not globally filtered. + sources += [ + "mach/child_port_handshake.cc", + "mach/child_port_handshake.h", + "mach/child_port_server.cc", + "mach/child_port_server.h", + "mach/child_port_types.h", + "mach/composite_mach_message_server.cc", + "mach/composite_mach_message_server.h", + "mach/exc_client_variants.cc", + "mach/exc_client_variants.h", + "mach/exc_server_variants.cc", + "mach/exc_server_variants.h", + "mach/exception_behaviors.cc", + "mach/exception_behaviors.h", + "mach/exception_ports.cc", + "mach/exception_ports.h", + "mach/exception_types.cc", + "mach/exception_types.h", + "mach/mach_extensions.cc", + "mach/mach_extensions.h", + "mach/mach_message.cc", + "mach/mach_message.h", + "mach/mach_message_server.cc", + "mach/mach_message_server.h", + "mach/notify_server.cc", + "mach/notify_server.h", + "mach/scoped_task_suspend.cc", + "mach/scoped_task_suspend.h", + "mach/symbolic_constants_mach.cc", + "mach/symbolic_constants_mach.h", + "mach/task_for_pid.cc", + "mach/task_for_pid.h", + "mach/task_memory.cc", + "mach/task_memory.h", + ] + } + + if (is_mac) { + sources += get_target_outputs(":mig") + } + + if (is_win) { + # There's no ml.exe yet in cross builds, so provide broken-but-not-asm + # versions of the functions defined in .asm files. + # CaptureContext() in capture_context_broken.cc just calls CHECK(false). + # SafeTerminateProcess() in safe_terminate_process.cc just calls regular + # TerminateProcess() without the protection against broken third-party + # patching of TerminateProcess(). + # TODO(thakis): Use the .asm file in cross builds somehow, crbug.com/762167 + if (host_os == "win") { + sources += [ + "win/capture_context.asm", + "win/safe_terminate_process.asm", + ] + } else { + sources += [ + "win/capture_context_broken.cc", + "win/safe_terminate_process_broken.cc", + ] + } + } + + public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + + # Include files from here and generated files starting with "util". + include_dirs = [ "$root_gen_dir/third_party/crashpad/crashpad" ] + + public_deps = [ + "//third_party/crashpad/crashpad/compat", + ] + + deps = [ + "//base", + "//third_party/crashpad/crashpad/third_party/zlib", + ] + + if (is_mac) { + libs = [ + "bsm", + "CoreFoundation.framework", + "Foundation.framework", + "IOKit.framework", + ] + deps += [ ":mig" ] + } + + if (is_win) { + cflags = [ + "/wd4201", # nonstandard extension used : nameless struct/union. + "/wd4577", # 'noexcept' used with no exception handling mode specified. + ] + libs = [ "winhttp.lib" ] + + if (current_cpu == "x86") { + asmflags = [ "/safeseh" ] + } + } +} + +source_set("util_test") { + testonly = true + + sources = [ + "file/delimited_file_reader_test.cc", + "file/directory_reader_test.cc", + "file/file_io_test.cc", + "file/file_reader_test.cc", + "file/filesystem_test.cc", + "file/string_file_test.cc", + "mac/launchd_test.mm", + "mac/mac_util_test.mm", + "mac/service_management_test.mm", + "mac/xattr_test.cc", + "misc/arraysize_unsafe_test.cc", + "misc/clock_test.cc", + "misc/from_pointer_cast_test.cc", + "misc/initialization_state_dcheck_test.cc", + "misc/initialization_state_test.cc", + "misc/paths_test.cc", + "misc/random_string_test.cc", + "misc/reinterpret_bytes_test.cc", + "misc/scoped_forbid_return_test.cc", + "misc/time_test.cc", + "misc/uuid_test.cc", + "net/http_body_gzip_test.cc", + "net/http_body_test.cc", + "net/http_body_test_util.cc", + "net/http_body_test_util.h", + "net/http_multipart_builder_test.cc", + "net/http_transport_test.cc", + "net/url_test.cc", + "numeric/checked_address_range_test.cc", + "numeric/checked_range_test.cc", + "numeric/in_range_cast_test.cc", + "numeric/int128_test.cc", + "posix/process_info_test.cc", + "posix/scoped_mmap_test.cc", + "posix/signals_test.cc", + "posix/symbolic_constants_posix_test.cc", + "stdlib/aligned_allocator_test.cc", + "stdlib/map_insert_test.cc", + "stdlib/string_number_conversion_test.cc", + "stdlib/strlcpy_test.cc", + "stdlib/strnlen_test.cc", + "stdlib/thread_safe_vector_test.cc", + "string/split_string_test.cc", + "synchronization/semaphore_test.cc", + "thread/thread_log_messages_test.cc", + "thread/thread_test.cc", + "thread/worker_thread_test.cc", + "win/capture_context_test.cc", + "win/command_line_test.cc", + "win/critical_section_with_debug_info_test.cc", + "win/exception_handler_server_test.cc", + "win/get_function_test.cc", + "win/handle_test.cc", + "win/initial_client_data_test.cc", + "win/process_info_test.cc", + "win/registration_protocol_win_test.cc", + "win/safe_terminate_process_test.cc", + "win/scoped_process_suspend_test.cc", + "win/session_end_watcher_test.cc", + ] + + if (is_mac) { + # mach/ are not globally filtered. + sources += [ + "mach/child_port_handshake_test.cc", + "mach/child_port_server_test.cc", + "mach/composite_mach_message_server_test.cc", + "mach/exc_client_variants_test.cc", + "mach/exc_server_variants_test.cc", + "mach/exception_behaviors_test.cc", + "mach/exception_ports_test.cc", + "mach/exception_types_test.cc", + "mach/mach_extensions_test.cc", + "mach/mach_message_server_test.cc", + "mach/mach_message_test.cc", + "mach/notify_server_test.cc", + "mach/scoped_task_suspend_test.cc", + "mach/symbolic_constants_mach_test.cc", + "mach/task_memory_test.cc", + ] + } + + data = [ + "net/http_transport_test_server.py", + "net/testdata/", + ] + + deps = [ + ":util", + "//base", + "//testing/gmock", + "//testing/gtest", + "//third_party/crashpad/crashpad/client", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/test", + "//third_party/crashpad/crashpad/third_party/zlib", + ] + + if (is_mac) { + libs = [ "Foundation.framework" ] + } + + if (is_win) { + libs = [ "rpcrt4.lib" ] + data_deps = [ + ":crashpad_util_test_process_info_test_child", + ":crashpad_util_test_safe_terminate_process_test_child", + ] + } +} + +if (is_win) { + executable("crashpad_util_test_process_info_test_child") { + testonly = true + sources = [ + "win/process_info_test_child.cc", + ] + + # Set an unusually high load address to make sure that the main executable + # still appears as the first element in ProcessInfo::Modules(). + ldflags = [ + "/BASE:0x78000000", + "/DYNAMICBASE:NO", + "/FIXED", + ] + } + + executable("crashpad_util_test_safe_terminate_process_test_child") { + testonly = true + sources = [ + "win/safe_terminate_process_test_child.cc", + ] + } +} From 94a5a72efaf29e431436ba39996a5e62d3be4c17 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Mon, 20 Nov 2017 13:32:26 -0500 Subject: [PATCH 026/326] =?UTF-8?q?mac:=20Tests=20that=20crash=20intention?= =?UTF-8?q?ally=20shouldn=E2=80=99t=20go=20to=20ReportCrash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Crashpad has many tests that crash intentionally. Some of these are gtest death tests, and others arrange for intentional crashes to test Crashpad’s own crash-catching logic. On macOS, all of the gtest death tests and some of the other intentional crashes were being logged by ReportCrash, the system’s crash reporter. Since these reports corresponded to intentional crashes, they were never useful, and served only to clutter ~/Library/Logs/DiagnosticReports. Since Crashpad is adept at handling exceptions on its own, this introduces the “exception swallowing server”, crashpad_exception_swallower, which is a Mach exception server that implements a no-op exception handler routine for all exceptions received. The exception swallowing server is established as the task handler for EXC_CRASH and EXC_CORPSE_NOTIFY exceptions during gtest death tests invoked by {ASSERT,EXPECT}_DEATH_{CHECK,CRASH}, and for all child processes invoked by the Multiprocess test infrastructure. The exception swallowing server is not in effect at other times, so unexpected crashes in test code can still be handled by ReportCrash or another crash reporter. With this change in place, no new reports are generated in the user-level ~/Library/Logs/DiagnosticReports or the system’s /Library/Logs/DiagnosticReports during a run of Crashpad’s full test suite on macOS. Bug: crashpad:33 Change-Id: I13891853a7e25accc30da21fa7ea8bd7d1f3bd2f Reviewed-on: https://chromium-review.googlesource.com/777859 Commit-Queue: Mark Mentovai Reviewed-by: Robert Sesek --- client/annotation.h | 4 +- client/annotation_test.cc | 11 +- client/crashpad_client_mac.cc | 104 +------- client/simple_address_range_bag_test.cc | 2 +- client/simple_string_dictionary_test.cc | 2 +- minidump/minidump_exception_writer_test.cc | 2 +- minidump/minidump_file_writer_test.cc | 2 +- minidump/minidump_module_writer_test.cc | 2 +- minidump/minidump_system_info_writer_test.cc | 2 +- minidump/minidump_thread_writer_test.cc | 2 +- .../mach_o_image_annotations_reader_test.cc | 53 ++-- test/BUILD.gn | 23 +- test/gtest_death.h | 115 +++++++++ test/gtest_death_check.h | 55 ----- test/mac/exception_swallower.cc | 155 ++++++++++++ test/mac/exception_swallower.h | 157 ++++++++++++ test/mac/exception_swallower_exe.cc | 229 ++++++++++++++++++ test/multiprocess.h | 4 + test/multiprocess_posix.cc | 27 ++- test/multiprocess_posix_test.cc | 2 +- test/test.gyp | 30 ++- util/BUILD.gn | 2 + .../composite_mach_message_server_test.cc | 2 +- util/mach/exc_server_variants_test.cc | 6 +- util/mach/exception_ports_test.cc | 9 +- util/misc/from_pointer_cast_test.cc | 35 +-- util/misc/initialization_state_dcheck_test.cc | 2 +- util/misc/scoped_forbid_return_test.cc | 2 +- util/net/http_multipart_builder_test.cc | 2 +- util/posix/double_fork_and_exec.cc | 146 +++++++++++ util/posix/double_fork_and_exec.h | 67 +++++ util/posix/scoped_mmap_test.cc | 19 +- util/stdlib/aligned_allocator_test.cc | 3 +- util/util.gyp | 2 + 34 files changed, 1053 insertions(+), 227 deletions(-) create mode 100644 test/gtest_death.h delete mode 100644 test/gtest_death_check.h create mode 100644 test/mac/exception_swallower.cc create mode 100644 test/mac/exception_swallower.h create mode 100644 test/mac/exception_swallower_exe.cc create mode 100644 util/posix/double_fork_and_exec.cc create mode 100644 util/posix/double_fork_and_exec.h diff --git a/client/annotation.h b/client/annotation.h index 34d0d566..35bdced4 100644 --- a/client/annotation.h +++ b/client/annotation.h @@ -213,13 +213,13 @@ class StringAnnotation : public Annotation { //! \brief Sets the Annotation's string value. //! - //! \param[in] value The string value. + //! \param[in] string The string value. void Set(base::StringPiece string) { Annotation::ValueSizeType size = std::min(MaxSize, base::saturated_cast(string.size())); memcpy(value_, string.data(), size); // Check for no embedded `NUL` characters. - DCHECK(!memchr(value_, '\0', size)); + DCHECK(!memchr(value_, '\0', size)) << "embedded NUL"; SetSize(size); } diff --git a/client/annotation_test.cc b/client/annotation_test.cc index 083b309b..656ade0c 100644 --- a/client/annotation_test.cc +++ b/client/annotation_test.cc @@ -19,7 +19,7 @@ #include "client/annotation_list.h" #include "client/crashpad_info.h" #include "gtest/gtest.h" -#include "test/gtest_death_check.h" +#include "test/gtest_death.h" namespace crashpad { namespace test { @@ -105,12 +105,17 @@ TEST_F(Annotation, StringType) { EXPECT_EQ(5u, annotation.size()); EXPECT_EQ("loooo", annotation.value()); +} #if DCHECK_IS_ON() - EXPECT_DEATH_CHECK(annotation.Set(std::string("te\0st", 5)), ""); -#endif + +TEST(AnnotationDeathTest, EmbeddedNUL) { + crashpad::StringAnnotation<5> annotation("name"); + EXPECT_DEATH_CHECK(annotation.Set(std::string("te\0st", 5)), "embedded NUL"); } +#endif + } // namespace } // namespace test } // namespace crashpad diff --git a/client/crashpad_client_mac.cc b/client/crashpad_client_mac.cc index a9e4aca4..a16f8d28 100644 --- a/client/crashpad_client_mac.cc +++ b/client/crashpad_client_mac.cc @@ -18,15 +18,12 @@ #include #include #include -#include -#include #include #include #include "base/logging.h" #include "base/mac/mach_logging.h" -#include "base/posix/eintr_wrapper.h" #include "base/strings/stringprintf.h" #include "util/mac/mac_util.h" #include "util/mach/child_port_handshake.h" @@ -36,7 +33,7 @@ #include "util/mach/notify_server.h" #include "util/misc/clock.h" #include "util/misc/implicit_cast.h" -#include "util/posix/close_multiple.h" +#include "util/posix/double_fork_and_exec.h" namespace crashpad { @@ -305,9 +302,6 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { handler_restarter->last_start_time_ = ClockMonotonicNanoseconds(); } - // Set up the arguments for execve() first. These aren’t needed until - // execve() is called, but it’s dangerous to do this in a child process - // after fork(). ChildPortHandshake child_port_handshake; base::ScopedFD server_write_fd = child_port_handshake.ServerWriteFD(); @@ -337,8 +331,6 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { } argv.push_back(FormatArgumentInt("handshake-fd", server_write_fd.get())); - const char* handler_c = handler.value().c_str(); - // argv_c contains const char* pointers and is terminated by nullptr. argv // is required because the pointers in argv_c need to point somewhere, and // they can’t point to temporaries such as those returned by @@ -350,97 +342,23 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { } argv_c.push_back(nullptr); - // Double-fork(). The three processes involved are parent, child, and - // grandchild. The grandchild will become the handler process. The child - // exits immediately after spawning the grandchild, so the grandchild - // becomes an orphan and its parent process ID becomes 1. This relieves the - // parent and child of the responsibility for reaping the grandchild with - // waitpid() or similar. The handler process is expected to outlive the - // parent process, so the parent shouldn’t be concerned with reaping it. - // This approach means that accidental early termination of the handler - // process will not result in a zombie process. - pid_t pid = fork(); - if (pid < 0) { - PLOG(ERROR) << "fork"; + // When restarting, reset the system default crash handler first. Otherwise, + // the crash exception port in the handler will have been inherited from + // this parent process, which was probably using the exception server now + // being restarted. The handler can’t monitor itself for its own crashes via + // this interface. + if (!DoubleForkAndExec( + argv, + server_write_fd.get(), + true, + restart ? CrashpadClient::UseSystemDefaultHandler : nullptr)) { return false; } - if (pid == 0) { - // Child process. - - if (restart) { - // When restarting, reset the system default crash handler first. - // Otherwise, the crash exception port here will have been inherited - // from the parent process, which was probably using the exception - // server now being restarted. The handler can’t monitor itself for its - // own crashes via this interface. - CrashpadClient::UseSystemDefaultHandler(); - } - - // Call setsid(), creating a new process group and a new session, both led - // by this process. The new process group has no controlling terminal. - // This disconnects it from signals generated by the parent process’ - // terminal. - // - // setsid() is done in the child instead of the grandchild so that the - // grandchild will not be a session leader. If it were a session leader, - // an accidental open() of a terminal device without O_NOCTTY would make - // that terminal the controlling terminal. - // - // It’s not desirable for the handler to have a controlling terminal. The - // handler monitors clients on its own and manages its own lifetime, - // exiting when it loses all clients and when it deems it appropraite to - // do so. It may serve clients in different process groups or sessions - // than its original client, and receiving signals intended for its - // original client’s process group could be harmful in that case. - PCHECK(setsid() != -1) << "setsid"; - - pid = fork(); - if (pid < 0) { - PLOG(FATAL) << "fork"; - } - - if (pid > 0) { - // Child process. - - // _exit() instead of exit(), because fork() was called. - _exit(EXIT_SUCCESS); - } - - // Grandchild process. - - CloseMultipleNowOrOnExec(STDERR_FILENO + 1, server_write_fd.get()); - - // &argv_c[0] is a pointer to a pointer to const char data, but because of - // how C (not C++) works, execvp() wants a pointer to a const pointer to - // char data. It modifies neither the data nor the pointers, so the - // const_cast is safe. - execvp(handler_c, const_cast(&argv_c[0])); - PLOG(FATAL) << "execvp " << handler_c; - } - - // Parent process. - // Close the write side of the pipe, so that the handler process is the only // process that can write to it. server_write_fd.reset(); - // waitpid() for the child, so that it does not become a zombie process. The - // child normally exits quickly. - int status; - pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0)); - PCHECK(wait_pid != -1) << "waitpid"; - DCHECK_EQ(wait_pid, pid); - - if (WIFSIGNALED(status)) { - LOG(WARNING) << "intermediate process: signal " << WTERMSIG(status); - } else if (!WIFEXITED(status)) { - DLOG(WARNING) << "intermediate process: unknown termination " << status; - } else if (WEXITSTATUS(status) != EXIT_SUCCESS) { - LOG(WARNING) << "intermediate process: exit status " - << WEXITSTATUS(status); - } - // Rendezvous with the handler running in the grandchild process. if (!child_port_handshake.RunClient(receive_right.get(), MACH_MSG_TYPE_MOVE_RECEIVE)) { diff --git a/client/simple_address_range_bag_test.cc b/client/simple_address_range_bag_test.cc index ac81a1f4..e9a6d9f3 100644 --- a/client/simple_address_range_bag_test.cc +++ b/client/simple_address_range_bag_test.cc @@ -16,7 +16,7 @@ #include "base/logging.h" #include "gtest/gtest.h" -#include "test/gtest_death_check.h" +#include "test/gtest_death.h" namespace crashpad { namespace test { diff --git a/client/simple_string_dictionary_test.cc b/client/simple_string_dictionary_test.cc index 6944934f..6f85e854 100644 --- a/client/simple_string_dictionary_test.cc +++ b/client/simple_string_dictionary_test.cc @@ -16,7 +16,7 @@ #include "base/logging.h" #include "gtest/gtest.h" -#include "test/gtest_death_check.h" +#include "test/gtest_death.h" namespace crashpad { namespace test { diff --git a/minidump/minidump_exception_writer_test.cc b/minidump/minidump_exception_writer_test.cc index a748641b..e4dc5faa 100644 --- a/minidump/minidump_exception_writer_test.cc +++ b/minidump/minidump_exception_writer_test.cc @@ -27,7 +27,7 @@ #include "minidump/test/minidump_writable_test_util.h" #include "snapshot/test/test_cpu_context.h" #include "snapshot/test/test_exception_snapshot.h" -#include "test/gtest_death_check.h" +#include "test/gtest_death.h" #include "util/file/string_file.h" namespace crashpad { diff --git a/minidump/minidump_file_writer_test.cc b/minidump/minidump_file_writer_test.cc index 00f7a7bf..730da26b 100644 --- a/minidump/minidump_file_writer_test.cc +++ b/minidump/minidump_file_writer_test.cc @@ -34,7 +34,7 @@ #include "snapshot/test/test_process_snapshot.h" #include "snapshot/test/test_system_snapshot.h" #include "snapshot/test/test_thread_snapshot.h" -#include "test/gtest_death_check.h" +#include "test/gtest_death.h" #include "util/file/string_file.h" namespace crashpad { diff --git a/minidump/minidump_module_writer_test.cc b/minidump/minidump_module_writer_test.cc index f6a278e9..9a2739b5 100644 --- a/minidump/minidump_module_writer_test.cc +++ b/minidump/minidump_module_writer_test.cc @@ -28,7 +28,7 @@ #include "minidump/test/minidump_string_writer_test_util.h" #include "minidump/test/minidump_writable_test_util.h" #include "snapshot/test/test_module_snapshot.h" -#include "test/gtest_death_check.h" +#include "test/gtest_death.h" #include "util/file/string_file.h" #include "util/misc/implicit_cast.h" #include "util/misc/uuid.h" diff --git a/minidump/minidump_system_info_writer_test.cc b/minidump/minidump_system_info_writer_test.cc index 645f39ed..c3d02472 100644 --- a/minidump/minidump_system_info_writer_test.cc +++ b/minidump/minidump_system_info_writer_test.cc @@ -27,7 +27,7 @@ #include "minidump/test/minidump_string_writer_test_util.h" #include "minidump/test/minidump_writable_test_util.h" #include "snapshot/test/test_system_snapshot.h" -#include "test/gtest_death_check.h" +#include "test/gtest_death.h" #include "util/file/string_file.h" namespace crashpad { diff --git a/minidump/minidump_thread_writer_test.cc b/minidump/minidump_thread_writer_test.cc index 60173da2..d9e85aa6 100644 --- a/minidump/minidump_thread_writer_test.cc +++ b/minidump/minidump_thread_writer_test.cc @@ -31,7 +31,7 @@ #include "snapshot/test/test_cpu_context.h" #include "snapshot/test/test_memory_snapshot.h" #include "snapshot/test/test_thread_snapshot.h" -#include "test/gtest_death_check.h" +#include "test/gtest_death.h" #include "util/file/string_file.h" namespace crashpad { diff --git a/snapshot/mac/mach_o_image_annotations_reader_test.cc b/snapshot/mac/mach_o_image_annotations_reader_test.cc index 3e7864b0..f486c1d5 100644 --- a/snapshot/mac/mach_o_image_annotations_reader_test.cc +++ b/snapshot/mac/mach_o_image_annotations_reader_test.cc @@ -101,6 +101,32 @@ class TestMachOImageAnnotationsReader final : MachMultiprocess(), UniversalMachExcServer::Interface(), test_type_(test_type) { + switch (test_type_) { + case kDontCrash: + // SetExpectedChildTermination(kTerminationNormal, EXIT_SUCCESS) is the + // default. + break; + + case kCrashAbort: + SetExpectedChildTermination(kTerminationSignal, SIGABRT); + break; + + case kCrashModuleInitialization: + // This crash is triggered by __builtin_trap(), which shows up as + // SIGILL. + SetExpectedChildTermination(kTerminationSignal, SIGILL); + break; + + case kCrashDyld: + // Prior to 10.12, dyld fatal errors result in the execution of an + // int3 instruction on x86 and a trap instruction on ARM, both of + // which raise SIGTRAP. 10.9.5 dyld-239.4/src/dyldStartup.s + // _dyld_fatal_error. This changed in 10.12 to use + // abort_with_payload(), which appears as SIGABRT to a waiting parent. + SetExpectedChildTermination( + kTerminationSignal, MacOSXMinorVersion() < 12 ? SIGTRAP : SIGABRT); + break; + } } ~TestMachOImageAnnotationsReader() {} @@ -322,33 +348,6 @@ class TestMachOImageAnnotationsReader final kMachMessageTimeoutWaitIndefinitely); EXPECT_EQ(mr, MACH_MSG_SUCCESS) << MachErrorMessage(mr, "MachMessageServer::Run"); - - switch (test_type_) { - case kCrashAbort: - SetExpectedChildTermination(kTerminationSignal, SIGABRT); - break; - - case kCrashModuleInitialization: - // This crash is triggered by __builtin_trap(), which shows up as - // SIGILL. - SetExpectedChildTermination(kTerminationSignal, SIGILL); - break; - - case kCrashDyld: - // Prior to 10.12, dyld fatal errors result in the execution of an - // int3 instruction on x86 and a trap instruction on ARM, both of - // which raise SIGTRAP. 10.9.5 dyld-239.4/src/dyldStartup.s - // _dyld_fatal_error. This changed in 10.12 to use - // abort_with_payload(), which appears as SIGABRT to a waiting parent. - SetExpectedChildTermination( - kTerminationSignal, - MacOSXMinorVersion() < 12 ? SIGTRAP : SIGABRT); - break; - - default: - FAIL(); - break; - } } } diff --git a/test/BUILD.gn b/test/BUILD.gn index 5b9b745f..9337fa99 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -24,13 +24,15 @@ static_library("test") { "file.h", "filesystem.cc", "filesystem.h", - "gtest_death_check.h", + "gtest_death.h", "gtest_disabled.cc", "gtest_disabled.h", "hex_string.cc", "hex_string.h", "mac/dyld.cc", "mac/dyld.h", + "mac/exception_swallower.cc", + "mac/exception_swallower.h", "mac/mach_errors.cc", "mac/mach_errors.h", "mac/mach_multiprocess.cc", @@ -78,6 +80,9 @@ static_library("test") { if (is_mac) { libs = [ "bsm" ] + data_deps = [ + ":crashpad_exception_swallower", + ] } } @@ -153,3 +158,19 @@ static_library("gtest_main") { "//testing/gtest", ] } + +if (is_mac) { + executable("crashpad_exception_swallower") { + testonly = true + sources = [ + "mac/exception_swallower_exe.cc", + ] + deps = [ + "//base", + "//third_party/crashpad/crashpad/compat", + "//third_party/crashpad/crashpad/handler", + "//third_party/crashpad/crashpad/tools:tool_support", + "//third_party/crashpad/crashpad/util", + ] + } +} diff --git a/test/gtest_death.h b/test/gtest_death.h new file mode 100644 index 00000000..1c7e5f6a --- /dev/null +++ b/test/gtest_death.h @@ -0,0 +1,115 @@ +// 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_TEST_GTEST_DEATH_H_ +#define CRASHPAD_TEST_GTEST_DEATH_H_ + +#include "base/logging.h" +#include "build/build_config.h" +#include "gtest/gtest.h" + +#if defined(OS_MACOSX) +#include "test/mac/exception_swallower.h" +#endif + +//! \file + +#if defined(OS_MACOSX) || DOXYGEN + +//! \brief Wraps the gtest `ASSERT_DEATH()` macro to make assertions about death +//! caused by crashes. +//! +//! On macOS, this macro prevents the system’s crash reporter from handling +//! crashes that occur in \a statement. Crashes are normally visible to the +//! system’s crash reporter, but it is undesirable for intentional +//! ASSERT_DEATH_CRASH() crashes to be handled by any crash reporter. +//! +//! \sa ASSERT_DEATH_CHECK() +//! \sa EXPECT_DEATH_CRASH() +#define ASSERT_DEATH_CRASH(statement, regex) \ + crashpad::test::ExceptionSwallower::Parent_PrepareForGtestDeathTest(); \ + ASSERT_DEATH(crashpad::test::ExceptionSwallower::Child_SwallowExceptions(); \ + { statement; }, regex) + +//! \brief Wraps the gtest `EXPECT_DEATH()` macro to make assertions about death +//! caused by crashes. +//! +//! On macOS, this macro prevents the system’s crash reporter from handling +//! crashes that occur in \a statement. Crashes are normally visible to the +//! system’s crash reporter, but it is undesirable for intentional +//! EXPECT_DEATH_CRASH() crashes to be handled by any crash reporter. +//! +//! \sa EXPECT_DEATH_CHECK() +//! \sa ASSERT_DEATH_CRASH() +#define EXPECT_DEATH_CRASH(statement, regex) \ + crashpad::test::ExceptionSwallower::Parent_PrepareForGtestDeathTest(); \ + EXPECT_DEATH(crashpad::test::ExceptionSwallower::Child_SwallowExceptions(); \ + { statement; }, regex) + +#else // OS_MACOSX + +#define ASSERT_DEATH_CRASH(statement, regex) ASSERT_DEATH(statement, regex) +#define EXPECT_DEATH_CRASH(statement, regex) EXPECT_DEATH(statement, regex) + +#endif // OS_MACOSX + +#if !(!defined(MINI_CHROMIUM_BASE_LOGGING_H_) && \ + defined(OFFICIAL_BUILD) && \ + defined(NDEBUG)) || \ + DOXYGEN + +//! \brief Wraps the ASSERT_DEATH_CRASH() macro to make assertions about death +//! caused by `CHECK()` failures. +//! +//! In an in-Chromium build in the official configuration, `CHECK()` does not +//! print its condition or streamed messages. In that case, this macro uses an +//! empty \a regex pattern when calling ASSERT_DEATH_CRASH() to avoid looking +//! for any particular output on the standard error stream. In other build +//! configurations, the \a regex pattern is left intact. +//! +//! `CHECK()` failures normally show up as crashes to the system’s crash +//! reporter, but it is undesirable for intentional ASSERT_DEATH_CHECK() crashes +//! to be handled by any crash reporter, so this is implemented in terms of +//! ASSERT_DEATH_CRASH() instead of `ASSERT_DEATH()`. +//! +//! \sa EXPECT_DEATH_CHECK() +#define ASSERT_DEATH_CHECK(statement, regex) \ + ASSERT_DEATH_CRASH(statement, regex) + +//! \brief Wraps the EXPECT_DEATH_CRASH() macro to make assertions about death +//! caused by `CHECK()` failures. +//! +//! In an in-Chromium build in the official configuration, `CHECK()` does not +//! print its condition or streamed messages. In that case, this macro uses an +//! empty \a regex pattern when calling EXPECT_DEATH_CRASH() to avoid looking +//! for any particular output on the standard error stream. In other build +//! configurations, the \a regex pattern is left intact. +//! +//! `CHECK()` failures normally show up as crashes to the system’s crash +//! reporter, but it is undesirable for intentional EXPECT_DEATH_CHECK() crashes +//! to be handled by any crash reporter, so this is implemented in terms of +//! EXPECT_DEATH_CRASH() instead of `EXPECT_DEATH()`. +//! +//! \sa ASSERT_DEATH_CHECK() +#define EXPECT_DEATH_CHECK(statement, regex) \ + EXPECT_DEATH_CRASH(statement, regex) + +#else // !(!MINI_CHROMIUM_BASE_LOGGING_H_ && OFFICIAL_BUILD && NDEBUG) + +#define ASSERT_DEATH_CHECK(statement, regex) ASSERT_DEATH_CRASH(statement, "") +#define EXPECT_DEATH_CHECK(statement, regex) EXPECT_DEATH_CRASH(statement, "") + +#endif // !(!MINI_CHROMIUM_BASE_LOGGING_H_ && OFFICIAL_BUILD && NDEBUG) + +#endif // CRASHPAD_TEST_GTEST_DEATH_H_ diff --git a/test/gtest_death_check.h b/test/gtest_death_check.h deleted file mode 100644 index 8af50aac..00000000 --- a/test/gtest_death_check.h +++ /dev/null @@ -1,55 +0,0 @@ -// 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_TEST_GTEST_DEATH_CHECK_H_ -#define CRASHPAD_TEST_GTEST_DEATH_CHECK_H_ - -#include "base/logging.h" -#include "gtest/gtest.h" - -//! \file - -#if !(!defined(MINI_CHROMIUM_BASE_LOGGING_H_) && \ - defined(OFFICIAL_BUILD) && \ - defined(NDEBUG)) || \ - DOXYGEN - -//! \brief Wraps the gtest `ASSERT_DEATH()` macro to make assertions about death -//! caused by `CHECK()` failures. -//! -//! In an in-Chromium build in the official configuration in Release mode, -//! `CHECK()` does not print its condition or streamed messages. In that case, -//! this macro uses an empty \a regex pattern when calling `ASSERT_DEATH()` to -//! avoid looking for any particular output on the standard error stream. In -//! other build configurations, the \a regex pattern is left intact. -#define ASSERT_DEATH_CHECK(statement, regex) ASSERT_DEATH(statement, regex) - -//! \brief Wraps the gtest `EXPECT_DEATH()` macro to make assertions about death -//! caused by `CHECK()` failures. -//! -//! In an in-Chromium build in the official configuration in Release mode, -//! `CHECK()` does not print its condition or streamed messages. In that case, -//! this macro uses an empty \a regex pattern when calling `EXPECT_DEATH()` to -//! avoid looking for any particular output on the standard error stream. In -//! other build configurations, the \a regex pattern is left intact. -#define EXPECT_DEATH_CHECK(statement, regex) EXPECT_DEATH(statement, regex) - -#else - -#define ASSERT_DEATH_CHECK(statement, regex) ASSERT_DEATH(statement, "") -#define EXPECT_DEATH_CHECK(statement, regex) EXPECT_DEATH(statement, "") - -#endif - -#endif // CRASHPAD_TEST_GTEST_DEATH_CHECK_H_ diff --git a/test/mac/exception_swallower.cc b/test/mac/exception_swallower.cc new file mode 100644 index 00000000..307680c0 --- /dev/null +++ b/test/mac/exception_swallower.cc @@ -0,0 +1,155 @@ +// 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 "test/mac/exception_swallower.h" + +#include +#include + +#include +#include + +#include "base/logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "test/test_paths.h" +#include "util/file/file_io.h" +#include "util/mach/exception_ports.h" +#include "util/mach/mach_extensions.h" +#include "util/posix/double_fork_and_exec.h" + +namespace crashpad { +namespace test { + +// static +void ExceptionSwallower::Parent_PrepareForCrashingChild() { + Get()->SetParent(); +} + +// static +void ExceptionSwallower::Parent_PrepareForGtestDeathTest() { + if (testing::FLAGS_gtest_death_test_style == "fast") { + Parent_PrepareForCrashingChild(); + } else { + // This is the only other death test style that’s known to gtest. + DCHECK_EQ(testing::FLAGS_gtest_death_test_style, "threadsafe"); + } +} + +// static +void ExceptionSwallower::Child_SwallowExceptions() { + Get()->SwallowExceptions(); +} + +ExceptionSwallower::ExceptionSwallower() + : service_name_(), fd_(), parent_pid_(0) { + base::FilePath exception_swallower_server_path = + TestPaths::Executable().DirName().Append("crashpad_exception_swallower"); + + // Use socketpair() as a full-duplex pipe(). + int socket_fds[2]; + PCHECK(socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socket_fds) == 0) + << "socketpair"; + + fd_.reset(socket_fds[0]); + base::ScopedFD exception_swallower_fd(socket_fds[1]); + + // fd_ is long-lived. Make sure that nobody accidentaly inherits it. + PCHECK(fcntl(fd_.get(), F_SETFD, FD_CLOEXEC) != -1) << "fcntl"; + + // SIGPIPE is undesirable when writing to this socket. Allow broken-pipe + // writes to fail with EPIPE instead. + for (size_t index = 0; index < arraysize(socket_fds); ++index) { + constexpr int value = 1; + PCHECK(setsockopt(socket_fds[index], + SOL_SOCKET, + SO_NOSIGPIPE, + &value, + sizeof(value)) == 0) + << "setsockopt"; + } + + std::vector argv; + argv.reserve(2); + argv.push_back(exception_swallower_server_path.value()); + argv.push_back( + base::StringPrintf("--socket-fd=%d", exception_swallower_fd.get())); + + CHECK(DoubleForkAndExec(argv, exception_swallower_fd.get(), false, nullptr)); + + // Close the exception swallower server’s side of the socket, so that it’s the + // only process that can use it. + exception_swallower_fd.reset(); + + // When the exception swallower server provides its registered service name, + // it’s ready to go. + uint8_t service_name_size; + CheckedReadFileExactly( + fd_.get(), &service_name_size, sizeof(service_name_size)); + service_name_.resize(service_name_size); + if (!service_name_.empty()) { + CheckedReadFileExactly(fd_.get(), &service_name_[0], service_name_.size()); + } + + // Verify that everything’s set up. + base::mac::ScopedMachSendRight exception_swallower_port( + BootstrapLookUp(service_name_)); + CHECK(exception_swallower_port.is_valid()); +} + +ExceptionSwallower::~ExceptionSwallower() {} + +// static +ExceptionSwallower* ExceptionSwallower::Get() { + static ExceptionSwallower* const instance = new ExceptionSwallower(); + return instance; +} + +void ExceptionSwallower::SetParent() { + parent_pid_ = getpid(); +} + +void ExceptionSwallower::SwallowExceptions() { + CHECK_NE(getpid(), parent_pid_); + + base::mac::ScopedMachSendRight exception_swallower_port( + BootstrapLookUp(service_name_)); + CHECK(exception_swallower_port.is_valid()); + + ExceptionPorts task_exception_ports(ExceptionPorts::kTargetTypeTask, + TASK_NULL); + + // The mask is similar to the one used by CrashpadClient::UseHandler(), but + // EXC_CORPSE_NOTIFY is added. This is done for the benefit of tests that + // crash intentionally with their own custom exception port set for EXC_CRASH. + // In that case, depending on the actions taken by the EXC_CRASH handler, the + // exception may be transformed by the kernel into an EXC_CORPSE_NOTIFY, which + // would be sent to an EXC_CORPSE_NOTIFY handler, normally the system’s crash + // reporter at the task or host level. See 10.13.0 + // xnu-4570.1.46/bsd/kern/kern_exit.c proc_prepareexit(). Swallowing + // EXC_CORPSE_NOTIFY at the task level prevents such exceptions from reaching + // the system’s crash reporter. + CHECK(task_exception_ports.SetExceptionPort( + (EXC_MASK_CRASH | + EXC_MASK_RESOURCE | + EXC_MASK_GUARD | + EXC_MASK_CORPSE_NOTIFY) & ExcMaskValid(), + exception_swallower_port.get(), + EXCEPTION_DEFAULT, + THREAD_STATE_NONE)); +} + +} // namespace test +} // namespace crashpad diff --git a/test/mac/exception_swallower.h b/test/mac/exception_swallower.h new file mode 100644 index 00000000..ddf49b46 --- /dev/null +++ b/test/mac/exception_swallower.h @@ -0,0 +1,157 @@ +// 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_TEST_MAC_EXCEPTION_SWALLOWER_H_ +#define CRASHPAD_TEST_MAC_EXCEPTION_SWALLOWER_H_ + +#include + +#include + +#include "base/files/scoped_file.h" +#include "base/macros.h" + +namespace crashpad { +namespace test { + +//! \brief Swallows `EXC_CRASH` and `EXC_CORPSE_NOTIFY` exceptions in test child +//! processes. +//! +//! This class is intended to be used by test code that crashes intentionally. +//! +//! On macOS, the system’s crash reporter normally saves crash reports for all +//! crashes in test code, by virtue of being set as the `EXC_CRASH` or +//! `EXC_CORPSE_NOTIFY` handler. This litters the user’s +//! `~/Library/Logs/DiagnosticReports` directory and can be time-consuming. +//! Reports generated for code that crashes intentionally have no value, and +//! many Crashpad tests do crash intentionally. +//! +//! In order to prevent the system’s crash reporter from handling intentional +//! crashes, use this class. First, ensure that the exception swallower server +//! process, `crashpad_exception_swallower`, is running by calling +//! Parent_PrepareForCrashingChild() or Parent_PrepareForGtestDeathTest() from +//! the parent test process. Then, call Child_SwallowExceptions() from the test +//! child process that crashes intentionally. +//! +//! Don’t call Child_SwallowExceptions() except in test child processes that are +//! expected to crash. It is invalid to call Child_SwallowExceptions() in the +//! parent test process. +//! +//! An exception swallower server process started by this interface will live as +//! long as the process that created it, and will then exit. +//! +//! Crashpad’s ASSERT_DEATH_CRASH(), EXPECT_DEATH_CRASH(), ASSERT_DEATH_CHECK(), +//! and EXPECT_DEATH_CHECK() macros make use of this class on macOS, as does the +//! Multiprocess test interface. +class ExceptionSwallower { + public: + //! \brief In a parent test process, prepares for a crashing child process + //! whose exceptions are to be swallowed. + //! + //! Calling this in a parent test process starts an exception swallower server + //! process if none has been started yet. Subsequently, a forked child process + //! expecting to crash can call Child_SwallowExceptions() to direct exceptions + //! to the exception swallower server process. Multiple children can share a + //! single exception swallower server process. + //! + //! This function establishes the exception swallower server unconditionally. + //! This is not appropriate for gtest death tests, which should use + //! Parent_PrepareForGtestDeathTest() instead. + static void Parent_PrepareForCrashingChild(); + + //! \brief In a parent test process, prepares for a gtest death test whose + //! exceptions are to be swallowed. + //! + //! This is similar to Parent_PrepareForCrashingChild(), except it only starts + //! an exception swallower server process if the gtest + //! death test style is “fast”. With the “fast” style, the death test is + //! run directly from a forked child. The alternative, “threadsafe”, + //! reexecutes the test executable to run the death test. Since the death test + //! does not run directly forked from the parent test process, the parent’s + //! ExceptionSwallower object would not be available to the child, rendering + //! any exception swallower server process started by a parent test process + //! unavailable to the child. Since such an exception swallower server process + //! would go unused, this function will not start one when running under the + //! “threadsafe” style. In that case, each child death test is responsible for + //! starting its own exception swallower server process, and this will occur + //! when child death tests call Child_SwallowExceptions(). + //! + //! This function establishes the exception swallower server conditionally + //! based on the gtest death test style. This is not appropriate for tests + //! that unconditionally fork a child that intentionally crashes without an + //! intervening execv(). For such tests, use Parent_PrepareForCrashingChild() + //! instead. + static void Parent_PrepareForGtestDeathTest(); + + //! \brief In a test child process, arranges to swallow `EXC_CRASH` and + //! `EXC_CORPSE_NOTIFY` exceptions. + //! + //! This must be called in a test child process. It must not be called from a + //! parent test process directly. + //! + //! It is not an error to call this in a child process without having first + //! called Parent_PrepareForCrashingChild() or + //! Parent_PrepareForGtestDeathTest() in the parent process, but failing to do + //! so precludes the possibility of multiple qualified child processes sharing + //! a single exception swallower server process. In this context, children + //! running directly from a forked parent are qualified. gtest death tests + //! under the “threadsafe” gtest + //! death test style are not qualified. + static void Child_SwallowExceptions(); + + private: + ExceptionSwallower(); + ~ExceptionSwallower(); + + //! \brief Returns the ExceptionSwallower singleton. + //! + //! If the object does not yet exist, it will be created, and the exception + //! swallower server process, `crashpad_exception_swallower`, will be started. + static ExceptionSwallower* Get(); + + //! \brief Identifies the calling process as the test parent. + //! + //! This is used to check for interface abuses. Its use is optional, but if + //! it’s called, SwallowExceptions() must not be called from the same process. + void SetParent(); + + //! \brief In a test child process, arranges to swallow `EXC_CRASH` and + //! `EXC_CORPSE_NOTIFY` exceptions. + //! + //! This must be called in a test child process. It must not be called from a + //! parent test process directly. + void SwallowExceptions(); + + std::string service_name_; + + // fd_ is half of a socketpair() that serves a dual purpose. The exception + // swallower server process writes its service name to the socket once + // registered, allowing the parent test process to obtain a reference to the + // service. The socket remains open in the parent test process so that the + // exception swallower server process can observe, based on reading + // end-of-file, when the parent test process has died. The exception swallower + // server process uses this as a signal that it’s safe to exit. + base::ScopedFD fd_; + + pid_t parent_pid_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionSwallower); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_MAC_EXCEPTION_SWALLOWER_H_ diff --git a/test/mac/exception_swallower_exe.cc b/test/mac/exception_swallower_exe.cc new file mode 100644 index 00000000..b5850418 --- /dev/null +++ b/test/mac/exception_swallower_exe.cc @@ -0,0 +1,229 @@ +// 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 +#include +#include +#include +#include + +#include +#include + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "handler/mac/exception_handler_server.h" +#include "tools/tool_support.h" +#include "util/file/file_io.h" +#include "util/mach/exc_server_variants.h" +#include "util/mach/mach_extensions.h" +#include "util/misc/random_string.h" +#include "util/posix/close_stdio.h" +#include "util/stdlib/string_number_conversion.h" +#include "util/thread/thread.h" + +namespace crashpad { +namespace { + +// A Mach exception handler that accepts all exceptions but doesn’t do anything +// with any of them. +class SwallowingExceptionHandler : public UniversalMachExcServer::Interface { + public: + SwallowingExceptionHandler() {} + ~SwallowingExceptionHandler() {} + + kern_return_t CatchMachException(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + *destroy_complex_request = true; + + // Swallow. + + ExcServerCopyState( + behavior, old_state, old_state_count, new_state, new_state_count); + return ExcServerSuccessfulReturnValue(exception, behavior, false); + } + + private: + DISALLOW_COPY_AND_ASSIGN(SwallowingExceptionHandler); +}; + +// Watches a file descriptor, and when it reaches end-of-file, asks the +// ExceptionHandlerServer to stop. Because the file descriptor is one end of a +// socketpair(), and the other end is kept open in the client process, +// end-of-file will be reached when the client process terminates. +class EOFWatcherThread : public Thread { + public: + EOFWatcherThread(int fd, ExceptionHandlerServer* exception_handler_server) + : Thread(), + exception_handler_server_(exception_handler_server), + fd_(fd) {} + + private: + void ThreadMain() override { + char c; + ssize_t rv = ReadFile(fd_.get(), &c, 1); + PCHECK(rv >= 0) << internal::kNativeReadFunctionName; + CHECK(rv == 0); + + exception_handler_server_->Stop(); + } + + ExceptionHandlerServer* exception_handler_server_; // weak + base::ScopedFD fd_; + + DISALLOW_COPY_AND_ASSIGN(EOFWatcherThread); +}; + +void Usage(const std::string& me) { + fprintf(stderr, +"Usage: %s [OPTION]...\n" +"Crashpad's exception swallower.\n" +"\n" +" --socket-fd=FD synchronize with the client over FD\n" +" --help display this help and exit\n" +" --version output version information and exit\n", + me.c_str()); + ToolSupport::UsageTail(me); +} + +int ExceptionSwallowerMain(int argc, char* argv[]) { + const std::string me(basename(argv[0])); + + enum OptionFlags { + // Long options without short equivalents. + kOptionLastChar = 255, + kOptionSocketFD, + + // Standard options. + kOptionHelp = -2, + kOptionVersion = -3, + }; + + struct { + int socket_fd; + } options = {}; + options.socket_fd = -1; + + const option long_options[] = { + {"socket-fd", required_argument, nullptr, kOptionSocketFD}, + {"help", no_argument, nullptr, kOptionHelp}, + {"version", no_argument, nullptr, kOptionVersion}, + {nullptr, 0, nullptr, 0}, + }; + + int opt; + while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) { + switch (opt) { + case kOptionSocketFD: + if (!StringToNumber(optarg, &options.socket_fd) || + options.socket_fd <= STDERR_FILENO) { + ToolSupport::UsageHint(me, "--socket-fd requires a file descriptor"); + return EXIT_FAILURE; + } + break; + case kOptionHelp: { + Usage(me); + return EXIT_SUCCESS; + } + case kOptionVersion: { + ToolSupport::Version(me); + return EXIT_SUCCESS; + } + default: { + ToolSupport::UsageHint(me, nullptr); + return EXIT_FAILURE; + } + } + } + argc -= optind; + argv += optind; + + if (options.socket_fd < 0) { + ToolSupport::UsageHint(me, "--socket-fd is required"); + return EXIT_FAILURE; + } + + if (argc) { + ToolSupport::UsageHint(me, nullptr); + return EXIT_FAILURE; + } + + CloseStdinAndStdout(); + + // Build a service name. Include the PID of the client at the other end of the + // socket, so that the service name has a meaningful relation back to the + // client that started this server process. A simple getppid() won’t do + // because the client started this process with a double-fork(). + pid_t peer_pid; + socklen_t peer_pid_size = base::checked_cast(sizeof(peer_pid)); + PCHECK(getsockopt(options.socket_fd, + SOL_LOCAL, + LOCAL_PEERPID, + &peer_pid, + &peer_pid_size) == 0) + << "getsockopt"; + CHECK_EQ(peer_pid_size, sizeof(peer_pid)); + + std::string service_name = + base::StringPrintf("org.chromium.crashpad.test.exception_swallower.%d.%s", + peer_pid, + RandomString().c_str()); + + base::mac::ScopedMachReceiveRight receive_right( + BootstrapCheckIn(service_name)); + CHECK(receive_right.is_valid()); + + // Tell the client that the service has been checked in, providing the + // service name. + uint8_t service_name_size = base::checked_cast(service_name.size()); + CheckedWriteFile( + options.socket_fd, &service_name_size, sizeof(service_name_size)); + CheckedWriteFile( + options.socket_fd, service_name.c_str(), service_name.size()); + + ExceptionHandlerServer exception_handler_server(std::move(receive_right), + true); + + EOFWatcherThread eof_watcher_thread(options.socket_fd, + &exception_handler_server); + eof_watcher_thread.Start(); + + // This runs until stopped by eof_watcher_thread. + SwallowingExceptionHandler swallowing_exception_handler; + exception_handler_server.Run(&swallowing_exception_handler); + + eof_watcher_thread.Join(); + + return EXIT_SUCCESS; +} + +} // namespace +} // namespace crashpad + +int main(int argc, char* argv[]) { + return crashpad::ExceptionSwallowerMain(argc, argv); +} diff --git a/test/multiprocess.h b/test/multiprocess.h index a7ef6898..b0ff9870 100644 --- a/test/multiprocess.h +++ b/test/multiprocess.h @@ -78,6 +78,10 @@ class Multiprocess { //! TerminationReason::kTerminationNormal, and the default expected //! termination code is `EXIT_SUCCESS` (`0`). //! + //! This method does not need to be called if the default termination + //! expectation is appropriate, but if this method is called, it must be + //! called before Run(). + //! //! \param[in] reason Whether to expect the child to terminate normally or //! as a result of a signal. //! \param[in] code If \a reason is TerminationReason::kTerminationNormal, diff --git a/test/multiprocess_posix.cc b/test/multiprocess_posix.cc index d6796dda..2a8a15cc 100644 --- a/test/multiprocess_posix.cc +++ b/test/multiprocess_posix.cc @@ -27,9 +27,15 @@ #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "base/strings/stringprintf.h" +#include "build/build_config.h" #include "gtest/gtest.h" #include "test/errors.h" #include "util/misc/scoped_forbid_return.h" +#include "util/posix/signals.h" + +#if defined(OS_MACOSX) +#include "test/mac/exception_swallower.h" +#endif namespace crashpad { namespace test { @@ -67,6 +73,17 @@ void Multiprocess::Run() { ASSERT_NO_FATAL_FAILURE(PreFork()); +#if defined(OS_MACOSX) + // If the child is expected to crash, set up an exception swallower process + // to swallow the exception instead of allowing it to be seen by the system’s + // crash reporter. + const bool swallow_exceptions = + reason_ == kTerminationSignal && Signals::IsCrashSignal(code_); + if (swallow_exceptions) { + ExceptionSwallower::Parent_PrepareForCrashingChild(); + } +#endif // OS_MACOSX + pid_t pid = fork(); ASSERT_GE(pid, 0) << ErrnoMessage("fork"); @@ -108,7 +125,7 @@ void Multiprocess::Run() { strsignal(code), WCOREDUMP(status) ? " (core dumped)" : ""); } else { - FAIL() << "Unknown termination reason"; + FAIL() << base::StringPrintf("Unknown termination reason 0x%x", status); } if (reason_ == kTerminationNormal) { @@ -123,12 +140,20 @@ void Multiprocess::Run() { ADD_FAILURE() << message; } } else { +#if defined(OS_MACOSX) + if (swallow_exceptions) { + ExceptionSwallower::Child_SwallowExceptions(); + } +#endif // OS_MACOSX + RunChild(); } } void Multiprocess::SetExpectedChildTermination(TerminationReason reason, int code) { + EXPECT_EQ(info_, nullptr) + << "SetExpectedChildTermination() must be called before Run()"; reason_ = reason; code_ = code; } diff --git a/test/multiprocess_posix_test.cc b/test/multiprocess_posix_test.cc index 44894e8c..94891477 100644 --- a/test/multiprocess_posix_test.cc +++ b/test/multiprocess_posix_test.cc @@ -20,7 +20,7 @@ #include "base/macros.h" #include "gtest/gtest.h" -#include "test/gtest_death_check.h" +#include "test/gtest_death.h" #include "util/file/file_io.h" namespace crashpad { diff --git a/test/test.gyp b/test/test.gyp index 4b389334..f6c1f251 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -37,7 +37,7 @@ 'file.h', 'filesystem.cc', 'filesystem.h', - 'gtest_death_check.h', + 'gtest_death.h', 'gtest_disabled.cc', 'gtest_disabled.h', 'hex_string.cc', @@ -46,6 +46,8 @@ 'linux/fake_ptrace_connection.h', 'mac/dyld.cc', 'mac/dyld.h', + 'mac/exception_swallower.cc', + 'mac/exception_swallower.h', 'mac/mach_errors.cc', 'mac/mach_errors.h', 'mac/mach_multiprocess.cc', @@ -81,6 +83,9 @@ }, 'conditions': [ ['OS=="mac"', { + 'dependencies': [ + 'crashpad_exception_swallower', + ], 'link_settings': { 'libraries': [ '$(SDKROOT)/usr/lib/libbsm.dylib', @@ -141,4 +146,27 @@ ], }, ], + 'conditions': [ + ['OS=="mac"', { + 'targets': [ + { + 'target_name': 'crashpad_exception_swallower', + 'type': 'executable', + 'dependencies': [ + '../compat/compat.gyp:crashpad_compat', + '../handler/handler.gyp:crashpad_handler_lib', + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../tools/tools.gyp:crashpad_tool_support', + '../util/util.gyp:crashpad_util', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'mac/exception_swallower_exe.cc', + ], + }, + ], + }], + ], } diff --git a/util/BUILD.gn b/util/BUILD.gn index b61a76e3..1175de3e 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -149,6 +149,8 @@ static_library("util") { "posix/close_multiple.h", "posix/close_stdio.cc", "posix/close_stdio.h", + "posix/double_fork_and_exec.cc", + "posix/double_fork_and_exec.h", "posix/drop_privileges.cc", "posix/drop_privileges.h", "posix/process_info.h", diff --git a/util/mach/composite_mach_message_server_test.cc b/util/mach/composite_mach_message_server_test.cc index 74e1707a..d45eca0b 100644 --- a/util/mach/composite_mach_message_server_test.cc +++ b/util/mach/composite_mach_message_server_test.cc @@ -18,7 +18,7 @@ #include "base/strings/stringprintf.h" #include "gtest/gtest.h" -#include "test/gtest_death_check.h" +#include "test/gtest_death.h" #include "util/mach/mach_message.h" namespace crashpad { diff --git a/util/mach/exc_server_variants_test.cc b/util/mach/exc_server_variants_test.cc index 358cd1ec..5e67bfe3 100644 --- a/util/mach/exc_server_variants_test.cc +++ b/util/mach/exc_server_variants_test.cc @@ -968,7 +968,10 @@ class TestExcServerVariants : public MachMultiprocess, behavior_(behavior), flavor_(flavor), state_count_(state_count), - handled_(false) {} + handled_(false) { + // This is how the __builtin_trap() in MachMultiprocessChild() appears. + SetExpectedChildTermination(kTerminationSignal, SIGILL); + } // UniversalMachExcServer::Interface: @@ -1013,7 +1016,6 @@ class TestExcServerVariants : public MachMultiprocess, if (exception == EXC_CRASH && code_count >= 1) { int signal; ExcCrashRecoverOriginalException(code[0], nullptr, &signal); - SetExpectedChildTermination(kTerminationSignal, signal); } const bool has_state = ExceptionBehaviorHasState(behavior); diff --git a/util/mach/exception_ports_test.cc b/util/mach/exception_ports_test.cc index 224217bf..af9cc726 100644 --- a/util/mach/exception_ports_test.cc +++ b/util/mach/exception_ports_test.cc @@ -132,7 +132,12 @@ class TestExceptionPorts : public MachMultiprocess, set_on_(set_on), set_type_(set_type), who_crashes_(who_crashes), - handled_(false) {} + handled_(false) { + if (who_crashes_ != kNobodyCrashes) { + // This is how the __builtin_trap() in Child::Crash() appears. + SetExpectedChildTermination(kTerminationSignal, SIGILL); + } + } SetOn set_on() const { return set_on_; } SetType set_type() const { return set_type_; } @@ -190,8 +195,6 @@ class TestExceptionPorts : public MachMultiprocess, // The child crashed with __builtin_trap(), which shows up as SIGILL. EXPECT_EQ(signal, SIGILL); - - SetExpectedChildTermination(kTerminationSignal, signal); } EXPECT_EQ(AuditPIDFromMachMessageTrailer(trailer), 0); diff --git a/util/misc/from_pointer_cast_test.cc b/util/misc/from_pointer_cast_test.cc index b961abba..1a6850ff 100644 --- a/util/misc/from_pointer_cast_test.cc +++ b/util/misc/from_pointer_cast_test.cc @@ -21,7 +21,7 @@ #include "build/build_config.h" #include "gtest/gtest.h" -#include "test/gtest_death_check.h" +#include "test/gtest_death.h" namespace crashpad { namespace test { @@ -233,27 +233,28 @@ TEST(FromPointerCast, ToNarrowInteger) { TEST(FromPointerCastDeathTest, ToNarrowInteger) { if (sizeof(int) < sizeof(void*)) { - EXPECT_DEATH(FromPointerCast( - reinterpret_cast(static_cast( - std::numeric_limits::max() + 1ull))), - ""); - EXPECT_DEATH(FromPointerCast( - reinterpret_cast(static_cast( - std::numeric_limits::max() + 1ull))), - ""); + EXPECT_DEATH_CHECK( + FromPointerCast(reinterpret_cast(static_cast( + std::numeric_limits::max() + 1ull))), + ""); + EXPECT_DEATH_CHECK( + FromPointerCast( + reinterpret_cast(static_cast( + std::numeric_limits::max() + 1ull))), + ""); } // int and unsigned int may not be narrower than a pointer, so also test short // and unsigned short. - EXPECT_DEATH(FromPointerCast( - reinterpret_cast(static_cast( - std::numeric_limits::max() + 1u))), - ""); - EXPECT_DEATH(FromPointerCast( - reinterpret_cast(static_cast( - std::numeric_limits::max() + 1u))), - ""); + EXPECT_DEATH_CHECK( + FromPointerCast(reinterpret_cast(static_cast( + std::numeric_limits::max() + 1u))), + ""); + EXPECT_DEATH_CHECK(FromPointerCast( + reinterpret_cast(static_cast( + std::numeric_limits::max() + 1u))), + ""); } } // namespace diff --git a/util/misc/initialization_state_dcheck_test.cc b/util/misc/initialization_state_dcheck_test.cc index 2c407675..f954d004 100644 --- a/util/misc/initialization_state_dcheck_test.cc +++ b/util/misc/initialization_state_dcheck_test.cc @@ -21,7 +21,7 @@ #include "base/logging.h" #include "base/memory/free_deleter.h" #include "gtest/gtest.h" -#include "test/gtest_death_check.h" +#include "test/gtest_death.h" namespace crashpad { namespace test { diff --git a/util/misc/scoped_forbid_return_test.cc b/util/misc/scoped_forbid_return_test.cc index 4f73ea58..06cb5494 100644 --- a/util/misc/scoped_forbid_return_test.cc +++ b/util/misc/scoped_forbid_return_test.cc @@ -16,7 +16,7 @@ #include "base/compiler_specific.h" #include "gtest/gtest.h" -#include "test/gtest_death_check.h" +#include "test/gtest_death.h" namespace crashpad { namespace test { diff --git a/util/net/http_multipart_builder_test.cc b/util/net/http_multipart_builder_test.cc index 08806c37..dab9e7e7 100644 --- a/util/net/http_multipart_builder_test.cc +++ b/util/net/http_multipart_builder_test.cc @@ -19,7 +19,7 @@ #include #include "gtest/gtest.h" -#include "test/gtest_death_check.h" +#include "test/gtest_death.h" #include "test/test_paths.h" #include "util/net/http_body.h" #include "util/net/http_body_test_util.h" diff --git a/util/posix/double_fork_and_exec.cc b/util/posix/double_fork_and_exec.cc new file mode 100644 index 00000000..df74f709 --- /dev/null +++ b/util/posix/double_fork_and_exec.cc @@ -0,0 +1,146 @@ +// 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 "util/posix/double_fork_and_exec.h" + +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/stringprintf.h" +#include "util/posix/close_multiple.h" + +namespace crashpad { + +bool DoubleForkAndExec(const std::vector& argv, + int preserve_fd, + bool use_path, + void (*child_function)()) { + // argv_c contains const char* pointers and is terminated by nullptr. This is + // suitable for passing to execv(). Although argv_c is not used in the parent + // process, it must be built in the parent process because it’s unsafe to do + // so in the child or grandchild process. + std::vector argv_c; + argv_c.reserve(argv.size() + 1); + for (const std::string& argument : argv) { + argv_c.push_back(argument.c_str()); + } + argv_c.push_back(nullptr); + + // Double-fork(). The three processes involved are parent, child, and + // grandchild. The grandchild will call execv(). The child exits immediately + // after spawning the grandchild, so the grandchild becomes an orphan and its + // parent process ID becomes 1. This relieves the parent and child of the + // responsibility to reap the grandchild with waitpid() or similar. The + // grandchild is expected to outlive the parent process, so the parent + // shouldn’t be concerned with reaping it. This approach means that accidental + // early termination of the handler process will not result in a zombie + // process. + pid_t pid = fork(); + if (pid < 0) { + PLOG(ERROR) << "fork"; + return false; + } + + if (pid == 0) { + // Child process. + + if (child_function) { + child_function(); + } + + // Call setsid(), creating a new process group and a new session, both led + // by this process. The new process group has no controlling terminal. This + // disconnects it from signals generated by the parent process’ terminal. + // + // setsid() is done in the child instead of the grandchild so that the + // grandchild will not be a session leader. If it were a session leader, an + // accidental open() of a terminal device without O_NOCTTY would make that + // terminal the controlling terminal. + // + // It’s not desirable for the grandchild to have a controlling terminal. The + // grandchild manages its own lifetime, such as by monitoring clients on its + // own and exiting when it loses all clients and when it deems it + // appropraite to do so. It may serve clients in different process groups or + // sessions than its original client, and receiving signals intended for its + // original client’s process group could be harmful in that case. + PCHECK(setsid() != -1) << "setsid"; + + pid = fork(); + if (pid < 0) { + PLOG(FATAL) << "fork"; + } + + if (pid > 0) { + // Child process. + + // _exit() instead of exit(), because fork() was called. + _exit(EXIT_SUCCESS); + } + + // Grandchild process. + + CloseMultipleNowOrOnExec(STDERR_FILENO + 1, preserve_fd); + + // &argv_c[0] is a pointer to a pointer to const char data, but because of + // how C (not C++) works, execvp() wants a pointer to a const pointer to + // char data. It modifies neither the data nor the pointers, so the + // const_cast is safe. + char* const* argv_for_execv = const_cast(&argv_c[0]); + + if (use_path) { + execvp(argv_for_execv[0], argv_for_execv); + PLOG(FATAL) << "execvp " << argv_for_execv[0]; + } + + execv(argv_for_execv[0], argv_for_execv); + PLOG(FATAL) << "execv " << argv_for_execv[0]; + } + + // waitpid() for the child, so that it does not become a zombie process. The + // child normally exits quickly. + // + // Failures from this point on may result in the accumulation of a zombie, but + // should not be considered fatal. Log only warnings, but don’t treat these + // failures as a failure of the function overall. + int status; + pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0)); + if (wait_pid == -1) { + PLOG(WARNING) << "waitpid"; + return true; + } + DCHECK_EQ(wait_pid, pid); + + if (WIFSIGNALED(status)) { + int sig = WTERMSIG(status); + LOG(WARNING) << base::StringPrintf( + "intermediate process terminated by signal %d (%s)%s", + sig, + strsignal(sig), + WCOREDUMP(status) ? " (core dumped)" : ""); + } else if (!WIFEXITED(status)) { + LOG(WARNING) << base::StringPrintf( + "intermediate process: unknown termination 0x%x", status); + } else if (WEXITSTATUS(status) != EXIT_SUCCESS) { + LOG(WARNING) << "intermediate process exited with code " + << WEXITSTATUS(status); + } + + return true; +} + +} // namespace crashpad diff --git a/util/posix/double_fork_and_exec.h b/util/posix/double_fork_and_exec.h new file mode 100644 index 00000000..df340d07 --- /dev/null +++ b/util/posix/double_fork_and_exec.h @@ -0,0 +1,67 @@ +// 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_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_ +#define CRASHPAD_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_ + +#include +#include + +namespace crashpad { + +//! \brief Executes a (grand-)child process. +//! +//! The grandchild process will be started through the +//! double-`fork()`-and-`execv()` pattern. This allows the grandchild to fully +//! disassociate from the parent. The grandchild will not be a member of the +//! parent’s process group or session and will not have a controlling terminal, +//! providing isolation from signals not intended for it. The grandchild’s +//! parent process, in terms of the process tree hierarchy, will be the process +//! with process ID 1, relieving any other process of the responsibility to reap +//! it via `waitpid()`. Aside from the three file descriptors associated with +//! the standard input/output streams and any file descriptor passed in \a +//! preserve_fd, the grandchild will not inherit any file descriptors from the +//! parent process. +//! +//! \param[in] argv The argument vector to start the grandchild process with. +//! `argv[0]` is used as the path to the executable. +//! \param[in] preserve_fd A file descriptor to be inherited by the grandchild +//! process. This file descriptor is inherited in addition to the three file +//! descriptors associated with the standard input/output streams. Use `-1` +//! if no additional file descriptors are to be inherited. +//! \param[in] use_path Whether to consult the `PATH` environment variable when +//! requested to start an executable at a non-absolute path. If `false`, +//! `execv()`, which does not consult `PATH`, will be used. If `true`, +//! `execvp()`, which does consult `PATH`, will be used. +//! \param[in] child_function If not `nullptr`, this function will be called in +//! the intermediate child process, prior to the second `fork()`. Take note +//! that this function will run in the context of a forked process, and must +//! be safe for that purpose. +//! +//! \return `true` on success, and `false` on failure with a message logged. +//! Only failures that occur in the parent process that indicate a definite +//! failure to start the the grandchild are reported in the return value. +//! Failures in the intermediate child or grandchild processes cannot be +//! reported in the return value, and are addressed by logging a message and +//! terminating. The caller assumes the responsibility for detecting such +//! failures, for example, by observing a failure to perform a successful +//! handshake with the grandchild process. +bool DoubleForkAndExec(const std::vector& argv, + int preserve_fd, + bool use_path, + void (*child_function)()); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_POSIX_DOUBLE_FORK_AND_EXEC_H_ diff --git a/util/posix/scoped_mmap_test.cc b/util/posix/scoped_mmap_test.cc index 33807295..7312b849 100644 --- a/util/posix/scoped_mmap_test.cc +++ b/util/posix/scoped_mmap_test.cc @@ -22,6 +22,7 @@ #include "base/rand_util.h" #include "base/strings/stringprintf.h" #include "gtest/gtest.h" +#include "test/gtest_death.h" namespace crashpad { namespace test { @@ -118,7 +119,7 @@ TEST(ScopedMmapDeathTest, Destructor) { cookie.SetUp(mapping.addr_as()); } - EXPECT_DEATH(cookie.Check(), ""); + EXPECT_DEATH_CRASH(cookie.Check(), ""); } TEST(ScopedMmapDeathTest, Reset) { @@ -135,7 +136,7 @@ TEST(ScopedMmapDeathTest, Reset) { ASSERT_TRUE(mapping.Reset()); - EXPECT_DEATH(cookie.Check(), ""); + EXPECT_DEATH_CRASH(cookie.Check(), ""); } TEST(ScopedMmapDeathTest, ResetAddrLen_Shrink) { @@ -164,8 +165,8 @@ TEST(ScopedMmapDeathTest, ResetAddrLen_Shrink) { EXPECT_EQ(cookies[1].Observed(), cookies[1].Expected()); - EXPECT_DEATH(cookies[0].Check(), ""); - EXPECT_DEATH(cookies[2].Check(), ""); + EXPECT_DEATH_CRASH(cookies[0].Check(), ""); + EXPECT_DEATH_CRASH(cookies[2].Check(), ""); } TEST(ScopedMmap, ResetAddrLen_Grow) { @@ -230,7 +231,7 @@ TEST(ScopedMmapDeathTest, ResetAddrLen_MoveDownAndGrow) { EXPECT_EQ(cookies[0].Observed(), cookies[0].Expected()); EXPECT_EQ(cookies[1].Observed(), cookies[1].Expected()); - EXPECT_DEATH(cookies[2].Check(), ""); + EXPECT_DEATH_CRASH(cookies[2].Check(), ""); } TEST(ScopedMmapDeathTest, ResetAddrLen_MoveUpAndShrink) { @@ -262,8 +263,8 @@ TEST(ScopedMmapDeathTest, ResetAddrLen_MoveUpAndShrink) { EXPECT_EQ(cookies[2].Observed(), cookies[2].Expected()); - EXPECT_DEATH(cookies[0].Check(), ""); - EXPECT_DEATH(cookies[1].Check(), ""); + EXPECT_DEATH_CRASH(cookies[0].Check(), ""); + EXPECT_DEATH_CRASH(cookies[1].Check(), ""); } TEST(ScopedMmapDeathTest, ResetMmap) { @@ -289,7 +290,7 @@ TEST(ScopedMmapDeathTest, ResetMmap) { EXPECT_NE(mapping.addr(), MAP_FAILED); EXPECT_EQ(mapping.len(), kPageSize); - EXPECT_DEATH(cookie.Check(), ""); + EXPECT_DEATH_CRASH(cookie.Check(), ""); } TEST(ScopedMmapDeathTest, Mprotect) { @@ -306,7 +307,7 @@ TEST(ScopedMmapDeathTest, Mprotect) { ASSERT_TRUE(mapping.Mprotect(PROT_READ)); - EXPECT_DEATH(*addr = 0, ""); + EXPECT_DEATH_CRASH(*addr = 0, ""); ASSERT_TRUE(mapping.Mprotect(PROT_READ | PROT_WRITE)); EXPECT_EQ(*addr, 1); diff --git a/util/stdlib/aligned_allocator_test.cc b/util/stdlib/aligned_allocator_test.cc index d52dcfa2..1c16dcc5 100644 --- a/util/stdlib/aligned_allocator_test.cc +++ b/util/stdlib/aligned_allocator_test.cc @@ -18,6 +18,7 @@ #include "base/compiler_specific.h" #include "gtest/gtest.h" +#include "test/gtest_death.h" #if defined(OS_WIN) #include @@ -110,7 +111,7 @@ void BadAlignmentTest() { } TEST(AlignedAllocatorDeathTest, BadAlignment) { - ASSERT_DEATH(BadAlignmentTest(), ""); + ASSERT_DEATH_CRASH(BadAlignmentTest(), ""); } } // namespace diff --git a/util/util.gyp b/util/util.gyp index 63314834..5aeb4503 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -179,6 +179,8 @@ 'posix/close_stdio.h', 'posix/drop_privileges.cc', 'posix/drop_privileges.h', + 'posix/double_fork_and_exec.cc', + 'posix/double_fork_and_exec.h', 'posix/process_info.h', 'posix/process_info_linux.cc', 'posix/process_info_mac.cc', From cd1d773a40e4d75addc19f47ead67bfbd058f007 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Mon, 20 Nov 2017 14:36:54 -0500 Subject: [PATCH 027/326] mac: Run the exception swallower server in the parent test process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The exception swallower server’s design and interface are both considerably simpler when the server runs in a thread in the parent test process, as opposed to a separate process. The only caveat is that this results in calls to fork() while threaded. Uses of gtest {ASSERT,EXPECT}_DEATH with the default “fast” gtest death test style result in this warning: [WARNING] ../../third_party/gtest/gtest/googletest/src/gtest-death-test.cc:836:: Death tests use fork(), which is unsafe particularly in a threaded context. For this test, Google Test detected 2 threads. Bug: crashpad:33 Change-Id: Ib8f418064ea4ab942859c3393cb15cf71365614d Reviewed-on: https://chromium-review.googlesource.com/779481 Commit-Queue: Mark Mentovai Reviewed-by: Robert Sesek --- test/BUILD.gn | 20 +-- test/gtest_death.h | 22 ++- test/mac/exception_swallower.cc | 204 +++++++++++++++---------- test/mac/exception_swallower.h | 125 ++++----------- test/mac/exception_swallower_exe.cc | 229 ---------------------------- test/multiprocess_posix.cc | 17 +-- test/test.gyp | 25 +-- 7 files changed, 172 insertions(+), 470 deletions(-) delete mode 100644 test/mac/exception_swallower_exe.cc diff --git a/test/BUILD.gn b/test/BUILD.gn index 9337fa99..c3208ad6 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -80,9 +80,7 @@ static_library("test") { if (is_mac) { libs = [ "bsm" ] - data_deps = [ - ":crashpad_exception_swallower", - ] + deps += [ "//third_party/crashpad/crashpad/handler" ] } } @@ -158,19 +156,3 @@ static_library("gtest_main") { "//testing/gtest", ] } - -if (is_mac) { - executable("crashpad_exception_swallower") { - testonly = true - sources = [ - "mac/exception_swallower_exe.cc", - ] - deps = [ - "//base", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/handler", - "//third_party/crashpad/crashpad/tools:tool_support", - "//third_party/crashpad/crashpad/util", - ] - } -} diff --git a/test/gtest_death.h b/test/gtest_death.h index 1c7e5f6a..493c9c6b 100644 --- a/test/gtest_death.h +++ b/test/gtest_death.h @@ -37,10 +37,13 @@ //! //! \sa ASSERT_DEATH_CHECK() //! \sa EXPECT_DEATH_CRASH() -#define ASSERT_DEATH_CRASH(statement, regex) \ - crashpad::test::ExceptionSwallower::Parent_PrepareForGtestDeathTest(); \ - ASSERT_DEATH(crashpad::test::ExceptionSwallower::Child_SwallowExceptions(); \ - { statement; }, regex) +#define ASSERT_DEATH_CRASH(statement, regex) \ + do { \ + crashpad::test::ExceptionSwallower exception_swallower; \ + ASSERT_DEATH(crashpad::test::ExceptionSwallower::SwallowExceptions(); \ + { statement; }, \ + regex); \ + } while (false) //! \brief Wraps the gtest `EXPECT_DEATH()` macro to make assertions about death //! caused by crashes. @@ -52,10 +55,13 @@ //! //! \sa EXPECT_DEATH_CHECK() //! \sa ASSERT_DEATH_CRASH() -#define EXPECT_DEATH_CRASH(statement, regex) \ - crashpad::test::ExceptionSwallower::Parent_PrepareForGtestDeathTest(); \ - EXPECT_DEATH(crashpad::test::ExceptionSwallower::Child_SwallowExceptions(); \ - { statement; }, regex) +#define EXPECT_DEATH_CRASH(statement, regex) \ + do { \ + crashpad::test::ExceptionSwallower exception_swallower; \ + EXPECT_DEATH(crashpad::test::ExceptionSwallower::SwallowExceptions(); \ + { statement; }, \ + regex); \ + } while (false) #else // OS_MACOSX diff --git a/test/mac/exception_swallower.cc b/test/mac/exception_swallower.cc index 307680c0..18c5cf71 100644 --- a/test/mac/exception_swallower.cc +++ b/test/mac/exception_swallower.cc @@ -14,118 +14,156 @@ #include "test/mac/exception_swallower.h" -#include -#include +#include +#include +#include #include -#include #include "base/logging.h" #include "base/mac/scoped_mach_port.h" #include "base/strings/stringprintf.h" -#include "gtest/gtest.h" -#include "test/test_paths.h" -#include "util/file/file_io.h" +#include "handler/mac/exception_handler_server.h" +#include "util/mach/exc_server_variants.h" #include "util/mach/exception_ports.h" #include "util/mach/mach_extensions.h" -#include "util/posix/double_fork_and_exec.h" +#include "util/misc/random_string.h" +#include "util/thread/thread.h" namespace crashpad { namespace test { -// static -void ExceptionSwallower::Parent_PrepareForCrashingChild() { - Get()->SetParent(); +namespace { + +constexpr char kServiceEnvironmentVariable[] = + "CRASHPAD_EXCEPTION_SWALLOWER_SERVICE"; + +ExceptionSwallower* g_exception_swallower; + +// Like getenv(), but fails a CHECK() if the underlying function fails. It’s not +// considered a failure for |name| to be unset in the environment. In that case, +// nullptr is returned. +const char* CheckedGetenv(const char* name) { + errno = 0; + const char* value; + PCHECK((value = getenv(name)) || errno == 0) << "getenv"; + return value; } -// static -void ExceptionSwallower::Parent_PrepareForGtestDeathTest() { - if (testing::FLAGS_gtest_death_test_style == "fast") { - Parent_PrepareForCrashingChild(); - } else { - // This is the only other death test style that’s known to gtest. - DCHECK_EQ(testing::FLAGS_gtest_death_test_style, "threadsafe"); - } -} +} // namespace -// static -void ExceptionSwallower::Child_SwallowExceptions() { - Get()->SwallowExceptions(); -} - -ExceptionSwallower::ExceptionSwallower() - : service_name_(), fd_(), parent_pid_(0) { - base::FilePath exception_swallower_server_path = - TestPaths::Executable().DirName().Append("crashpad_exception_swallower"); - - // Use socketpair() as a full-duplex pipe(). - int socket_fds[2]; - PCHECK(socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socket_fds) == 0) - << "socketpair"; - - fd_.reset(socket_fds[0]); - base::ScopedFD exception_swallower_fd(socket_fds[1]); - - // fd_ is long-lived. Make sure that nobody accidentaly inherits it. - PCHECK(fcntl(fd_.get(), F_SETFD, FD_CLOEXEC) != -1) << "fcntl"; - - // SIGPIPE is undesirable when writing to this socket. Allow broken-pipe - // writes to fail with EPIPE instead. - for (size_t index = 0; index < arraysize(socket_fds); ++index) { - constexpr int value = 1; - PCHECK(setsockopt(socket_fds[index], - SOL_SOCKET, - SO_NOSIGPIPE, - &value, - sizeof(value)) == 0) - << "setsockopt"; +class ExceptionSwallower::ExceptionSwallowerThread + : public Thread, + public UniversalMachExcServer::Interface { + public: + explicit ExceptionSwallowerThread( + base::mac::ScopedMachReceiveRight receive_right) + : Thread(), + UniversalMachExcServer::Interface(), + exception_handler_server_(std::move(receive_right), true), + pid_(getpid()) { + Start(); } - std::vector argv; - argv.reserve(2); - argv.push_back(exception_swallower_server_path.value()); - argv.push_back( - base::StringPrintf("--socket-fd=%d", exception_swallower_fd.get())); + ~ExceptionSwallowerThread() override {} - CHECK(DoubleForkAndExec(argv, exception_swallower_fd.get(), false, nullptr)); + void Stop() { exception_handler_server_.Stop(); } - // Close the exception swallower server’s side of the socket, so that it’s the - // only process that can use it. - exception_swallower_fd.reset(); + // Returns the process ID that the thread is running in. This is used to + // detect misuses that place the exception swallower server thread and code + // that wants its exceptions swallowed in the same process. + pid_t ProcessID() const { return pid_; } - // When the exception swallower server provides its registered service name, - // it’s ready to go. - uint8_t service_name_size; - CheckedReadFileExactly( - fd_.get(), &service_name_size, sizeof(service_name_size)); - service_name_.resize(service_name_size); - if (!service_name_.empty()) { - CheckedReadFileExactly(fd_.get(), &service_name_[0], service_name_.size()); + private: + // Thread: + + void ThreadMain() override { exception_handler_server_.Run(this); } + + // UniversalMachExcServer::Interface: + + kern_return_t CatchMachException(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + *destroy_complex_request = true; + + // Swallow. + + ExcServerCopyState( + behavior, old_state, old_state_count, new_state, new_state_count); + return ExcServerSuccessfulReturnValue(exception, behavior, false); } - // Verify that everything’s set up. - base::mac::ScopedMachSendRight exception_swallower_port( - BootstrapLookUp(service_name_)); - CHECK(exception_swallower_port.is_valid()); + ExceptionHandlerServer exception_handler_server_; + pid_t pid_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionSwallowerThread); +}; + +ExceptionSwallower::ExceptionSwallower() : exception_swallower_thread_() { + CHECK(!g_exception_swallower); + g_exception_swallower = this; + + if (CheckedGetenv(kServiceEnvironmentVariable)) { + // The environment variable is already set, so just proceed with the + // existing service. This normally happens when the gtest “threadsafe” death + // test style is chosen, because the test child process will re-execute code + // already run in the test parent process. See + // https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#death-test-styles. + return; + } + + std::string service_name = + base::StringPrintf("org.chromium.crashpad.test.exception_swallower.%d.%s", + getpid(), + RandomString().c_str()); + base::mac::ScopedMachReceiveRight receive_right( + BootstrapCheckIn(service_name)); + CHECK(receive_right.is_valid()); + + exception_swallower_thread_.reset( + new ExceptionSwallowerThread(std::move(receive_right))); + + PCHECK(setenv(kServiceEnvironmentVariable, service_name.c_str(), 1) == 0) + << "setenv"; } -ExceptionSwallower::~ExceptionSwallower() {} +ExceptionSwallower::~ExceptionSwallower() { + PCHECK(unsetenv(kServiceEnvironmentVariable) == 0) << "unsetenv"; + + exception_swallower_thread_->Stop(); + exception_swallower_thread_->Join(); + + CHECK_EQ(g_exception_swallower, this); + g_exception_swallower = nullptr; +} // static -ExceptionSwallower* ExceptionSwallower::Get() { - static ExceptionSwallower* const instance = new ExceptionSwallower(); - return instance; -} - -void ExceptionSwallower::SetParent() { - parent_pid_ = getpid(); -} - void ExceptionSwallower::SwallowExceptions() { - CHECK_NE(getpid(), parent_pid_); + // The exception swallower thread can’t be in this process, because the + // EXC_CRASH or EXC_CORPSE_NOTIFY exceptions that it needs to swallow will be + // delivered after a crash has occurred and none of its threads will be + // scheduled to run. + CHECK(!g_exception_swallower || + !g_exception_swallower->exception_swallower_thread_ || + g_exception_swallower->exception_swallower_thread_->ProcessID() != + getpid()); + + const char* service_name = CheckedGetenv(kServiceEnvironmentVariable); + CHECK(service_name); base::mac::ScopedMachSendRight exception_swallower_port( - BootstrapLookUp(service_name_)); + BootstrapLookUp(service_name)); CHECK(exception_swallower_port.is_valid()); ExceptionPorts task_exception_ports(ExceptionPorts::kTargetTypeTask, diff --git a/test/mac/exception_swallower.h b/test/mac/exception_swallower.h index ddf49b46..7c3a4421 100644 --- a/test/mac/exception_swallower.h +++ b/test/mac/exception_swallower.h @@ -15,11 +15,8 @@ #ifndef CRASHPAD_TEST_MAC_EXCEPTION_SWALLOWER_H_ #define CRASHPAD_TEST_MAC_EXCEPTION_SWALLOWER_H_ -#include +#include -#include - -#include "base/files/scoped_file.h" #include "base/macros.h" namespace crashpad { @@ -37,116 +34,48 @@ namespace test { //! Reports generated for code that crashes intentionally have no value, and //! many Crashpad tests do crash intentionally. //! -//! In order to prevent the system’s crash reporter from handling intentional -//! crashes, use this class. First, ensure that the exception swallower server -//! process, `crashpad_exception_swallower`, is running by calling -//! Parent_PrepareForCrashingChild() or Parent_PrepareForGtestDeathTest() from -//! the parent test process. Then, call Child_SwallowExceptions() from the test -//! child process that crashes intentionally. +//! Instantiate an ExceptionSwallower object in a parent test process (a process +//! where `TEST()`, `TEST_F()`, and `TEST_P()` execute) to create an exception +//! swallower server running on a dedicated thread. A service mapping for this +//! server will be published with the bootstrap server and made available in the +//! `CRASHPAD_EXCEPTION_SWALLOWER_SERVICE` environment variable. In a child +//! process, call SwallowExceptions() to look up this service and set it as the +//! `EXC_CRASH` and `EXC_CORPSE_NOTIFY` handler. When these exceptions are +//! raised in the child process, they’ll be handled by the exception swallower +//! server, which performs no action but reports that exceptions were +//! successfully handled so that the system’s crash reporter, ReportCrash, will +//! not be invoked. //! -//! Don’t call Child_SwallowExceptions() except in test child processes that are -//! expected to crash. It is invalid to call Child_SwallowExceptions() in the -//! parent test process. -//! -//! An exception swallower server process started by this interface will live as -//! long as the process that created it, and will then exit. +//! At most one ExceptionSwallower may be instantiated in a process at a time. +//! If `CRASHPAD_EXCEPTION_SWALLOWER_SERVICE` is already set, ExceptionSwallower +//! leaves it in place and takes no additional action. //! //! Crashpad’s ASSERT_DEATH_CRASH(), EXPECT_DEATH_CRASH(), ASSERT_DEATH_CHECK(), //! and EXPECT_DEATH_CHECK() macros make use of this class on macOS, as does the //! Multiprocess test interface. class ExceptionSwallower { public: - //! \brief In a parent test process, prepares for a crashing child process - //! whose exceptions are to be swallowed. - //! - //! Calling this in a parent test process starts an exception swallower server - //! process if none has been started yet. Subsequently, a forked child process - //! expecting to crash can call Child_SwallowExceptions() to direct exceptions - //! to the exception swallower server process. Multiple children can share a - //! single exception swallower server process. - //! - //! This function establishes the exception swallower server unconditionally. - //! This is not appropriate for gtest death tests, which should use - //! Parent_PrepareForGtestDeathTest() instead. - static void Parent_PrepareForCrashingChild(); - - //! \brief In a parent test process, prepares for a gtest death test whose - //! exceptions are to be swallowed. - //! - //! This is similar to Parent_PrepareForCrashingChild(), except it only starts - //! an exception swallower server process if the gtest - //! death test style is “fast”. With the “fast” style, the death test is - //! run directly from a forked child. The alternative, “threadsafe”, - //! reexecutes the test executable to run the death test. Since the death test - //! does not run directly forked from the parent test process, the parent’s - //! ExceptionSwallower object would not be available to the child, rendering - //! any exception swallower server process started by a parent test process - //! unavailable to the child. Since such an exception swallower server process - //! would go unused, this function will not start one when running under the - //! “threadsafe” style. In that case, each child death test is responsible for - //! starting its own exception swallower server process, and this will occur - //! when child death tests call Child_SwallowExceptions(). - //! - //! This function establishes the exception swallower server conditionally - //! based on the gtest death test style. This is not appropriate for tests - //! that unconditionally fork a child that intentionally crashes without an - //! intervening execv(). For such tests, use Parent_PrepareForCrashingChild() - //! instead. - static void Parent_PrepareForGtestDeathTest(); - - //! \brief In a test child process, arranges to swallow `EXC_CRASH` and - //! `EXC_CORPSE_NOTIFY` exceptions. - //! - //! This must be called in a test child process. It must not be called from a - //! parent test process directly. - //! - //! It is not an error to call this in a child process without having first - //! called Parent_PrepareForCrashingChild() or - //! Parent_PrepareForGtestDeathTest() in the parent process, but failing to do - //! so precludes the possibility of multiple qualified child processes sharing - //! a single exception swallower server process. In this context, children - //! running directly from a forked parent are qualified. gtest death tests - //! under the “threadsafe” gtest - //! death test style are not qualified. - static void Child_SwallowExceptions(); - - private: ExceptionSwallower(); ~ExceptionSwallower(); - //! \brief Returns the ExceptionSwallower singleton. - //! - //! If the object does not yet exist, it will be created, and the exception - //! swallower server process, `crashpad_exception_swallower`, will be started. - static ExceptionSwallower* Get(); - - //! \brief Identifies the calling process as the test parent. - //! - //! This is used to check for interface abuses. Its use is optional, but if - //! it’s called, SwallowExceptions() must not be called from the same process. - void SetParent(); - //! \brief In a test child process, arranges to swallow `EXC_CRASH` and //! `EXC_CORPSE_NOTIFY` exceptions. //! //! This must be called in a test child process. It must not be called from a - //! parent test process directly. - void SwallowExceptions(); + //! parent test process directly. Parent test processes are those that execute + //! `TEST()`, `TEST_F()`, and `TEST_P()`. Test child processes execute + //! ASSERT_DEATH_CRASH(), EXPECT_DEATH_CRASH(), ASSERT_DEATH_CHECK(), + //! EXPECT_DEATH_CHECK(), and Multiprocess::RunChild(). + //! + //! It is an error to call this in a test child process without having first + //! instantiated an ExceptionSwallower object in a parent test project. It is + //! also an error to call this in a parent test process. + static void SwallowExceptions(); - std::string service_name_; + private: + class ExceptionSwallowerThread; - // fd_ is half of a socketpair() that serves a dual purpose. The exception - // swallower server process writes its service name to the socket once - // registered, allowing the parent test process to obtain a reference to the - // service. The socket remains open in the parent test process so that the - // exception swallower server process can observe, based on reading - // end-of-file, when the parent test process has died. The exception swallower - // server process uses this as a signal that it’s safe to exit. - base::ScopedFD fd_; - - pid_t parent_pid_; + std::unique_ptr exception_swallower_thread_; DISALLOW_COPY_AND_ASSIGN(ExceptionSwallower); }; diff --git a/test/mac/exception_swallower_exe.cc b/test/mac/exception_swallower_exe.cc deleted file mode 100644 index b5850418..00000000 --- a/test/mac/exception_swallower_exe.cc +++ /dev/null @@ -1,229 +0,0 @@ -// 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 -#include -#include -#include -#include - -#include -#include - -#include "base/logging.h" -#include "base/numerics/safe_conversions.h" -#include "base/strings/stringprintf.h" -#include "handler/mac/exception_handler_server.h" -#include "tools/tool_support.h" -#include "util/file/file_io.h" -#include "util/mach/exc_server_variants.h" -#include "util/mach/mach_extensions.h" -#include "util/misc/random_string.h" -#include "util/posix/close_stdio.h" -#include "util/stdlib/string_number_conversion.h" -#include "util/thread/thread.h" - -namespace crashpad { -namespace { - -// A Mach exception handler that accepts all exceptions but doesn’t do anything -// with any of them. -class SwallowingExceptionHandler : public UniversalMachExcServer::Interface { - public: - SwallowingExceptionHandler() {} - ~SwallowingExceptionHandler() {} - - kern_return_t CatchMachException(exception_behavior_t behavior, - exception_handler_t exception_port, - thread_t thread, - task_t task, - exception_type_t exception, - const mach_exception_data_type_t* code, - mach_msg_type_number_t code_count, - thread_state_flavor_t* flavor, - ConstThreadState old_state, - mach_msg_type_number_t old_state_count, - thread_state_t new_state, - mach_msg_type_number_t* new_state_count, - const mach_msg_trailer_t* trailer, - bool* destroy_complex_request) override { - *destroy_complex_request = true; - - // Swallow. - - ExcServerCopyState( - behavior, old_state, old_state_count, new_state, new_state_count); - return ExcServerSuccessfulReturnValue(exception, behavior, false); - } - - private: - DISALLOW_COPY_AND_ASSIGN(SwallowingExceptionHandler); -}; - -// Watches a file descriptor, and when it reaches end-of-file, asks the -// ExceptionHandlerServer to stop. Because the file descriptor is one end of a -// socketpair(), and the other end is kept open in the client process, -// end-of-file will be reached when the client process terminates. -class EOFWatcherThread : public Thread { - public: - EOFWatcherThread(int fd, ExceptionHandlerServer* exception_handler_server) - : Thread(), - exception_handler_server_(exception_handler_server), - fd_(fd) {} - - private: - void ThreadMain() override { - char c; - ssize_t rv = ReadFile(fd_.get(), &c, 1); - PCHECK(rv >= 0) << internal::kNativeReadFunctionName; - CHECK(rv == 0); - - exception_handler_server_->Stop(); - } - - ExceptionHandlerServer* exception_handler_server_; // weak - base::ScopedFD fd_; - - DISALLOW_COPY_AND_ASSIGN(EOFWatcherThread); -}; - -void Usage(const std::string& me) { - fprintf(stderr, -"Usage: %s [OPTION]...\n" -"Crashpad's exception swallower.\n" -"\n" -" --socket-fd=FD synchronize with the client over FD\n" -" --help display this help and exit\n" -" --version output version information and exit\n", - me.c_str()); - ToolSupport::UsageTail(me); -} - -int ExceptionSwallowerMain(int argc, char* argv[]) { - const std::string me(basename(argv[0])); - - enum OptionFlags { - // Long options without short equivalents. - kOptionLastChar = 255, - kOptionSocketFD, - - // Standard options. - kOptionHelp = -2, - kOptionVersion = -3, - }; - - struct { - int socket_fd; - } options = {}; - options.socket_fd = -1; - - const option long_options[] = { - {"socket-fd", required_argument, nullptr, kOptionSocketFD}, - {"help", no_argument, nullptr, kOptionHelp}, - {"version", no_argument, nullptr, kOptionVersion}, - {nullptr, 0, nullptr, 0}, - }; - - int opt; - while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) { - switch (opt) { - case kOptionSocketFD: - if (!StringToNumber(optarg, &options.socket_fd) || - options.socket_fd <= STDERR_FILENO) { - ToolSupport::UsageHint(me, "--socket-fd requires a file descriptor"); - return EXIT_FAILURE; - } - break; - case kOptionHelp: { - Usage(me); - return EXIT_SUCCESS; - } - case kOptionVersion: { - ToolSupport::Version(me); - return EXIT_SUCCESS; - } - default: { - ToolSupport::UsageHint(me, nullptr); - return EXIT_FAILURE; - } - } - } - argc -= optind; - argv += optind; - - if (options.socket_fd < 0) { - ToolSupport::UsageHint(me, "--socket-fd is required"); - return EXIT_FAILURE; - } - - if (argc) { - ToolSupport::UsageHint(me, nullptr); - return EXIT_FAILURE; - } - - CloseStdinAndStdout(); - - // Build a service name. Include the PID of the client at the other end of the - // socket, so that the service name has a meaningful relation back to the - // client that started this server process. A simple getppid() won’t do - // because the client started this process with a double-fork(). - pid_t peer_pid; - socklen_t peer_pid_size = base::checked_cast(sizeof(peer_pid)); - PCHECK(getsockopt(options.socket_fd, - SOL_LOCAL, - LOCAL_PEERPID, - &peer_pid, - &peer_pid_size) == 0) - << "getsockopt"; - CHECK_EQ(peer_pid_size, sizeof(peer_pid)); - - std::string service_name = - base::StringPrintf("org.chromium.crashpad.test.exception_swallower.%d.%s", - peer_pid, - RandomString().c_str()); - - base::mac::ScopedMachReceiveRight receive_right( - BootstrapCheckIn(service_name)); - CHECK(receive_right.is_valid()); - - // Tell the client that the service has been checked in, providing the - // service name. - uint8_t service_name_size = base::checked_cast(service_name.size()); - CheckedWriteFile( - options.socket_fd, &service_name_size, sizeof(service_name_size)); - CheckedWriteFile( - options.socket_fd, service_name.c_str(), service_name.size()); - - ExceptionHandlerServer exception_handler_server(std::move(receive_right), - true); - - EOFWatcherThread eof_watcher_thread(options.socket_fd, - &exception_handler_server); - eof_watcher_thread.Start(); - - // This runs until stopped by eof_watcher_thread. - SwallowingExceptionHandler swallowing_exception_handler; - exception_handler_server.Run(&swallowing_exception_handler); - - eof_watcher_thread.Join(); - - return EXIT_SUCCESS; -} - -} // namespace -} // namespace crashpad - -int main(int argc, char* argv[]) { - return crashpad::ExceptionSwallowerMain(argc, argv); -} diff --git a/test/multiprocess_posix.cc b/test/multiprocess_posix.cc index 2a8a15cc..c638b48a 100644 --- a/test/multiprocess_posix.cc +++ b/test/multiprocess_posix.cc @@ -74,13 +74,12 @@ void Multiprocess::Run() { ASSERT_NO_FATAL_FAILURE(PreFork()); #if defined(OS_MACOSX) - // If the child is expected to crash, set up an exception swallower process - // to swallow the exception instead of allowing it to be seen by the system’s - // crash reporter. - const bool swallow_exceptions = - reason_ == kTerminationSignal && Signals::IsCrashSignal(code_); - if (swallow_exceptions) { - ExceptionSwallower::Parent_PrepareForCrashingChild(); + // If the child is expected to crash, set up an exception swallower to swallow + // the exception instead of allowing it to be seen by the system’s crash + // reporter. + std::unique_ptr exception_swallower; + if (reason_ == kTerminationSignal && Signals::IsCrashSignal(code_)) { + exception_swallower.reset(new ExceptionSwallower()); } #endif // OS_MACOSX @@ -141,8 +140,8 @@ void Multiprocess::Run() { } } else { #if defined(OS_MACOSX) - if (swallow_exceptions) { - ExceptionSwallower::Child_SwallowExceptions(); + if (exception_swallower.get()) { + ExceptionSwallower::SwallowExceptions(); } #endif // OS_MACOSX diff --git a/test/test.gyp b/test/test.gyp index f6c1f251..79e0671a 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -84,7 +84,7 @@ 'conditions': [ ['OS=="mac"', { 'dependencies': [ - 'crashpad_exception_swallower', + '../handler/handler.gyp:crashpad_handler_lib', ], 'link_settings': { 'libraries': [ @@ -146,27 +146,4 @@ ], }, ], - 'conditions': [ - ['OS=="mac"', { - 'targets': [ - { - 'target_name': 'crashpad_exception_swallower', - 'type': 'executable', - 'dependencies': [ - '../compat/compat.gyp:crashpad_compat', - '../handler/handler.gyp:crashpad_handler_lib', - '../third_party/mini_chromium/mini_chromium.gyp:base', - '../tools/tools.gyp:crashpad_tool_support', - '../util/util.gyp:crashpad_util', - ], - 'include_dirs': [ - '..', - ], - 'sources': [ - 'mac/exception_swallower_exe.cc', - ], - }, - ], - }], - ], } From e935375fe9552fabd093f35027d0980b055397f4 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Mon, 20 Nov 2017 16:03:05 -0500 Subject: [PATCH 028/326] =?UTF-8?q?Don=E2=80=99t=20check=20out=20lldb=20(o?= =?UTF-8?q?r=20llvm=20or=20clang)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are large, not pinned (floating at HEAD), and we’re not currently using them. Change-Id: I550f832aeb42db8404fdb764f78e83136a2a7ef6 Reviewed-on: https://chromium-review.googlesource.com/779668 Reviewed-by: Scott Graham Commit-Queue: Mark Mentovai --- .gitignore | 1 - DEPS | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/.gitignore b/.gitignore index 43ab9c0a..3311c2ef 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ /out /third_party/gtest/gtest /third_party/gyp/gyp -/third_party/llvm /third_party/mini_chromium/mini_chromium /third_party/zlib/zlib /xcodebuild diff --git a/DEPS b/DEPS index 26aa6bf4..2e40af5e 100644 --- a/DEPS +++ b/DEPS @@ -26,16 +26,6 @@ deps = { 'crashpad/third_party/gyp/gyp': Var('chromium_git') + '/external/gyp@' + 'f72586209ecbf70b71ce690f2182ebe51669cbb3', - - # TODO(scottmg): Consider pinning these. For now, we don't have any particular - # reason to do so. - 'crashpad/third_party/llvm': - Var('chromium_git') + '/external/llvm.org/llvm.git@HEAD', - 'crashpad/third_party/llvm/tools/clang': - Var('chromium_git') + '/external/llvm.org/clang.git@HEAD', - 'crashpad/third_party/llvm/tools/lldb': - Var('chromium_git') + '/external/llvm.org/lldb.git@HEAD', - 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + 'dd0c3e9680ae3c4c22f2221a2a75e48dd4a562ec', From 20e5aba1aff797bae2e01b08d4099006ab068b77 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Mon, 20 Nov 2017 16:57:43 -0500 Subject: [PATCH 029/326] URL cleanups: switch to HTTPS, fix dead ones, use canonical ones Change-Id: I4b247d7fae1a212350f8ffcf2bf5ba1fa730f5c1 Reviewed-on: https://chromium-review.googlesource.com/780339 Reviewed-by: Scott Graham Commit-Queue: Mark Mentovai --- client/crashpad_client_win.cc | 6 ++--- compat/non_win/dbghelp.h | 19 ++++++++-------- compat/win/winnt.h | 4 ++-- doc/developing.md | 22 +++++++++---------- doc/status.md | 2 +- handler/win/crash_other_program.cc | 5 ++--- infra/config/cq.cfg | 4 ++-- minidump/minidump_context.h | 2 +- snapshot/handle_snapshot.h | 2 +- snapshot/mac/process_reader.cc | 6 ++--- .../crashreporterclient.proctype | 2 +- snapshot/posix/timezone_test.cc | 8 +++---- snapshot/win/pe_image_reader.cc | 2 +- snapshot/win/pe_image_resource_reader.cc | 2 +- snapshot/win/process_reader_win.cc | 8 +++---- snapshot/win/process_snapshot_win.cc | 17 +++++++------- snapshot/win/system_snapshot_win_test.cc | 8 +++---- third_party/mini_chromium/README.crashpad | 2 +- third_party/zlib/README.crashpad | 2 +- util/BUILD.gn | 5 ++++- util/mac/mac_util.cc | 6 ++--- util/misc/pdb_structures.h | 7 +++--- util/net/http_body_gzip_test.cc | 2 +- util/net/http_transport_win.cc | 3 +-- util/stdlib/objc.h | 2 +- util/stdlib/strnlen.cc | 2 +- util/win/command_line.cc | 2 +- util/win/command_line_test.cc | 2 +- util/win/handle.h | 4 ++-- util/win/nt_internals.h | 2 +- util/win/process_info.h | 6 ++--- util/win/process_structs.h | 14 ++++++------ util/win/registration_protocol_win.cc | 2 +- util/win/xp_compat.h | 4 ++-- 34 files changed, 92 insertions(+), 94 deletions(-) diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc index 0da0c228..7d9c1fb2 100644 --- a/client/crashpad_client_win.cc +++ b/client/crashpad_client_win.cc @@ -750,9 +750,9 @@ void CrashpadClient::DumpWithoutCrash(const CONTEXT& context) { // We include a fake exception and use a code of '0x517a7ed' (something like // "simulated") so that it's relatively obvious in windbg that it's not // actually an exception. Most values in - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363082.aspx have - // some of the top nibble set, so we make sure to pick a value that doesn't, - // so as to be unlikely to conflict. + // https://msdn.microsoft.com/library/aa363082.aspx have some of the top + // nibble set, so we make sure to pick a value that doesn't, so as to be + // unlikely to conflict. constexpr uint32_t kSimulatedExceptionCode = 0x517a7ed; EXCEPTION_RECORD record = {}; record.ExceptionCode = kSimulatedExceptionCode; diff --git a/compat/non_win/dbghelp.h b/compat/non_win/dbghelp.h index a8d5a0f3..5ce88b88 100644 --- a/compat/non_win/dbghelp.h +++ b/compat/non_win/dbghelp.h @@ -564,16 +564,15 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MODULE { //! Microsoft //! Symbol and Type Information, section 7.2, “Debug Information Format” - //! for a list of debug information formats, and Undocumented - //! Windows 2000 Secrets, Windows 2000 Debugging Support/Microsoft Symbol - //! File Internals/CodeView Subsections for an in-depth description of the - //! CodeView 4.1 format. Signatures seen in the wild include “NB09” - //! (0x3930424e) for CodeView 4.1 and “NB11” (0x3131424e) for CodeView 5.0. - //! This form of debugging information within the module, as opposed to a link - //! to an external `.pdb` file, is chosen by building with `/Z7` in Visual - //! Studio 6.0 (1998) and earlier. This embedded form of debugging information - //! is now considered obsolete. + //! for a list of debug information formats, and Undocumented Windows 2000 + //! Secrets, Windows 2000 Debugging Support/Microsoft Symbol File + //! Internals/CodeView Subsections for an in-depth description of the CodeView + //! 4.1 format. Signatures seen in the wild include “NB09” (0x3930424e) for + //! CodeView 4.1 and “NB11” (0x3131424e) for CodeView 5.0. This form of + //! debugging information within the module, as opposed to a link to an + //! external `.pdb` file, is chosen by building with `/Z7` in Visual Studio + //! 6.0 (1998) and earlier. This embedded form of debugging information is now + //! considered obsolete. //! //! On Windows, the CodeView record is taken from a module’s //! IMAGE_DEBUG_DIRECTORY entry whose Type field has the value diff --git a/compat/win/winnt.h b/compat/win/winnt.h index 6a5e0164..34ea80f9 100644 --- a/compat/win/winnt.h +++ b/compat/win/winnt.h @@ -18,8 +18,8 @@ // include_next #include <../um/winnt.h> -// https://msdn.microsoft.com/en-us/library/windows/desktop/aa373184.aspx: -// "Note that this structure definition was accidentally omitted from WinNT.h." +// https://msdn.microsoft.com/library/aa373184.aspx: "Note that this structure +// definition was accidentally omitted from WinNT.h." struct PROCESSOR_POWER_INFORMATION { ULONG Number; ULONG MaxMhz; diff --git a/doc/developing.md b/doc/developing.md index 6b2a9e4e..4dd74782 100644 --- a/doc/developing.md +++ b/doc/developing.md @@ -22,7 +22,7 @@ limitations under the License. ## Introduction -Crashpad is a [Chromium project](https://dev.chromium.org/Home). Most of its +Crashpad is a [Chromium project](https://www.chromium.org/Home). Most of its development practices follow Chromium’s. In order to function on its own in other projects, Crashpad uses [mini_chromium](https://chromium.googlesource.com/chromium/mini_chromium/), a @@ -43,9 +43,9 @@ the `$PATH` environment variable: C++ support and the Windows SDK. MSVS 2015 and MSVS 2017 are both supported. Some tests also require the CDB debugger, installed with [Debugging Tools for - Windows](https://msdn.microsoft.com/library/windows/hardware/ff551063.aspx). + Windows](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/). * Chromium’s - [depot_tools](https://dev.chromium.org/developers/how-tos/depottools). + [depot_tools](https://www.chromium.org/developers/how-tos/depottools). * [Git](https://git-scm.com/). This is provided by Xcode on macOS and by depot_tools on Windows. * [Python](https://www.python.org/). This is provided by the operating system @@ -57,12 +57,12 @@ The main source code repository is a Git repository hosted at https://chromium.googlesource.com/crashpad/crashpad. Although it is possible to check out this repository directly with `git clone`, Crashpad’s dependencies are managed by -[`gclient`](https://dev.chromium.org/developers/how-tos/depottools#TOC-gclient) +[`gclient`](https://www.chromium.org/developers/how-tos/depottools#TOC-gclient) instead of Git submodules, so to work on Crashpad, it is best to use `fetch` to get the source code. `fetch` and `gclient` are part of the -[depot_tools](https://dev.chromium.org/developers/how-tos/depottools). There’s +[depot_tools](https://www.chromium.org/developers/how-tos/depottools). There’s no need to install them separately. ### Initial Checkout @@ -106,7 +106,7 @@ $ ninja -C out/Debug ``` Ninja is part of the -[depot_tools](https://dev.chromium.org/developers/how-tos/depottools). There’s +[depot_tools](https://www.chromium.org/developers/how-tos/depottools). There’s no need to install it separately. ### Android @@ -200,7 +200,7 @@ $ python build/run_tests.py out/Debug On Windows, `end_to_end_test.py` requires the CDB debugger, installed with [Debugging Tools for -Windows](https://msdn.microsoft.com/library/windows/hardware/ff551063.aspx). +Windows](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/). This can be installed either as part of the [Windows Driver Kit](https://go.microsoft.com/fwlink/p?LinkID=239721) or the [Windows SDK](https://go.microsoft.com/fwlink/p?LinkID=271979). If the Windows SDK has @@ -243,7 +243,7 @@ device:/data/local/tmp $ CRASHPAD_TEST_DATA_ROOT=crashpad_test_data_root \ ## Contributing Crashpad’s contribution process is very similar to [Chromium’s contribution -process](https://dev.chromium.org/developers/contributing-code). +process](https://www.chromium.org/developers/contributing-code). ### Code Review @@ -256,7 +256,7 @@ must be sent to an appropriate reviewer, with a Cc sent to file specifies this environment to `git-cl`. `git-cl` is part of the -[depot_tools](https://dev.chromium.org/developers/how-tos/depottools). There’s +[depot_tools](https://www.chromium.org/developers/how-tos/depottools). There’s no need to install it separately. ``` @@ -282,7 +282,7 @@ patch set with `git cl upload` and let your reviewer know you’ve addressed the feedback. The most recently uploaded patch set on a review may be tested on a [try -server](https://dev.chromium.org/developers/testing/try-server-usage) by running +server](https://www.chromium.org/developers/testing/try-server-usage) by running `git cl try` or by clicking the “CQ Dry Run” button in Gerrit. These set the “Commit-Queue: +1” label. This does not mean that the patch will be committed, but the try server and commit queue share infrastructure and a Gerrit label. The @@ -294,7 +294,7 @@ Crashpad and Chromium committers. After code review is complete and “Code-Review: +1” has been received from all reviewers, the patch can be submitted to Crashpad’s [commit -queue](https://dev.chromium.org/developers/testing/commit-queue) by clicking the +queue](https://www.chromium.org/developers/testing/commit-queue) by clicking the “Submit to CQ” button in Gerrit. This sets the “Commit-Queue: +2” label, which tests the patch on the try server before landing it. Commit queue access is available to Crashpad and Chromium committers. diff --git a/doc/status.md b/doc/status.md index be4503a1..dcb6e62e 100644 --- a/doc/status.md +++ b/doc/status.md @@ -21,7 +21,7 @@ limitations under the License. Crashpad currently consists of a crash-reporting client and some related tools for macOS and Windows. The core client work for both platforms is substantially complete. Crashpad became the crash reporter client for -[Chromium](https://dev.chromium.org/Home) on macOS as of [March +[Chromium](https://www.chromium.org/Home) on macOS as of [March 2015](https://chromium.googlesource.com/chromium/src/\+/d413b2dcb54d523811d386f1ff4084f677a6d089), and on Windows as of [November 2015](https://chromium.googlesource.com/chromium/src/\+/cfa5b01bb1d06bf96967bd37e21a44752801948c). diff --git a/handler/win/crash_other_program.cc b/handler/win/crash_other_program.cc index ddad4c53..2b62aea2 100644 --- a/handler/win/crash_other_program.cc +++ b/handler/win/crash_other_program.cc @@ -52,9 +52,8 @@ bool CrashAndDumpTarget(const CrashpadClient& client, HANDLE process) { do { if (te32.th32OwnerProcessID == target_pid) { // We set the thread priority of "Thread1" to a non-default value before - // going to sleep. Dump and blame this thread. For an explanation of - // "9", see - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100.aspx. + // going to sleep. Dump and blame this thread. For an explanation of "9", + // see https://msdn.microsoft.com/library/ms685100.aspx. if (te32.tpBasePri == 9) { ScopedKernelHANDLE thread( OpenThread(kXPThreadAllAccess, false, te32.th32ThreadID)); diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg index 32f3854d..d00ef141 100644 --- a/infra/config/cq.cfg +++ b/infra/config/cq.cfg @@ -37,8 +37,8 @@ verifiers { builders { name: "crashpad_try_mac_rel" } builders { name: "crashpad_try_win_x64_dbg" } builders { name: "crashpad_try_win_x64_rel" } - # crbug.com/743139 - disabled until we can move these to - # swarming, at which point we can just remove them. + # https://crbug.com/743139 - disabled until we can move these to swarming, + # at which point we can just remove them. #builders { name: "crashpad_try_win_x86_dbg" } #builders { name: "crashpad_try_win_x86_rel" } builders { name: "crashpad_try_win_x86_wow64_dbg" } diff --git a/minidump/minidump_context.h b/minidump/minidump_context.h index f558832f..88db5414 100644 --- a/minidump/minidump_context.h +++ b/minidump/minidump_context.h @@ -26,7 +26,7 @@ namespace crashpad { //! \brief Architecture-independent flags for `context_flags` fields in Minidump //! context structures. // -// http://zachsaw.blogspot.com/2010/11/wow64-bug-getthreadcontext-may-return.html#c5639760895973344002 +// https://zachsaw.blogspot.com/2010/11/wow64-bug-getthreadcontext-may-return.html#c5639760895973344002 enum MinidumpContextFlags : uint32_t { //! \brief The thread was executing a trap handler in kernel mode //! (`CONTEXT_EXCEPTION_ACTIVE`). diff --git a/snapshot/handle_snapshot.h b/snapshot/handle_snapshot.h index 1346b41d..eee7e7e4 100644 --- a/snapshot/handle_snapshot.h +++ b/snapshot/handle_snapshot.h @@ -38,7 +38,7 @@ struct HandleSnapshot { //! \brief The ACCESS_MASK for the handle in this process. //! //! See - //! http://blogs.msdn.com/b/openspecification/archive/2010/04/01/about-the-access-mask-structure.aspx + //! https://blogs.msdn.microsoft.com/openspecification/2010/04/01/about-the-access_mask-structure/ //! for more information. uint32_t granted_access; diff --git a/snapshot/mac/process_reader.cc b/snapshot/mac/process_reader.cc index d38ab144..6ddcff05 100644 --- a/snapshot/mac/process_reader.cc +++ b/snapshot/mac/process_reader.cc @@ -681,9 +681,9 @@ void ProcessReader::LocateRedZone(mach_vm_address_t* const start_address, const unsigned int user_tag) { #if defined(ARCH_CPU_X86_FAMILY) if (Is64Bit()) { - // x86_64 has a red zone. See AMD64 ABI 0.99.6, - // http://www.x86-64.org/documentation/abi.pdf, section 3.2.2, “The Stack - // Frame”. + // x86_64 has a red zone. See AMD64 ABI 0.99.8, + // https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-r252.pdf#page=19, + // section 3.2.2, “The Stack Frame”. constexpr mach_vm_size_t kRedZoneSize = 128; mach_vm_address_t red_zone_base = *start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0; diff --git a/snapshot/mac/process_types/crashreporterclient.proctype b/snapshot/mac/process_types/crashreporterclient.proctype index fb518522..398bd639 100644 --- a/snapshot/mac/process_types/crashreporterclient.proctype +++ b/snapshot/mac/process_types/crashreporterclient.proctype @@ -13,7 +13,7 @@ // limitations under the License. // The name of this file was chosen based on -// http://llvm.org/svn/llvm-project/llvm/trunk/lib/Support/PrettyStackTrace.cpp. +// https://llvm.org/svn/llvm-project/llvm/trunk/lib/Support/PrettyStackTrace.cpp. // The name of the structure it describes was chosen based on that file as well // as 10.9.2 cups-372.2/cups/backend/usb-darwin.c. That file also provided the // names and types of the fields in the structure. diff --git a/snapshot/posix/timezone_test.cc b/snapshot/posix/timezone_test.cc index 01bdff50..4ba14f1c 100644 --- a/snapshot/posix/timezone_test.cc +++ b/snapshot/posix/timezone_test.cc @@ -88,7 +88,7 @@ TEST(TimeZone, Basic) { // In contemporary usage, most time zones have an integer hour offset from // UTC, although several are at a half-hour offset, and two are at 15-minute // offsets. Throughout history, other variations existed. See - // http://www.timeanddate.com/time/time-zones-interesting.html. + // https://www.timeanddate.com/time/time-zones-interesting.html. EXPECT_EQ(standard_offset_seconds % (15 * 60), 0) << "standard_offset_seconds " << standard_offset_seconds; @@ -100,9 +100,9 @@ TEST(TimeZone, Basic) { << "daylight_offset_seconds " << daylight_offset_seconds; // In contemporary usage, dst_delta_seconds will almost always be one hour, - // except for Lord Howe Island, Australia, which uses a 30-minute - // delta. Throughout history, other variations existed. See - // http://www.timeanddate.com/time/dst/#brief. + // except for Lord Howe Island, Australia, which uses a 30-minute delta. + // Throughout history, other variations existed. See + // https://www.timeanddate.com/time/dst/. int dst_delta_seconds = daylight_offset_seconds - standard_offset_seconds; if (dst_delta_seconds != 60 * 60 && dst_delta_seconds != 30 * 60) { FAIL() << "dst_delta_seconds " << dst_delta_seconds; diff --git a/snapshot/win/pe_image_reader.cc b/snapshot/win/pe_image_reader.cc index aba7f588..e705bb51 100644 --- a/snapshot/win/pe_image_reader.cc +++ b/snapshot/win/pe_image_reader.cc @@ -235,7 +235,7 @@ bool PEImageReader::VSFixedFileInfo( } // This structure is not declared anywhere in the SDK, but is documented at - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms647001.aspx. + // https://msdn.microsoft.com/library/ms647001.aspx. struct VS_VERSIONINFO { WORD wLength; WORD wValueLength; diff --git a/snapshot/win/pe_image_resource_reader.cc b/snapshot/win/pe_image_resource_reader.cc index a010276a..f3739452 100644 --- a/snapshot/win/pe_image_resource_reader.cc +++ b/snapshot/win/pe_image_resource_reader.cc @@ -165,7 +165,7 @@ uint32_t PEImageResourceReader::GetEntryFromResourceDirectoryByLanguage( return 0; } - // https://msdn.microsoft.com/en-us/library/cc194810.aspx + // https://msdn.microsoft.com/library/cc194810.aspx // // TODO(mark): It seems like FindResourceEx() might do something more complex. // It would be best to mimic its behavior. diff --git a/snapshot/win/process_reader_win.cc b/snapshot/win/process_reader_win.cc index 6041ec2d..73a0ca78 100644 --- a/snapshot/win/process_reader_win.cc +++ b/snapshot/win/process_reader_win.cc @@ -374,9 +374,9 @@ void ProcessReaderWin::ReadThreadData(bool is_64_reading_32) { // TODO(scottmg): I believe we could reverse engineer the PriorityClass from // the Priority, BasePriority, and - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100 . - // MinidumpThreadWriter doesn't handle it yet in any case, so investigate - // both of those at the same time if it's useful. + // https://msdn.microsoft.com/library/ms685100.aspx. MinidumpThreadWriter + // doesn't handle it yet in any case, so investigate both of those at the + // same time if it's useful. thread.priority_class = NORMAL_PRIORITY_CLASS; thread.priority = thread_info.Priority; @@ -403,7 +403,7 @@ void ProcessReaderWin::ReadThreadData(bool is_64_reading_32) { WinVMAddress limit = 0; // If we're reading a WOW64 process, then the TIB we just retrieved is the // x64 one. The first word of the x64 TIB points at the x86 TIB. See - // https://msdn.microsoft.com/en-us/library/dn424783.aspx + // https://msdn.microsoft.com/library/dn424783.aspx. if (is_64_reading_32) { process_types::NT_TIB tib32; thread.teb_address = tib.Wow64Teb; diff --git a/snapshot/win/process_snapshot_win.cc b/snapshot/win/process_snapshot_win.cc index 180b2cb7..289f50c0 100644 --- a/snapshot/win/process_snapshot_win.cc +++ b/snapshot/win/process_snapshot_win.cc @@ -265,12 +265,11 @@ void ProcessSnapshotWin::InitializeModules() { } void ProcessSnapshotWin::InitializeUnloadedModules() { - // As documented by https://msdn.microsoft.com/en-us/library/cc678403.aspx - // we can retrieve the location for our unload events, and use that address in - // the target process. Unfortunately, this of course only works for - // 64-reading-64 and 32-reading-32, so at the moment, we simply do not - // retrieve unloaded modules for 64-reading-32. See - // https://crashpad.chromium.org/bug/89. + // As documented by https://msdn.microsoft.com/library/cc678403.aspx, we can + // retrieve the location for our unload events, and use that address in the + // target process. Unfortunately, this of course only works for 64-reading-64 + // and 32-reading-32, so at the moment, we simply do not retrieve unloaded + // modules for 64-reading-32. See https://crashpad.chromium.org/bug/89. #if defined(ARCH_CPU_X86_64) if (!process_reader_.Is64Bit()) { @@ -516,9 +515,9 @@ void ProcessSnapshotWin::AddMemorySnapshotForLdrLIST_ENTRY( WinVMSize ProcessSnapshotWin::DetermineSizeOfEnvironmentBlock( WinVMAddress start_of_environment_block) { - // http://blogs.msdn.com/b/oldnewthing/archive/2010/02/03/9957320.aspx On - // newer OSs there's no stated limit, but in practice grabbing 32k characters - // should be more than enough. + // https://blogs.msdn.microsoft.com/oldnewthing/20100203-00/?p=15083: On newer + // OSs there's no stated limit, but in practice grabbing 32k characters should + // be more than enough. std::wstring env_block; env_block.resize(32768); WinVMSize bytes_read = process_reader_.ReadAvailableMemory( diff --git a/snapshot/win/system_snapshot_win_test.cc b/snapshot/win/system_snapshot_win_test.cc index 5f469a3f..4c994971 100644 --- a/snapshot/win/system_snapshot_win_test.cc +++ b/snapshot/win/system_snapshot_win_test.cc @@ -130,7 +130,7 @@ TEST_F(SystemSnapshotWinTest, TimeZone) { // In contemporary usage, most time zones have an integer hour offset from // UTC, although several are at a half-hour offset, and two are at 15-minute // offsets. Throughout history, other variations existed. See - // http://www.timeanddate.com/time/time-zones-interesting.html. + // https://www.timeanddate.com/time/time-zones-interesting.html. EXPECT_EQ(standard_offset_seconds % (15 * 60), 0) << "standard_offset_seconds " << standard_offset_seconds; @@ -142,9 +142,9 @@ TEST_F(SystemSnapshotWinTest, TimeZone) { << "daylight_offset_seconds " << daylight_offset_seconds; // In contemporary usage, dst_delta_seconds will almost always be one hour, - // except for Lord Howe Island, Australia, which uses a 30-minute - // delta. Throughout history, other variations existed. See - // http://www.timeanddate.com/time/dst/#brief. + // except for Lord Howe Island, Australia, which uses a 30-minute delta. + // Throughout history, other variations existed. See + // https://www.timeanddate.com/time/dst/. int dst_delta_seconds = daylight_offset_seconds - standard_offset_seconds; if (dst_delta_seconds != 60 * 60 && dst_delta_seconds != 30 * 60) { FAIL() << "dst_delta_seconds " << dst_delta_seconds; diff --git a/third_party/mini_chromium/README.crashpad b/third_party/mini_chromium/README.crashpad index 1fa3c4d9..37d507bf 100644 --- a/third_party/mini_chromium/README.crashpad +++ b/third_party/mini_chromium/README.crashpad @@ -8,7 +8,7 @@ Security Critical: yes Description: mini_chromium is a small collection of useful low-level (“base”) routines from -the Chromium open-source project at http://www.chromium.org/. Chromium is +the Chromium open-source project at https://www.chromium.org/Home. Chromium is large, sprawling, full of dependencies, and a web browser. mini_chromium is small, self-contained, and a library. mini_chromium is especially useful as a dependency of other code that wishes to use Chromium’s base routines. diff --git a/third_party/zlib/README.crashpad b/third_party/zlib/README.crashpad index 83f47e31..8a9533d3 100644 --- a/third_party/zlib/README.crashpad +++ b/third_party/zlib/README.crashpad @@ -1,6 +1,6 @@ Name: zlib Short Name: zlib -URL: http://zlib.net/ +URL: https://zlib.net/ Revision: See zlib/README.chromium License: zlib License File: zlib/LICENSE diff --git a/util/BUILD.gn b/util/BUILD.gn index 1175de3e..34b30361 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -277,11 +277,14 @@ static_library("util") { if (is_win) { # There's no ml.exe yet in cross builds, so provide broken-but-not-asm # versions of the functions defined in .asm files. + # # CaptureContext() in capture_context_broken.cc just calls CHECK(false). # SafeTerminateProcess() in safe_terminate_process.cc just calls regular # TerminateProcess() without the protection against broken third-party # patching of TerminateProcess(). - # TODO(thakis): Use the .asm file in cross builds somehow, crbug.com/762167 + # + # TODO(thakis): Use the .asm file in cross builds somehow, + # https://crbug.com/762167. if (host_os == "win") { sources += [ "win/capture_context.asm", diff --git a/util/mac/mac_util.cc b/util/mac/mac_util.cc index 7c51cc2e..5f79701b 100644 --- a/util/mac/mac_util.cc +++ b/util/mac/mac_util.cc @@ -66,9 +66,9 @@ int DarwinMajorVersion() { // base::OperatingSystemVersionNumbers calls Gestalt(), which is a // higher-level function than is needed. It might perform unnecessary // operations. On 10.6, it was observed to be able to spawn threads (see - // http://crbug.com/53200). It might also read files or perform other blocking - // operations. Actually, nobody really knows for sure just what Gestalt() - // might do, or what it might be taught to do in the future. + // https://crbug.com/53200). It might also read files or perform other + // blocking operations. Actually, nobody really knows for sure just what + // Gestalt() might do, or what it might be taught to do in the future. // // uname(), on the other hand, is implemented as a simple series of sysctl() // system calls to obtain the relevant data from the kernel. The data is diff --git a/util/misc/pdb_structures.h b/util/misc/pdb_structures.h index 218a7632..d0cc9a32 100644 --- a/util/misc/pdb_structures.h +++ b/util/misc/pdb_structures.h @@ -30,10 +30,9 @@ namespace crashpad { //! //! For more information about this structure and format, see Matching -//! Debug Information, PDB Files, and Undocumented -//! Windows 2000 Secrets, Windows 2000 Debugging Support/Microsoft Symbol -//! File Internals/CodeView Subsections. +//! Debug Information, PDB Files, and Undocumented Windows 2000 +//! Secrets, Windows 2000 Debugging Support/Microsoft Symbol File +//! Internals/CodeView Subsections. //! //! \sa IMAGE_DEBUG_MISC struct CodeViewRecordPDB20 { diff --git a/util/net/http_body_gzip_test.cc b/util/net/http_body_gzip_test.cc index 85887502..9dafb373 100644 --- a/util/net/http_body_gzip_test.cc +++ b/util/net/http_body_gzip_test.cc @@ -85,7 +85,7 @@ void TestGzipDeflateInflate(const std::string& string) { // 8-byte trailer. constexpr size_t kGzipHeaderSize = 18; - // Per http://www.zlib.net/zlib_tech.html, in the worst case, zlib will store + // Per https://zlib.net/zlib_tech.html, in the worst case, zlib will store // uncompressed data as-is, at an overhead of 5 bytes per 16384-byte block. // Zero-length input will “compress” to a 2-byte zlib stream. Add the overhead // of the gzip wrapper, assuming no optional fields are present. diff --git a/util/net/http_transport_win.cc b/util/net/http_transport_win.cc index dc3eeeb7..18d343cc 100644 --- a/util/net/http_transport_win.cc +++ b/util/net/http_transport_win.cc @@ -169,8 +169,7 @@ bool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) { url_components.dwUrlPathLength = 1; url_components.dwExtraInfoLength = 1; std::wstring url_wide(base::UTF8ToUTF16(url())); - // dwFlags = ICU_REJECT_USERPWD fails on XP. See "Community Additions" at: - // https://msdn.microsoft.com/en-us/library/aa384092.aspx + // dwFlags = ICU_REJECT_USERPWD fails on XP. if (!WinHttpCrackUrl( url_wide.c_str(), 0, 0, &url_components)) { LOG(ERROR) << WinHttpMessage("WinHttpCrackUrl"); diff --git a/util/stdlib/objc.h b/util/stdlib/objc.h index 0b44bbab..faaa4538 100644 --- a/util/stdlib/objc.h +++ b/util/stdlib/objc.h @@ -22,7 +22,7 @@ // In order for the @NO and @YES literals to work, NO and YES must be defined as // __objc_no and __objc_yes. See -// http://llvm.org/releases/3.1/tools/clang/docs/ObjectiveCLiterals.html . +// https://clang.llvm.org/docs/ObjectiveCLiterals.html. // // NO and YES are defined properly for this purpose in the 10.8 SDK, but not in // earlier SDKs. Because this code is never expected to be compiled with a diff --git a/util/stdlib/strnlen.cc b/util/stdlib/strnlen.cc index 3016bf63..a238728f 100644 --- a/util/stdlib/strnlen.cc +++ b/util/stdlib/strnlen.cc @@ -17,7 +17,7 @@ #if defined(OS_MACOSX) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 -// Redeclare a method only available on OSX 10.7+ to suppress a +// Redeclare a method only available on Mac OS X 10.7 and later to suppress a // -Wpartial-availability warning. extern "C" { size_t strnlen(const char* string, size_t max_length); diff --git a/util/win/command_line.cc b/util/win/command_line.cc index 930c83c8..c015eed7 100644 --- a/util/win/command_line.cc +++ b/util/win/command_line.cc @@ -19,7 +19,7 @@ namespace crashpad { // Ref: -// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx +// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ void AppendCommandLineArgument(const std::wstring& argument, std::wstring* command_line) { if (!command_line->empty()) { diff --git a/util/win/command_line_test.cc b/util/win/command_line_test.cc index e7761f12..025ef8a7 100644 --- a/util/win/command_line_test.cc +++ b/util/win/command_line_test.cc @@ -53,7 +53,7 @@ void AppendCommandLineArgumentTest(size_t argc, const wchar_t* const argv[]) { TEST(CommandLine, AppendCommandLineArgument) { // Most of these test cases come from - // http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx, + // https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/, // which was also a reference for the implementation of // AppendCommandLineArgument(). diff --git a/util/win/handle.h b/util/win/handle.h index 8a630690..951302e2 100644 --- a/util/win/handle.h +++ b/util/win/handle.h @@ -24,7 +24,7 @@ namespace crashpad { //! `HANDLE` is a `typedef` for `void *`, but kernel `HANDLE` values aren’t //! pointers to anything. Only 32 bits of kernel `HANDLE`s are significant, even //! in 64-bit processes on 64-bit operating systems. See Interprocess +//! href="https://msdn.microsoft.com/library/aa384203.aspx">Interprocess //! Communication Between 32-bit and 64-bit Applications. //! //! This function safely converts a kernel `HANDLE` to an `int` similarly to a @@ -45,7 +45,7 @@ int HandleToInt(HANDLE handle); //! `HANDLE` is a `typedef` for `void *`, but kernel `HANDLE` values aren’t //! pointers to anything. Only 32 bits of kernel `HANDLE`s are significant, even //! in 64-bit processes on 64-bit operating systems. See Interprocess +//! href="https://msdn.microsoft.com/library/aa384203.aspx">Interprocess //! Communication Between 32-bit and 64-bit Applications. //! //! This function safely convert an `int` to a kernel `HANDLE` similarly to a diff --git a/util/win/nt_internals.h b/util/win/nt_internals.h index a14678fc..a2a88b77 100644 --- a/util/win/nt_internals.h +++ b/util/win/nt_internals.h @@ -76,7 +76,7 @@ NTSTATUS NtSuspendProcess(HANDLE handle); NTSTATUS NtResumeProcess(HANDLE handle); -// From https://msdn.microsoft.com/en-us/library/cc678403(v=vs.85).aspx. +// From https://msdn.microsoft.com/library/cc678403.aspx. template struct RTL_UNLOAD_EVENT_TRACE { typename Traits::Pointer BaseAddress; diff --git a/util/win/process_info.h b/util/win/process_info.h index be968f92..58921c56 100644 --- a/util/win/process_info.h +++ b/util/win/process_info.h @@ -72,7 +72,7 @@ class ProcessInfo { //! \brief The `ACCESS_MASK` for the handle in this process. //! //! See - //! http://blogs.msdn.com/b/openspecification/archive/2010/04/01/about-the-access-mask-structure.aspx + //! https://blogs.msdn.microsoft.com/openspecification/2010/04/01/about-the-access_mask-structure/ //! for more information. uint32_t granted_access; @@ -188,9 +188,9 @@ class ProcessInfo { // the presumed alignment and emits SSE instructions that require aligned // storage. clang-cl should relax (unfortunately), but in the mean time, this // provides aligned storage. See https://crbug.com/564691 and - // http://llvm.org/PR25779. + // https://llvm.org/PR25779. // - // TODO(mark): Remove this workaround when http://llvm.org/PR25779 is fixed + // TODO(mark): Remove this workaround when https://llvm.org/PR25779 is fixed // and the fix is present in the clang-cl that compiles this code. MemoryBasicInformation64Vector memory_info_; diff --git a/util/win/process_structs.h b/util/win/process_structs.h index 66bc7f5b..d9b5b2fa 100644 --- a/util/win/process_structs.h +++ b/util/win/process_structs.h @@ -289,7 +289,7 @@ struct PEB { template struct NT_TIB { union { - // See https://msdn.microsoft.com/en-us/library/dn424783.aspx. + // See https://msdn.microsoft.com/library/dn424783.aspx. typename Traits::Pointer Wow64Teb; struct { typename Traits::Pointer ExceptionList; @@ -306,7 +306,7 @@ struct NT_TIB { }; }; -// See https://msdn.microsoft.com/en-us/library/gg750647.aspx. +// See https://msdn.microsoft.com/library/gg750647.aspx. template struct CLIENT_ID { typename Traits::Pointer UniqueProcess; @@ -314,8 +314,8 @@ struct CLIENT_ID { }; // This is a partial definition of the TEB, as we do not currently use many -// fields of it. See http://www.nirsoft.net/kernel_struct/vista/TEB.html, and -// the (arch-specific) definition of _TEB in winternl.h. +// fields of it. See https://nirsoft.net/kernel_struct/vista/TEB.html, and the +// (arch-specific) definition of _TEB in winternl.h. template struct TEB { NT_TIB NtTib; @@ -334,7 +334,7 @@ struct TEB { typename Traits::Pointer TlsExpansionSlots; }; -// See https://msdn.microsoft.com/en-us/library/gg750724.aspx. +// See https://msdn.microsoft.com/library/gg750724.aspx. template struct SYSTEM_THREAD_INFORMATION { union { @@ -400,7 +400,7 @@ struct VM_COUNTERS { SIZE_T PrivateUsage; }; -// See http://undocumented.ntinternals.net/source/usermode/undocumented%20functions/system%20information/structures/system_process_information.html +// https://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/System%20Information/Structures/SYSTEM_PROCESS_INFORMATION.html template struct SYSTEM_PROCESS_INFORMATION { ULONG NextEntryOffset; @@ -436,7 +436,7 @@ struct SYSTEM_PROCESS_INFORMATION { SYSTEM_THREAD_INFORMATION Threads[1]; }; -// http://undocumented.ntinternals.net/source/usermode/structures/thread_basic_information.html +// https://undocumented.ntinternals.net/source/usermode/structures/THREAD_BASIC_INFORMATION.html template struct THREAD_BASIC_INFORMATION { union { diff --git a/util/win/registration_protocol_win.cc b/util/win/registration_protocol_win.cc index dc31e7d4..4fb536d9 100644 --- a/util/win/registration_protocol_win.cc +++ b/util/win/registration_protocol_win.cc @@ -137,7 +137,7 @@ const void* GetSecurityDescriptorForNamedPipeInstance(size_t* size) { #pragma pack(push, 1) static constexpr struct SecurityDescriptorBlob { - // See https://msdn.microsoft.com/en-us/library/cc230366.aspx. + // See https://msdn.microsoft.com/library/cc230366.aspx. SECURITY_DESCRIPTOR_RELATIVE sd_rel; struct { ACL acl; diff --git a/util/win/xp_compat.h b/util/win/xp_compat.h index 7c62e41b..1d4d4a0b 100644 --- a/util/win/xp_compat.h +++ b/util/win/xp_compat.h @@ -24,14 +24,14 @@ enum { //! //! Requesting `PROCESS_ALL_ACCESS` with the value defined when building //! against a Vista+ SDK results in `ERROR_ACCESS_DENIED` when running on XP. - //! See https://msdn.microsoft.com/en-ca/library/windows/desktop/ms684880.aspx + //! See https://msdn.microsoft.com/library/ms684880.aspx. kXPProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF, //! \brief This is the XP-suitable value of `THREAD_ALL_ACCESS`. //! //! Requesting `THREAD_ALL_ACCESS` with the value defined when building //! against a Vista+ SDK results in `ERROR_ACCESS_DENIED` when running on XP. - //! See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686769.aspx + //! See https://msdn.microsoft.com/library/ms686769.aspx. kXPThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3FF, }; From e9f40ae176fb15a3a5ed1adb9aa7bd85ad221ce8 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Mon, 20 Nov 2017 18:19:22 -0500 Subject: [PATCH 030/326] Remove double double words MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I ran the thing below (piped to “grep -v namespace”), fixed things up, and rewrapped comments in the affected file. import re import sys LAST_WORD_RE = re.compile('^.*[\s]+([\w]+)$') FIRST_WORD_RE = re.compile('^[^\w]+([\w]+).*$') for path in sys.argv[1:]: with open(path) as file: line_number = 0 last_word = None for line in file: line_number += 1 first_word = FIRST_WORD_RE.match(line) if first_word and first_word.group(1) == last_word: print('%s:%u: %s' % (path, line_number - 1, last_word)) last_word = LAST_WORD_RE.match(line) if last_word: last_word = last_word.group(1) Change-Id: Iea9f2a6453d9d9ec17e2f238e09252535d7408bd Reviewed-on: https://chromium-review.googlesource.com/780284 Reviewed-by: Robert Sesek Commit-Queue: Mark Mentovai --- minidump/minidump_context.h | 11 +++++------ minidump/minidump_module_writer.h | 4 ++-- minidump/minidump_module_writer_test.cc | 2 +- minidump/minidump_writable.h | 6 +++--- snapshot/crashpad_info_client_options.h | 10 +++++----- snapshot/win/module_snapshot_win.cc | 9 ++++----- snapshot/win/module_snapshot_win.h | 2 +- test/errors.h | 8 ++++---- test/mac/mach_errors.h | 4 ++-- util/mach/child_port.defs | 12 ++++++------ util/mach/composite_mach_message_server.h | 8 ++++---- util/mach/exc_client_variants.h | 2 +- 12 files changed, 38 insertions(+), 40 deletions(-) diff --git a/minidump/minidump_context.h b/minidump/minidump_context.h index 88db5414..1226b654 100644 --- a/minidump/minidump_context.h +++ b/minidump/minidump_context.h @@ -61,7 +61,7 @@ enum MinidumpContextFlags : uint32_t { //! //! If this bit is set, it indicates that the bits indicating how the thread //! had entered kernel mode (::kMinidumpContextExceptionActive and - //! and ::kMinidumpContextServiceActive) are valid. This bit is only used on + //! ::kMinidumpContextServiceActive) are valid. This bit is only used on //! Windows. kMinidumpContextExceptionReporting = 0x80000000, }; @@ -154,8 +154,8 @@ struct MinidumpContextX86 { uint32_t dr6; uint32_t dr7; - // CPUContextX86::Fsave has identical layout to what the x86 CONTEXT - // structure places here. + // CPUContextX86::Fsave has identical layout to what the x86 CONTEXT structure + // places here. CPUContextX86::Fsave fsave; union { uint32_t spare_0; // As in the native x86 CONTEXT structure since Windows 8 @@ -324,9 +324,8 @@ struct alignas(16) MinidumpContextAMD64 { //! //! See Intel Software Developer’s Manual, Volume 3B: System Programming, Part //! 2 (253669-051), 17.4 “Last Branch, Interrupt, and Exception Recording - //! Overview”, and AMD Architecture Programmer’s Manual, Volume 2: - //! System Programming (24593-3.24), 13.1.6 “Control-Transfer Breakpoint - //! Features”. + //! Overview”, and AMD Architecture Programmer’s Manual, Volume 2: System + //! Programming (24593-3.24), 13.1.6 “Control-Transfer Breakpoint Features”. //! //! \{ uint64_t debug_control; diff --git a/minidump/minidump_module_writer.h b/minidump/minidump_module_writer.h index 775a8523..555c4115 100644 --- a/minidump/minidump_module_writer.h +++ b/minidump/minidump_module_writer.h @@ -317,8 +317,8 @@ class MinidumpModuleListWriter final : public internal::MinidumpStreamWriter { //! \param[in] module_snapshots The module snapshots to use as source data. //! //! \note Valid in #kStateMutable. AddModule() may not be called before this - //! this method, and it is not normally necessary to call AddModule() - //! after this method. + //! method, and it is not normally necessary to call AddModule() after + //! this method. void InitializeFromSnapshot( const std::vector& module_snapshots); diff --git a/minidump/minidump_module_writer_test.cc b/minidump/minidump_module_writer_test.cc index 9a2739b5..0fddfe88 100644 --- a/minidump/minidump_module_writer_test.cc +++ b/minidump/minidump_module_writer_test.cc @@ -83,7 +83,7 @@ TEST(MinidumpModuleWriter, EmptyModuleList) { // If |expected_pdb_name| is not nullptr, |codeview_record| is used to locate a // CodeView record in |file_contents|, and its fields are compared against the -// the |expected_pdb_*| values. If |expected_pdb_uuid| is supplied, the CodeView +// |expected_pdb_*| values. If |expected_pdb_uuid| is supplied, the CodeView // record must be a PDB 7.0 link, otherwise, it must be a PDB 2.0 link. If // |expected_pdb_name| is nullptr, |codeview_record| must not point to anything. void ExpectCodeViewRecord(const MINIDUMP_LOCATION_DESCRIPTOR* codeview_record, diff --git a/minidump/minidump_writable.h b/minidump/minidump_writable.h index 3518e6a9..b2ebf04e 100644 --- a/minidump/minidump_writable.h +++ b/minidump/minidump_writable.h @@ -127,9 +127,9 @@ class MinidumpWritable { //! Some objects, such as those capturing memory region snapshots, are //! written to minidump files after all other objects. This “late” phase //! identifies such objects. This is useful to improve spatial locality in - //! in minidump files in accordance with expected access patterns: unlike - //! most other data, memory snapshots are large and the entire snapshots do - //! not need to be consulted in order to process a minidump file. + //! minidump files in accordance with expected access patterns: unlike most + //! other data, memory snapshots are large and do not usually need to be + //! consulted in their entirety in order to process a minidump file. kPhaseLate, }; diff --git a/snapshot/crashpad_info_client_options.h b/snapshot/crashpad_info_client_options.h index 17469b77..89c947a6 100644 --- a/snapshot/crashpad_info_client_options.h +++ b/snapshot/crashpad_info_client_options.h @@ -23,14 +23,14 @@ namespace crashpad { //! \brief Options represented in a client’s CrashpadInfo structure. //! -//! The CrashpadInfo structure is not suitable to expose client options -//! in a generic way at the snapshot level. This structure duplicates -//! option-related fields from the client structure for general use within the -//! snapshot layer and by users of this layer. +//! The CrashpadInfo structure is not suitable to expose client options in a +//! generic way at the snapshot level. This structure duplicates option-related +//! fields from the client structure for general use within the snapshot layer +//! and by users of this layer. //! //! For objects of this type corresponding to a module, option values are taken //! from the module’s CrashpadInfo structure directly. If the module has no such -//! such structure, option values appear unset. +//! structure, option values appear unset. //! //! For objects of this type corresponding to an entire process, option values //! are taken from the CrashpadInfo structures of modules within the process. diff --git a/snapshot/win/module_snapshot_win.cc b/snapshot/win/module_snapshot_win.cc index 325e2db1..880e9da0 100644 --- a/snapshot/win/module_snapshot_win.cc +++ b/snapshot/win/module_snapshot_win.cc @@ -70,9 +70,8 @@ bool ModuleSnapshotWin::Initialize( // If we fully supported all old debugging formats, we would want to extract // and emit a different type of CodeView record here (as old Microsoft tools // would do). As we don't expect to ever encounter a module that wouldn't be - // be using .PDB that we actually have symbols for, we simply set a - // plausible name here, but this will never correspond to symbols that we - // have. + // using .PDB that we actually have symbols for, we simply set a plausible + // name here, but this will never correspond to symbols that we have. pdb_name_ = base::UTF16ToUTF8(name_); } @@ -177,8 +176,8 @@ std::string ModuleSnapshotWin::DebugFileName() const { std::vector ModuleSnapshotWin::AnnotationsVector() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); // These correspond to system-logged things on Mac. We don't currently track - // any of these on Windows, but could in the future. - // See https://crashpad.chromium.org/bug/38. + // any of these on Windows, but could in the future. See + // https://crashpad.chromium.org/bug/38. return std::vector(); } diff --git a/snapshot/win/module_snapshot_win.h b/snapshot/win/module_snapshot_win.h index 2a7083d1..414f0a4d 100644 --- a/snapshot/win/module_snapshot_win.h +++ b/snapshot/win/module_snapshot_win.h @@ -118,7 +118,7 @@ class ModuleSnapshotWin final : public ModuleSnapshot { InitializationStateDcheck initialized_; // VSFixedFileInfo() is logically const, but updates these members on the - // the call. See https://crashpad.chromium.org/bug/9. + // call. See https://crashpad.chromium.org/bug/9. mutable VS_FIXEDFILEINFO vs_fixed_file_info_; mutable InitializationState initialized_vs_fixed_file_info_; diff --git a/test/errors.h b/test/errors.h index c4f1a532..cf84ad64 100644 --- a/test/errors.h +++ b/test/errors.h @@ -35,7 +35,7 @@ namespace test { //! \brief Formats an error message using an `errno` value. //! //! The returned string will combine the \a base string, if supplied, with a -//! a textual and numeric description of the error. +//! textual and numeric description of the error. //! //! The message is formatted using `strerror()`. \a err may be `0` or outside of //! the range of known error codes, and the message returned will contain the @@ -53,7 +53,7 @@ std::string ErrnoMessage(int err, const std::string& base = std::string()); //! \brief Formats an error message using `errno`. //! //! The returned string will combine the \a base string, if supplied, with a -//! a textual and numeric description of the error. +//! textual and numeric description of the error. //! //! The message is formatted using `strerror()`. `errno` may be `0` or outside //! of the range of known error codes, and the message returned will contain the @@ -71,8 +71,8 @@ std::string ErrnoMessage(const std::string& base = std::string()); //! \brief Formats an error message using `GetLastError()`. //! //! The returned string will combine the \a base string, if supplied, with a -//! a textual and numeric description of the error. The format is the same as -//! the `PLOG()` formatting in base. +//! textual and numeric description of the error. The format is the same as the +//! `PLOG()` formatting in base. std::string ErrorMessage(const std::string& base = std::string()); #endif diff --git a/test/mac/mach_errors.h b/test/mac/mach_errors.h index 0376bd1e..9e43ef22 100644 --- a/test/mac/mach_errors.h +++ b/test/mac/mach_errors.h @@ -34,7 +34,7 @@ namespace test { //! \brief Formats a Mach error message. //! //! The returned string will combine the \a base string, if supplied, with a -//! a textual and numeric description of the error. +//! textual and numeric description of the error. //! //! \param[in] mach_err The Mach error code, which may be a `kern_return_t` or //! related type. @@ -50,7 +50,7 @@ std::string MachErrorMessage(mach_error_t mach_err, //! \brief Formats a bootstrap error message. //! //! The returned string will combine the \a base string, if supplied, with a -//! a textual and numeric description of the error. +//! textual and numeric description of the error. //! //! \param[in] bootstrap_err The bootstrap error code. //! \param[in] base A string to prepend to the error description. diff --git a/util/mach/child_port.defs b/util/mach/child_port.defs index b227f00f..b0cf982d 100644 --- a/util/mach/child_port.defs +++ b/util/mach/child_port.defs @@ -18,14 +18,14 @@ // child_port provides an interface for port rights to be transferred between // tasks. Its expected usage is for processes to be able to pass port rights // across IPC boundaries. A child process may wish to give its parent a copy of -// of a send right to its own task port, or a parent process may wish to give a +// a send right to its own task port, or a parent process may wish to give a // receive right to a child process that implements a server. // -// This Mach subsystem defines the lowest-level interface for these rights to -// be transferred. Most users will not user this interface directly, but will -// use ChildPortHandshake, which builds on this interface by providing client -// and server implementations, along with a protocol for establishing -// communication in a parent-child process relationship. +// This Mach subsystem defines the lowest-level interface for these rights to be +// transferred. Most users will not user this interface directly, but will use +// ChildPortHandshake, which builds on this interface by providing client and +// server implementations, along with a protocol for establishing communication +// in a parent-child process relationship. subsystem child_port 10011; serverprefix handle_; diff --git a/util/mach/composite_mach_message_server.h b/util/mach/composite_mach_message_server.h index fe3446e0..785af4dc 100644 --- a/util/mach/composite_mach_message_server.h +++ b/util/mach/composite_mach_message_server.h @@ -29,7 +29,7 @@ namespace crashpad { //! simultaneous use in a single MachMessageServer::Run() call. //! //! This class implements a MachMessageServer::Interface that contains other -//! other MachMessageServer::Interface objects. +//! MachMessageServer::Interface objects. //! //! In some situations, it may be desirable for a Mach message server to handle //! messages from distinct MIG subsystems with distinct @@ -59,9 +59,9 @@ class CompositeMachMessageServer : public MachMessageServer::Interface { //! \copydoc MachMessageServer::Interface::MachMessageServerFunction() //! //! This implementation forwards the message to an appropriate handler added - //! by AddHandler() on the basis of the \a in request message’s message ID. - //! If no appropriate handler exists, the \a out reply message is treated as - //! a `mig_reply_error_t`, its return code is set to `MIG_BAD_ID`, and `false` + //! by AddHandler() on the basis of the \a in request message’s message ID. If + //! no appropriate handler exists, the \a out reply message is treated as a + //! `mig_reply_error_t`, its return code is set to `MIG_BAD_ID`, and `false` //! is returned. bool MachMessageServerFunction(const mach_msg_header_t* in, mach_msg_header_t* out, diff --git a/util/mach/exc_client_variants.h b/util/mach/exc_client_variants.h index 3fa06a0b..8f52ed53 100644 --- a/util/mach/exc_client_variants.h +++ b/util/mach/exc_client_variants.h @@ -44,7 +44,7 @@ namespace crashpad { //! //! \a thread and \a task are only used when \a behavior indicates that the //! exception message will carry identity information, when it has the value -//! value `EXCEPTION_DEFAULT` or `EXCEPTION_STATE_IDENTITY`, possibly with +//! `EXCEPTION_DEFAULT` or `EXCEPTION_STATE_IDENTITY`, possibly with //! `MACH_EXCEPTION_CODES` also set. In other cases, these parameters are unused //! and may be set to `THREAD_NULL` and `TASK_NULL`, respectively. //! From ee84ce22bb892cdb7f458e079f044bdc5a1d2347 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 22 Nov 2017 16:30:50 -0800 Subject: [PATCH 031/326] fuchsia: Set kOS in MinidumpMiscInfoDebugBuildString Bug: crashpad:196 Change-Id: I80c979967d95383e0f703a336a494f30ff583f1b Reviewed-on: https://chromium-review.googlesource.com/786448 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- minidump/minidump_misc_info_writer.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/minidump/minidump_misc_info_writer.cc b/minidump/minidump_misc_info_writer.cc index 3e0dbbc9..c1e24892 100644 --- a/minidump/minidump_misc_info_writer.cc +++ b/minidump/minidump_misc_info_writer.cc @@ -108,6 +108,8 @@ std::string MinidumpMiscInfoDebugBuildString() { static constexpr char kOS[] = "linux"; #elif defined(OS_WIN) static constexpr char kOS[] = "win"; +#elif defined(OS_FUCHSIA) + static constexpr char kOS[] = "fuchsia"; #else #error define kOS for this operating system #endif From 1020a6147d3d171c2a88a93f122110a11f525690 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 27 Nov 2017 13:09:41 -0800 Subject: [PATCH 032/326] fuchsia: Use crashpad_info section matching Linux/Android for now I have no idea if this will work as not much is building yet, but it seems plausible for the time being. Bug: crashpad:196 Change-Id: Ie3a358512a968e9e777ed03c0bffc5e273a0f12e Reviewed-on: https://chromium-review.googlesource.com/786777 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- client/crashpad_info.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/crashpad_info.cc b/client/crashpad_info.cc index 7ead7445..d2896f4a 100644 --- a/client/crashpad_info.cc +++ b/client/crashpad_info.cc @@ -58,11 +58,11 @@ __attribute__(( // found without having to consult the symbol table. #if defined(OS_MACOSX) section(SEG_DATA ",crashpad_info"), -#elif defined(OS_LINUX) || defined(OS_ANDROID) +#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) section("crashpad_info"), -#else // !defined(OS_MACOSX) && !defined(OS_LINUX) && !defined(OS_ANDROID) +#else #error Port -#endif // !defined(OS_MACOSX) && !defined(OS_LINUX) && !defined(OS_ANDROID) +#endif #if defined(ADDRESS_SANITIZER) // AddressSanitizer would add a trailing red zone of at least 32 bytes, From 050d111bf986e32c0435d6117090a2392ed6b585 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 22 Nov 2017 16:58:07 -0800 Subject: [PATCH 033/326] fuchsia: Set METRICS_OS_NAME Bug: crashpad:196 Change-Id: I4f01c4f04c94a745b4c30bc41f66d2ae010e883a Reviewed-on: https://chromium-review.googlesource.com/786817 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- util/misc/metrics.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/misc/metrics.cc b/util/misc/metrics.cc index e2bd9f0b..30de6b90 100644 --- a/util/misc/metrics.cc +++ b/util/misc/metrics.cc @@ -26,6 +26,8 @@ #define METRICS_OS_NAME "Android" #elif defined(OS_LINUX) #define METRICS_OS_NAME "Linux" +#elif defined(OS_FUCHSIA) +#define METRICS_OS_NAME "Fuchsia" #endif namespace crashpad { From 4ee20f5831913c67fead07682143c37280f60146 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 27 Nov 2017 13:27:20 -0800 Subject: [PATCH 034/326] Add Fuchsia clang and sdk to DEPS These use CIPD (from depot_tools) to download packages created by the Fuchsia team. The .ensure file is passed to `cipd` which currently include specificiations for the Fuchsia team's build of clang (which, because it's close to HEAD includes support for targeting Fuchsia), as well as the Fuchsia SDK. Both packages are specified as "latest" rather than pinning to a specific revision. At this early point in the process we don't have any good reason to pin, but that can be accomplished later by replacing that with a specific package's SHA1. Due to the (relatively new) 'condition': 'checkout_fuchsia', this DEPS step will only be run when .gclient includes 'fuchsia' in the target_os block at the top_level, e.g. $ cat .gclient solutions = [ { ... }, ] target_os = ['fuchsia'] Bug: crashpad:196, crashpad:209 Change-Id: Id6d444a1c4450ffde8ee6665ff9720ce454c5cdf Reviewed-on: https://chromium-review.googlesource.com/786092 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- .gitignore | 3 +++ DEPS | 12 ++++++++++++ third_party/fuchsia/README.crashpad | 4 ++++ third_party/fuchsia/toolchain.ensure | 19 +++++++++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 third_party/fuchsia/README.crashpad create mode 100644 third_party/fuchsia/toolchain.ensure diff --git a/.gitignore b/.gitignore index 3311c2ef..41806962 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,9 @@ .gdbinit /Makefile /out +/third_party/fuchsia/.cipd +/third_party/fuchsia/clang +/third_party/fuchsia/sdk /third_party/gtest/gtest /third_party/gyp/gyp /third_party/mini_chromium/mini_chromium diff --git a/DEPS b/DEPS index 2e40af5e..c37cde74 100644 --- a/DEPS +++ b/DEPS @@ -113,6 +113,18 @@ hooks = [ 'buildtools/linux64/gn.sha1', ], }, + { + 'name': 'fuchsia_clang', + 'pattern': '.', + 'condition': 'checkout_fuchsia', + 'action': [ + 'cipd', + 'ensure', + '-ensure-file', 'crashpad/third_party/fuchsia/toolchain.ensure', + '-root', 'crashpad/third_party/fuchsia', + '-log-level', 'info', + ], + }, { 'name': 'gyp', 'pattern': '\.gypi?$', diff --git a/third_party/fuchsia/README.crashpad b/third_party/fuchsia/README.crashpad new file mode 100644 index 00000000..fc4a514a --- /dev/null +++ b/third_party/fuchsia/README.crashpad @@ -0,0 +1,4 @@ +This directory is a placeholder for Fuchsia tools that will be downloaded by +CIPD (https://github.com/luci/luci-go/tree/master/cipd). The toolchain.ensure +files specifies which CIPD packages to retrieve, at which versions, and where +they're stored locally. The CIPD update happens as part of gclient runhooks. diff --git a/third_party/fuchsia/toolchain.ensure b/third_party/fuchsia/toolchain.ensure new file mode 100644 index 00000000..00f9b788 --- /dev/null +++ b/third_party/fuchsia/toolchain.ensure @@ -0,0 +1,19 @@ +# 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. + +@Subdir clang +fuchsia/clang/${os}-${arch} latest + +@Subdir sdk +fuchsia/sdk/${os=linux}-${arch} latest From 0d05b0d59e191ceee40eeefafe90f19ee854f71d Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 27 Nov 2017 13:37:01 -0800 Subject: [PATCH 035/326] fuchsia: Use RandBytes UUID generator There's no particular UUID generator on Fuchsia, so use the RandBytes() version. (That won't work either yet, but will once RandBytes() is implemented.) Bug: crashpad:196 Change-Id: Id740bbfc80e170d7ab19995ac88db5eed474c119 Reviewed-on: https://chromium-review.googlesource.com/786822 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- util/misc/uuid.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/util/misc/uuid.cc b/util/misc/uuid.cc index 6cf777f5..92fba760 100644 --- a/util/misc/uuid.cc +++ b/util/misc/uuid.cc @@ -90,9 +90,11 @@ bool UUID::InitializeWithNew() { uuid_generate(uuid); InitializeFromBytes(uuid); return true; -#elif defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID) - // Linux does not provide a UUID generator in a widely-available system - // library. uuid_generate() from libuuid is not available everywhere. +#elif defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID) || \ + defined(OS_FUCHSIA) + // Linux, Android, and Fuchsia do not provide a UUID generator in a + // widely-available system library. On Linux and Android, uuid_generate() + // from libuuid is not available everywhere. // On Windows, do not use UuidCreate() to avoid a dependency on rpcrt4, so // that this function is usable early in DllMain(). base::RandBytes(this, sizeof(*this)); From af28b83eb7b6c5009c3ba1ccb440653df10fd7a0 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Tue, 28 Nov 2017 13:28:41 -0500 Subject: [PATCH 036/326] In Annotation::SetSize, use AnnotationList::Register rather than Get. In Chromium, the AnnotationList is registered in the main executable module. However, when using the component build, the individual shared libraries do not explicitly initialize the CrashpadInfo nor AnnotationList. This causes annotations to NULL-dereference the uninitialized AnnotationList when using the component build. By using the Register method instead, the AnnotationList will be lazily created. In Chromium's static/release build, the AnnotationList will still be initialized deterministically during startup. Bug: crashpad:192 Change-Id: I8599b52630f4d7608e5028b14264a8eed49a9176 Reviewed-on: https://chromium-review.googlesource.com/793981 Commit-Queue: Robert Sesek Reviewed-by: Mark Mentovai --- client/annotation.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/annotation.cc b/client/annotation.cc index 22c73956..258f9654 100644 --- a/client/annotation.cc +++ b/client/annotation.cc @@ -30,7 +30,9 @@ constexpr size_t Annotation::kValueMaxSize; void Annotation::SetSize(ValueSizeType size) { DCHECK_LT(size, kValueMaxSize); size_ = size; - AnnotationList::Get()->Add(this); + // Use Register() instead of Get() in case the calling module has not + // explicitly initialized the annotation list, to avoid crashing. + AnnotationList::Register()->Add(this); } void Annotation::Clear() { From 2bb56fafe3bd0c7a381ff4a1f54a82f772407a62 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 28 Nov 2017 10:31:13 -0800 Subject: [PATCH 037/326] Rework GN files to start to support building standalone, and also in Chromium - Adds a .gn and a build/BUILDCONFIG.gn that uses mini_chromium's build/BUILD.gn. - Adds some stub BUILD.gn files in locations where Chromium expects them (in //build, //testing, //third_party) containing empty targets/configs. These are no-ops in standalone builds, but add functionality when building in Chromium. This is in preference to having a global bool that conditionally does Chromium-y things in the Crashpad build files. These stub files are all contained in a secondary source root in build/chromium_compatibility, referred to by //.gn. - Adds //base/BUILD.gn which forwards to mini_chromium/base. This is only used when building standalone so that both Chromium and Crashpad can refer to it as "//base". - Changes references to other Crashpad targets to be relatively specified so that they work when the root of the project is //, and also when it's //third_party/crashpad/crashpad as it is in Chromium. - Moves any error-causing Mac/Win-specific files into explicit if (is_mac) or if (is_win) blocks as part of removing the dependency on set_sources_assignment_filter(). As yet unresolved: - CRASHPAD_IN_CHROMIUM needs to be removed when standalone; to be tackled in a follow up. - Not sure what to do with zlib yet, the build file currently assumes "in Chromium" too, and similarly having Crashpad //third_party/zlib:zlib pointing at itself doesn't work. Bug: crashpad:79 Change-Id: I6a7dda214e4b3b14a60c1ed285267ab97432a1a8 Reviewed-on: https://chromium-review.googlesource.com/777410 Reviewed-by: Mark Mentovai Reviewed-by: Robert Sesek Commit-Queue: Scott Graham --- .gn | 19 ++ BUILD.gn | 2 +- build/BUILDCONFIG.gn | 66 ++++++ build/chromium_compatibility/README.crashpad | 5 + build/chromium_compatibility/base/BUILD.gn | 26 +++ .../chromium_compatibility/base/test/BUILD.gn | 19 ++ .../build/config/compiler/BUILD.gn | 28 +++ .../build/config/compiler/compiler.gni | 16 ++ .../chromium_compatibility/build/win/BUILD.gn | 25 +++ .../testing/gmock/BUILD.gn | 19 ++ .../testing/gtest/BUILD.gn | 19 ++ build/chromium_compatibility/testing/test.gni | 22 ++ client/BUILD.gn | 17 +- compat/BUILD.gn | 2 +- handler/BUILD.gn | 60 +++--- minidump/BUILD.gn | 16 +- snapshot/BUILD.gn | 58 +++--- snapshot/test/BUILD.gn | 155 -------------- test/BUILD.gn | 12 +- tools/BUILD.gn | 38 ++-- util/BUILD.gn | 191 +++++++++--------- 21 files changed, 468 insertions(+), 347 deletions(-) create mode 100644 .gn create mode 100644 build/BUILDCONFIG.gn create mode 100644 build/chromium_compatibility/README.crashpad create mode 100644 build/chromium_compatibility/base/BUILD.gn create mode 100644 build/chromium_compatibility/base/test/BUILD.gn create mode 100644 build/chromium_compatibility/build/config/compiler/BUILD.gn create mode 100644 build/chromium_compatibility/build/config/compiler/compiler.gni create mode 100644 build/chromium_compatibility/build/win/BUILD.gn create mode 100644 build/chromium_compatibility/testing/gmock/BUILD.gn create mode 100644 build/chromium_compatibility/testing/gtest/BUILD.gn create mode 100644 build/chromium_compatibility/testing/test.gni delete mode 100644 snapshot/test/BUILD.gn diff --git a/.gn b/.gn new file mode 100644 index 00000000..304422da --- /dev/null +++ b/.gn @@ -0,0 +1,19 @@ +# 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. + +buildconfig = "//build/BUILDCONFIG.gn" + +# This secondary source root is used to put various forwarding/stub files that +# serve to make the core build files compatible with Chromium. +secondary_source = "//build/chromium_compatibility/" diff --git a/BUILD.gn b/BUILD.gn index 5c3918ef..88559c42 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -15,7 +15,7 @@ import("//testing/test.gni") config("crashpad_config") { - include_dirs = [ "//third_party/crashpad/crashpad" ] + include_dirs = [ "." ] } test("crashpad_tests") { diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn new file mode 100644 index 00000000..d85494ba --- /dev/null +++ b/build/BUILDCONFIG.gn @@ -0,0 +1,66 @@ +# 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. + +if (target_os == "") { + target_os = host_os +} + +if (current_os == "") { + current_os = target_os +} + +declare_args() { + is_debug = false + is_clang = current_os == "mac" || current_os == "fuchsia" + clang_root = "" +} + +is_mac = false +is_win = false +is_linux = false +is_android = false +is_fuchsia = false +is_ios = false # This is necessary for third_party/zlib/zlib/BUILD.gn. + +if (current_os == "mac") { + is_mac = true +} else if (current_os == "win") { + is_win = true +} else if (current_os == "android") { + is_android = true +} else if (current_os == "linux") { + is_linux = true +} else if (current_os == "fuchsia") { + is_fuchsia = true +} + +is_posix = is_mac || is_linux || is_android || is_fuchsia + +if (is_win) { + set_default_toolchain( + "//third_party/mini_chromium/mini_chromium/build:msvc_toolchain") +} else { + set_default_toolchain( + "//third_party/mini_chromium/mini_chromium/build:gcc_like_toolchain") +} + +set_defaults("static_library") { + configs = [ + "//third_party/mini_chromium/mini_chromium/build:default", + + # This (no-op) is added here so that build files that expect to be able to + # remove it can do so without causing an error. + "//build/config/compiler:chromium_code", + ] +} diff --git a/build/chromium_compatibility/README.crashpad b/build/chromium_compatibility/README.crashpad new file mode 100644 index 00000000..9a3c1296 --- /dev/null +++ b/build/chromium_compatibility/README.crashpad @@ -0,0 +1,5 @@ +This directory is used as a secondary GN source root for compatibility with +Chromium. Files in this subtree should match file paths that the Crashpad build +files need to refer to when building in Chromium. In the Crashpad tree, they +should either be empty/no-ops, or forward to the real Crashpad implementation +in the real tree. No actual configuration should be done in this secondary tree. diff --git a/build/chromium_compatibility/base/BUILD.gn b/build/chromium_compatibility/base/BUILD.gn new file mode 100644 index 00000000..f00a51c2 --- /dev/null +++ b/build/chromium_compatibility/base/BUILD.gn @@ -0,0 +1,26 @@ +# 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. + +# This target is a stub so that both Crashpad and Chromium can refer to "//base" +# in their build files. When building in the Chromium tree, "//base" will refer +# the "real" base, but when building standalone in Crashpad, we forward those +# references on to mini_chromium. + +group("base") { + public_configs = + [ "//third_party/mini_chromium/mini_chromium/base:base_public_config" ] + public_deps = [ + "//third_party/mini_chromium/mini_chromium/base", + ] +} diff --git a/build/chromium_compatibility/base/test/BUILD.gn b/build/chromium_compatibility/base/test/BUILD.gn new file mode 100644 index 00000000..5bc810a0 --- /dev/null +++ b/build/chromium_compatibility/base/test/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +# This is a stub to match Chromium. This target is unused and has no effect when +# building standalone in Crashpad. + +group("test_support") { +} diff --git a/build/chromium_compatibility/build/config/compiler/BUILD.gn b/build/chromium_compatibility/build/config/compiler/BUILD.gn new file mode 100644 index 00000000..9d4d2e14 --- /dev/null +++ b/build/chromium_compatibility/build/config/compiler/BUILD.gn @@ -0,0 +1,28 @@ +# 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. + +# This is a stub to match Chromium. The configs in this file do not have any +# effect on the build when building standalone in Crashpad. + +config("default_symbols") { +} + +config("minimal_symbols") { +} + +config("chromium_code") { +} + +config("no_chromium_code") { +} diff --git a/build/chromium_compatibility/build/config/compiler/compiler.gni b/build/chromium_compatibility/build/config/compiler/compiler.gni new file mode 100644 index 00000000..f615259a --- /dev/null +++ b/build/chromium_compatibility/build/config/compiler/compiler.gni @@ -0,0 +1,16 @@ +# 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. + +# This is a stub to match Chromium, but is unused when building standalone in +# Crashpad. diff --git a/build/chromium_compatibility/build/win/BUILD.gn b/build/chromium_compatibility/build/win/BUILD.gn new file mode 100644 index 00000000..3bf730e4 --- /dev/null +++ b/build/chromium_compatibility/build/win/BUILD.gn @@ -0,0 +1,25 @@ +# 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. + +# This is a stub to match Chromium. The configs in this file do not have any +# effect on the build when building standalone in Crashpad. + +group("default_exe_manifest") { +} + +config("console") { +} + +config("windowed") { +} diff --git a/build/chromium_compatibility/testing/gmock/BUILD.gn b/build/chromium_compatibility/testing/gmock/BUILD.gn new file mode 100644 index 00000000..81001042 --- /dev/null +++ b/build/chromium_compatibility/testing/gmock/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +# TODO(GN): This is a placeholder that matches the name of Chromium's location +# for this file. It will need to be filled out to cause a gmock dependency. + +group("gmock") { +} diff --git a/build/chromium_compatibility/testing/gtest/BUILD.gn b/build/chromium_compatibility/testing/gtest/BUILD.gn new file mode 100644 index 00000000..03d30464 --- /dev/null +++ b/build/chromium_compatibility/testing/gtest/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +# TODO(GN): This is a placeholder that matches the name of Chromium's location +# for this file. It will need to be filled out to cause a gtest dependency. + +group("gtest") { +} diff --git a/build/chromium_compatibility/testing/test.gni b/build/chromium_compatibility/testing/test.gni new file mode 100644 index 00000000..b94470dc --- /dev/null +++ b/build/chromium_compatibility/testing/test.gni @@ -0,0 +1,22 @@ +# 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. + +template("test") { + executable(target_name) { + deps = [] + forward_variables_from(invoker, "*") + + testonly = true + } +} diff --git a/client/BUILD.gn b/client/BUILD.gn index 3e24d6dd..2fa7110b 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -48,12 +48,12 @@ static_library("client") { ] } - public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + public_configs = [ "..:crashpad_config" ] deps = [ + "../compat", + "../util", "//base", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", ] if (is_win) { @@ -88,20 +88,19 @@ source_set("client_test") { deps = [ ":client", + "../compat", + "../test", + "../util", "//base", "//testing/gmock", "//testing/gtest", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/test", - "//third_party/crashpad/crashpad/util", ] data_deps = [ - "//third_party/crashpad/crashpad/handler:crashpad_handler", + "../handler:crashpad_handler", ] if (is_win) { - data_deps += - [ "//third_party/crashpad/crashpad/handler:crashpad_handler_console" ] + data_deps += [ "../handler:crashpad_handler_console" ] } } diff --git a/compat/BUILD.gn b/compat/BUILD.gn index ddeb53d7..0903e795 100644 --- a/compat/BUILD.gn +++ b/compat/BUILD.gn @@ -68,7 +68,7 @@ static_library("compat") { public_configs = [ ":compat_config", - "//third_party/crashpad/crashpad:crashpad_config", + "..:crashpad_config", ] deps = [] diff --git a/handler/BUILD.gn b/handler/BUILD.gn index 2c17f3d9..05f554b7 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -20,32 +20,42 @@ static_library("handler") { "crash_report_upload_thread.h", "handler_main.cc", "handler_main.h", - "mac/crash_report_exception_handler.cc", - "mac/crash_report_exception_handler.h", - "mac/exception_handler_server.cc", - "mac/exception_handler_server.h", - "mac/file_limit_annotation.cc", - "mac/file_limit_annotation.h", "minidump_to_upload_parameters.cc", "minidump_to_upload_parameters.h", "prune_crash_reports_thread.cc", "prune_crash_reports_thread.h", "user_stream_data_source.cc", "user_stream_data_source.h", - "win/crash_report_exception_handler.cc", - "win/crash_report_exception_handler.h", ] - public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + if (is_mac) { + sources += [ + "mac/crash_report_exception_handler.cc", + "mac/crash_report_exception_handler.h", + "mac/exception_handler_server.cc", + "mac/exception_handler_server.h", + "mac/file_limit_annotation.cc", + "mac/file_limit_annotation.h", + ] + } + + if (is_win) { + sources += [ + "win/crash_report_exception_handler.cc", + "win/crash_report_exception_handler.h", + ] + } + + public_configs = [ "..:crashpad_config" ] deps = [ + "../client", + "../compat", + "../minidump", + "../snapshot", + "../tools:tool_support", + "../util", "//base", - "//third_party/crashpad/crashpad/client", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/minidump", - "//third_party/crashpad/crashpad/snapshot", - "//third_party/crashpad/crashpad/tools:tool_support", - "//third_party/crashpad/crashpad/util", ] if (is_win) { @@ -62,13 +72,13 @@ source_set("handler_test") { deps = [ ":handler", + "../client", + "../compat", + "../snapshot", + "../test", + "../util", "//base", "//testing/gtest", - "//third_party/crashpad/crashpad/client", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/snapshot", - "//third_party/crashpad/crashpad/test", - "//third_party/crashpad/crashpad/util", ] if (is_win) { @@ -87,9 +97,9 @@ executable("crashpad_handler") { deps = [ ":handler", + "../compat", "//base", "//build/win:default_exe_manifest", - "//third_party/crashpad/crashpad/compat", ] if (is_mac && is_component_build) { @@ -123,11 +133,11 @@ executable("crashpad_handler_test_extended_handler") { deps = [ ":handler", + "../compat", + "../minidump:test_support", + "../tools:tool_support", "//base", "//build/win:default_exe_manifest", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/minidump:test_support", - "//third_party/crashpad/crashpad/tools:tool_support", ] } @@ -144,9 +154,9 @@ if (is_win) { deps = [ ":handler", + "../compat", "//base", "//build/win:default_exe_manifest", - "//third_party/crashpad/crashpad/compat", ] } diff --git a/minidump/BUILD.gn b/minidump/BUILD.gn index 42f8c3e1..a3c6a2c9 100644 --- a/minidump/BUILD.gn +++ b/minidump/BUILD.gn @@ -69,16 +69,16 @@ static_library("minidump") { "minidump_writer_util.h", ] - public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + public_configs = [ "..:crashpad_config" ] public_deps = [ - "//third_party/crashpad/crashpad/compat", + "../compat", ] deps = [ + "../snapshot", + "../util", "//base", - "//third_party/crashpad/crashpad/snapshot", - "//third_party/crashpad/crashpad/util", ] if (is_win) { @@ -111,7 +111,7 @@ static_library("test_support") { "test/minidump_writable_test_util.h", ] - public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + public_configs = [ "..:crashpad_config" ] public_deps = [ ":minidump", @@ -156,11 +156,11 @@ source_set("minidump_test") { deps = [ ":test_support", + "../snapshot:test_support", + "../test", + "../util", "//base", "//testing/gtest", - "//third_party/crashpad/crashpad/snapshot:test_support", - "//third_party/crashpad/crashpad/test", - "//third_party/crashpad/crashpad/util", ] if (is_win) { diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 49402fe9..2b01fc20 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -125,13 +125,13 @@ static_library("snapshot") { ] } - public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + public_configs = [ "..:crashpad_config" ] deps = [ + "../client", + "../compat", + "../util", "//base", - "//third_party/crashpad/crashpad/client", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", ] if (is_win) { @@ -147,15 +147,15 @@ if (is_win) { "api/module_annotations_win.h", ] - public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + public_configs = [ "..:crashpad_config" ] cflags = [ "/wd4201" ] deps = [ ":snapshot", + "../compat", + "../util", "//base", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", ] } } else { @@ -185,16 +185,16 @@ static_library("test_support") { "test/test_thread_snapshot.h", ] - public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + public_configs = [ "..:crashpad_config" ] public_deps = [ ":snapshot", ] deps = [ + "../compat", + "../util", "//base", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", ] if (is_win) { @@ -235,12 +235,12 @@ source_set("snapshot_test") { deps = [ ":snapshot_api", ":test_support", + "../client", + "../compat", + "../test", + "../util", "//base", "//testing/gtest", - "//third_party/crashpad/crashpad/client", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/test", - "//third_party/crashpad/crashpad/util", ] data_deps = [ @@ -278,8 +278,8 @@ loadable_module("crashpad_snapshot_test_module") { "crashpad_info_client_options_test_module.cc", ] deps = [ + "../client", "//base", - "//third_party/crashpad/crashpad/client", ] } @@ -328,9 +328,9 @@ if (is_win) { "win/crashpad_snapshot_test_annotations.cc", ] deps = [ + "../client", + "../compat", "//base", - "//third_party/crashpad/crashpad/client", - "//third_party/crashpad/crashpad/compat", ] } @@ -340,10 +340,10 @@ if (is_win) { "win/crashpad_snapshot_test_crashing_child.cc", ] deps = [ + "../client", + "../compat", + "../util", "//base", - "//third_party/crashpad/crashpad/client", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", ] } @@ -353,10 +353,10 @@ if (is_win) { "win/crashpad_snapshot_test_dump_without_crashing.cc", ] deps = [ + "../client", + "../compat", + "../util", "//base", - "//third_party/crashpad/crashpad/client", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", ] } @@ -366,9 +366,9 @@ if (is_win) { "win/crashpad_snapshot_test_extra_memory_ranges.cc", ] deps = [ + "../client", + "../compat", "//base", - "//third_party/crashpad/crashpad/client", - "//third_party/crashpad/crashpad/compat", ] } @@ -378,10 +378,10 @@ if (is_win) { "win/crashpad_snapshot_test_image_reader.cc", ] deps = [ + "../client", + "../compat", + "../util", "//base", - "//third_party/crashpad/crashpad/client", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", ] if (symbol_level == 0) { # The tests that use this executable rely on at least minimal debug info. @@ -396,8 +396,8 @@ if (is_win) { "win/crashpad_snapshot_test_image_reader_module.cc", ] deps = [ + "../client", "//base", - "//third_party/crashpad/crashpad/client", ] if (symbol_level == 0) { # The tests that use this module rely on at least minimal debug info. diff --git a/snapshot/test/BUILD.gn b/snapshot/test/BUILD.gn deleted file mode 100644 index 5b9b745f..00000000 --- a/snapshot/test/BUILD.gn +++ /dev/null @@ -1,155 +0,0 @@ -# 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. - -import("//testing/test.gni") - -static_library("test") { - testonly = true - - sources = [ - "errors.cc", - "errors.h", - "file.cc", - "file.h", - "filesystem.cc", - "filesystem.h", - "gtest_death_check.h", - "gtest_disabled.cc", - "gtest_disabled.h", - "hex_string.cc", - "hex_string.h", - "mac/dyld.cc", - "mac/dyld.h", - "mac/mach_errors.cc", - "mac/mach_errors.h", - "mac/mach_multiprocess.cc", - "mac/mach_multiprocess.h", - "main_arguments.cc", - "main_arguments.h", - "multiprocess.h", - "multiprocess_exec.h", - "multiprocess_exec_posix.cc", - "multiprocess_exec_win.cc", - "multiprocess_posix.cc", - "scoped_module_handle.cc", - "scoped_module_handle.h", - "scoped_temp_dir.cc", - "scoped_temp_dir.h", - "scoped_temp_dir_posix.cc", - "scoped_temp_dir_win.cc", - "test_paths.cc", - "test_paths.h", - "win/child_launcher.cc", - "win/child_launcher.h", - "win/win_child_process.cc", - "win/win_child_process.h", - "win/win_multiprocess.cc", - "win/win_multiprocess.h", - "win/win_multiprocess_with_temp_dir.cc", - "win/win_multiprocess_with_temp_dir.h", - ] - - public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] - - defines = [ "CRASHPAD_IN_CHROMIUM" ] - - data = [ - "test_paths_test_data_root.txt", - ] - - deps = [ - "//base", - "//testing/gtest", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/snapshot", - "//third_party/crashpad/crashpad/util", - ] - - if (is_mac) { - libs = [ "bsm" ] - } -} - -source_set("test_test") { - testonly = true - - sources = [ - "hex_string_test.cc", - "mac/mach_multiprocess_test.cc", - "main_arguments_test.cc", - "multiprocess_exec_test.cc", - "scoped_temp_dir_test.cc", - "test_paths_test.cc", - "win/win_child_process_test.cc", - "win/win_multiprocess_test.cc", - ] - - if (!is_win) { - sources += [ "multiprocess_posix_test.cc" ] - } - - deps = [ - ":test", - "//base", - "//testing/gmock", - "//testing/gtest", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", - ] - - data_deps = [ - ":crashpad_test_test_multiprocess_exec_test_child", - ] -} - -executable("crashpad_test_test_multiprocess_exec_test_child") { - sources = [ - "multiprocess_exec_test_child.cc", - ] -} - -static_library("gmock_main") { - testonly = true - sources = [ - "gtest_main.cc", - ] - defines = [ - "CRASHPAD_IN_CHROMIUM", - "CRASHPAD_TEST_LAUNCHER_GMOCK", - ] - deps = [ - ":test", - "//base", - "//base/test:test_support", - "//testing/gmock", - "//testing/gtest", - ] -} - -static_library("gtest_main") { - testonly = true - sources = [ - "gtest_main.cc", - ] - defines = [ - "CRASHPAD_IN_CHROMIUM", - "CRASHPAD_TEST_LAUNCHER_GTEST", - ] - deps = [ - ":test", - "//base", - "//base/test:test_support", - "//testing/gtest", - ] -} diff --git a/test/BUILD.gn b/test/BUILD.gn index c3208ad6..50f0c81f 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -62,7 +62,7 @@ static_library("test") { "win/win_multiprocess_with_temp_dir.h", ] - public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + public_configs = [ "..:crashpad_config" ] defines = [ "CRASHPAD_IN_CHROMIUM" ] @@ -71,11 +71,11 @@ static_library("test") { ] deps = [ + "../compat", + "../snapshot", + "../util", "//base", "//testing/gtest", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/snapshot", - "//third_party/crashpad/crashpad/util", ] if (is_mac) { @@ -104,11 +104,11 @@ source_set("test_test") { deps = [ ":test", + "../compat", + "../util", "//base", "//testing/gmock", "//testing/gtest", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", ] data_deps = [ diff --git a/tools/BUILD.gn b/tools/BUILD.gn index e2a82d39..af02e66c 100644 --- a/tools/BUILD.gn +++ b/tools/BUILD.gn @@ -18,7 +18,7 @@ source_set("tool_support") { "tool_support.h", ] - public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + public_configs = [ "..:crashpad_config" ] deps = [ "//base", @@ -32,11 +32,11 @@ executable("crashpad_database_util") { deps = [ ":tool_support", + "../client", + "../compat", + "../util", "//base", "//build/win:default_exe_manifest", - "//third_party/crashpad/crashpad/client", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", ] } @@ -47,10 +47,10 @@ executable("crashpad_http_upload") { deps = [ ":tool_support", + "../compat", + "../util", "//base", "//build/win:default_exe_manifest", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", ] } @@ -61,12 +61,12 @@ executable("generate_dump") { deps = [ ":tool_support", + "../compat", + "../minidump", + "../snapshot", + "../util", "//base", "//build/win:default_exe_manifest", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/minidump", - "//third_party/crashpad/crashpad/snapshot", - "//third_party/crashpad/crashpad/util", ] if (is_mac) { @@ -96,9 +96,9 @@ if (is_mac) { deps = [ ":tool_support", + "../compat", + "../util", "//base", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", ] } @@ -121,9 +121,9 @@ if (is_mac) { deps = [ ":tool_support", + "../compat", + "../util", "//base", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", ] } @@ -139,9 +139,9 @@ if (is_mac) { deps = [ ":tool_support", + "../compat", + "../util", "//base", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", ] } @@ -152,10 +152,10 @@ if (is_mac) { deps = [ ":tool_support", + "../client", + "../compat", + "../util", "//base", - "//third_party/crashpad/crashpad/client", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/util", ] } } diff --git a/util/BUILD.gn b/util/BUILD.gn index 34b30361..7f8e7ecc 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("//build/config/sanitizers/sanitizers.gni") -import("//build/toolchain/toolchain.gni") import("//testing/test.gni") if (is_mac) { @@ -61,11 +59,9 @@ static_library("util") { "file/delimited_file_reader.h", "file/directory_reader.h", "file/directory_reader_posix.cc", - "file/directory_reader_win.cc", "file/file_io.cc", "file/file_io.h", "file/file_io_posix.cc", - "file/file_io_win.cc", "file/file_reader.cc", "file/file_reader.h", "file/file_seeker.cc", @@ -74,28 +70,16 @@ static_library("util") { "file/file_writer.h", "file/filesystem.h", "file/filesystem_posix.cc", - "file/filesystem_win.cc", "file/scoped_remove_file.cc", "file/scoped_remove_file.h", "file/string_file.cc", "file/string_file.h", - "mac/checked_mach_address_range.h", - "mac/launchd.h", - "mac/launchd.mm", - "mac/mac_util.cc", - "mac/mac_util.h", - "mac/service_management.cc", - "mac/service_management.h", - "mac/xattr.cc", - "mac/xattr.h", "misc/address_sanitizer.h", "misc/address_types.h", "misc/arraysize_unsafe.h", "misc/as_underlying_type.h", "misc/clock.h", - "misc/clock_mac.cc", "misc/clock_posix.cc", - "misc/clock_win.cc", "misc/from_pointer_cast.h", "misc/implicit_cast.h", "misc/initialization_state.h", @@ -106,8 +90,6 @@ static_library("util") { "misc/metrics.cc", "misc/metrics.h", "misc/paths.h", - "misc/paths_mac.cc", - "misc/paths_win.cc", "misc/pdb_structures.cc", "misc/pdb_structures.h", "misc/random_string.cc", @@ -119,7 +101,6 @@ static_library("util") { "misc/symbolic_constants_common.h", "misc/time.cc", "misc/time.h", - "misc/time_win.cc", "misc/tri_state.h", "misc/uuid.cc", "misc/uuid.h", @@ -134,8 +115,6 @@ static_library("util") { "net/http_multipart_builder.h", "net/http_transport.cc", "net/http_transport.h", - "net/http_transport_mac.mm", - "net/http_transport_win.cc", "net/url.cc", "net/url.h", "numeric/checked_address_range.cc", @@ -154,7 +133,6 @@ static_library("util") { "posix/drop_privileges.cc", "posix/drop_privileges.h", "posix/process_info.h", - "posix/process_info_mac.cc", "posix/scoped_dir.cc", "posix/scoped_dir.h", "posix/scoped_mmap.cc", @@ -177,63 +155,27 @@ static_library("util") { "string/split_string.cc", "string/split_string.h", "synchronization/semaphore.h", - "synchronization/semaphore_mac.cc", "synchronization/semaphore_posix.cc", - "synchronization/semaphore_win.cc", "thread/thread.cc", "thread/thread.h", "thread/thread_log_messages.cc", "thread/thread_log_messages.h", "thread/thread_posix.cc", - "thread/thread_win.cc", "thread/worker_thread.cc", "thread/worker_thread.h", - "win/address_types.h", - "win/capture_context.h", - "win/checked_win_address_range.h", - "win/command_line.cc", - "win/command_line.h", - "win/critical_section_with_debug_info.cc", - "win/critical_section_with_debug_info.h", - "win/exception_handler_server.cc", - "win/exception_handler_server.h", - "win/get_function.cc", - "win/get_function.h", - "win/get_module_information.cc", - "win/get_module_information.h", - "win/handle.cc", - "win/handle.h", - "win/initial_client_data.cc", - "win/initial_client_data.h", - "win/module_version.cc", - "win/module_version.h", - "win/nt_internals.cc", - "win/nt_internals.h", - "win/ntstatus_logging.cc", - "win/ntstatus_logging.h", - "win/process_info.cc", - "win/process_info.h", - "win/process_structs.h", - "win/registration_protocol_win.cc", - "win/registration_protocol_win.h", - "win/safe_terminate_process.h", - "win/scoped_handle.cc", - "win/scoped_handle.h", - "win/scoped_local_alloc.cc", - "win/scoped_local_alloc.h", - "win/scoped_process_suspend.cc", - "win/scoped_process_suspend.h", - "win/scoped_set_event.cc", - "win/scoped_set_event.h", - "win/session_end_watcher.cc", - "win/session_end_watcher.h", - "win/termination_codes.h", - "win/xp_compat.h", ] if (is_mac) { - # mach/ are not globally filtered. sources += [ + "mac/checked_mach_address_range.h", + "mac/launchd.h", + "mac/launchd.mm", + "mac/mac_util.cc", + "mac/mac_util.h", + "mac/service_management.cc", + "mac/service_management.h", + "mac/xattr.cc", + "mac/xattr.h", "mach/child_port_handshake.cc", "mach/child_port_handshake.h", "mach/child_port_server.cc", @@ -267,14 +209,69 @@ static_library("util") { "mach/task_for_pid.h", "mach/task_memory.cc", "mach/task_memory.h", + "misc/clock_mac.cc", + "misc/paths_mac.cc", + "net/http_transport_mac.mm", + "posix/process_info_mac.cc", + "synchronization/semaphore_mac.cc", ] - } - - if (is_mac) { sources += get_target_outputs(":mig") } if (is_win) { + sources += [ + "file/directory_reader_win.cc", + "file/file_io_win.cc", + "file/filesystem_win.cc", + "misc/clock_win.cc", + "misc/paths_win.cc", + "misc/time_win.cc", + "net/http_transport_win.cc", + "synchronization/semaphore_win.cc", + "thread/thread_win.cc", + "win/address_types.h", + "win/capture_context.h", + "win/checked_win_address_range.h", + "win/command_line.cc", + "win/command_line.h", + "win/critical_section_with_debug_info.cc", + "win/critical_section_with_debug_info.h", + "win/exception_handler_server.cc", + "win/exception_handler_server.h", + "win/get_function.cc", + "win/get_function.h", + "win/get_module_information.cc", + "win/get_module_information.h", + "win/handle.cc", + "win/handle.h", + "win/initial_client_data.cc", + "win/initial_client_data.h", + "win/module_version.cc", + "win/module_version.h", + "win/nt_internals.cc", + "win/nt_internals.h", + "win/ntstatus_logging.cc", + "win/ntstatus_logging.h", + "win/process_info.cc", + "win/process_info.h", + "win/process_structs.h", + "win/registration_protocol_win.cc", + "win/registration_protocol_win.h", + "win/safe_terminate_process.h", + "win/scoped_handle.cc", + "win/scoped_handle.h", + "win/scoped_local_alloc.cc", + "win/scoped_local_alloc.h", + "win/scoped_process_suspend.cc", + "win/scoped_process_suspend.h", + "win/scoped_set_event.cc", + "win/scoped_set_event.h", + "win/session_end_watcher.cc", + "win/session_end_watcher.h", + "win/termination_codes.h", + "win/xp_compat.h", + ] + # There's no ml.exe yet in cross builds, so provide broken-but-not-asm # versions of the functions defined in .asm files. # @@ -298,18 +295,20 @@ static_library("util") { } } - public_configs = [ "//third_party/crashpad/crashpad:crashpad_config" ] + public_configs = [ "..:crashpad_config" ] # Include files from here and generated files starting with "util". include_dirs = [ "$root_gen_dir/third_party/crashpad/crashpad" ] public_deps = [ - "//third_party/crashpad/crashpad/compat", + "../compat", ] deps = [ "//base", - "//third_party/crashpad/crashpad/third_party/zlib", + + # TODO(GN): Should this point at Chromium's zlib when in Chromium? + "../third_party/zlib", ] if (is_mac) { @@ -345,10 +344,6 @@ source_set("util_test") { "file/file_reader_test.cc", "file/filesystem_test.cc", "file/string_file_test.cc", - "mac/launchd_test.mm", - "mac/mac_util_test.mm", - "mac/service_management_test.mm", - "mac/xattr_test.cc", "misc/arraysize_unsafe_test.cc", "misc/clock_test.cc", "misc/from_pointer_cast_test.cc", @@ -386,23 +381,14 @@ source_set("util_test") { "thread/thread_log_messages_test.cc", "thread/thread_test.cc", "thread/worker_thread_test.cc", - "win/capture_context_test.cc", - "win/command_line_test.cc", - "win/critical_section_with_debug_info_test.cc", - "win/exception_handler_server_test.cc", - "win/get_function_test.cc", - "win/handle_test.cc", - "win/initial_client_data_test.cc", - "win/process_info_test.cc", - "win/registration_protocol_win_test.cc", - "win/safe_terminate_process_test.cc", - "win/scoped_process_suspend_test.cc", - "win/session_end_watcher_test.cc", ] if (is_mac) { - # mach/ are not globally filtered. sources += [ + "mac/launchd_test.mm", + "mac/mac_util_test.mm", + "mac/service_management_test.mm", + "mac/xattr_test.cc", "mach/child_port_handshake_test.cc", "mach/child_port_server_test.cc", "mach/composite_mach_message_server_test.cc", @@ -421,6 +407,23 @@ source_set("util_test") { ] } + if (is_win) { + sources += [ + "win/capture_context_test.cc", + "win/command_line_test.cc", + "win/critical_section_with_debug_info_test.cc", + "win/exception_handler_server_test.cc", + "win/get_function_test.cc", + "win/handle_test.cc", + "win/initial_client_data_test.cc", + "win/process_info_test.cc", + "win/registration_protocol_win_test.cc", + "win/safe_terminate_process_test.cc", + "win/scoped_process_suspend_test.cc", + "win/session_end_watcher_test.cc", + ] + } + data = [ "net/http_transport_test_server.py", "net/testdata/", @@ -428,13 +431,13 @@ source_set("util_test") { deps = [ ":util", + "../client", + "../compat", + "../test", + "../third_party/zlib", "//base", "//testing/gmock", "//testing/gtest", - "//third_party/crashpad/crashpad/client", - "//third_party/crashpad/crashpad/compat", - "//third_party/crashpad/crashpad/test", - "//third_party/crashpad/crashpad/third_party/zlib", ] if (is_mac) { From 5e16410ad4485977424dfd1f369034d8f555725b Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Wed, 29 Nov 2017 10:08:56 -0500 Subject: [PATCH 038/326] fuchsia: Download the toolchain to distinct directories per build host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I like to share a single Crashpad checkout between my non-virtual machine and some virtual machines. Downloaded toolchains, which vary by build host configuration, must go in paths named for the build host. (Chromium doesn’t do this, and it bugs me.) Rather than downloading the Fuchsia toolchain to third_party/fuchsia/clang, this puts it in third_party/fuchsia/clang/{mac,linux}-amd64. The Fuchsia SDK is only published on cipd for linux-amd64, but the sysroot that it contains ought to be perfectly functional on any suitably-equipped build host, so this also checks out that package unconditionally. Bug: crashpad:196 Change-Id: Iabd4f2dd1e2c06a3f7208b5c40432619983919ea Reviewed-on: https://chromium-review.googlesource.com/794537 Commit-Queue: Mark Mentovai Reviewed-by: Scott Graham --- DEPS | 110 ++++++++++++++++++--------- third_party/fuchsia/toolchain.ensure | 19 ----- 2 files changed, 75 insertions(+), 54 deletions(-) delete mode 100644 third_party/fuchsia/toolchain.ensure diff --git a/DEPS b/DEPS index c37cde74..71f89242 100644 --- a/DEPS +++ b/DEPS @@ -38,9 +38,9 @@ hooks = [ { 'name': 'clang_format_mac', 'pattern': '.', + 'condition': 'host_os == "mac"', 'action': [ 'download_from_google_storage', - '--platform=^darwin$', '--no_resume', '--no_auth', '--bucket=chromium-clang-format', @@ -48,25 +48,12 @@ hooks = [ 'buildtools/mac/clang-format.sha1', ], }, - { - 'name': 'clang_format_win', - 'pattern': '.', - 'action': [ - 'download_from_google_storage', - '--platform=^win32$', - '--no_resume', - '--no_auth', - '--bucket=chromium-clang-format', - '--sha1_file', - 'buildtools/win/clang-format.exe.sha1', - ], - }, { 'name': 'clang_format_linux', 'pattern': '.', + 'condition': 'host_os == "linux"', 'action': [ 'download_from_google_storage', - '--platform=^linux2?$', '--no_resume', '--no_auth', '--bucket=chromium-clang-format', @@ -75,11 +62,24 @@ hooks = [ ], }, { - 'name': 'gn_mac', + 'name': 'clang_format_win', 'pattern': '.', + 'condition': 'host_os == "win"', + 'action': [ + 'download_from_google_storage', + '--no_resume', + '--no_auth', + '--bucket=chromium-clang-format', + '--sha1_file', + 'buildtools/win/clang-format.exe.sha1', + ], + }, + { + 'name': 'gn_mac', + 'pattern': '.', + 'condition': 'host_os == "mac"', 'action': [ 'download_from_google_storage', - '--platform=^darwin$', '--no_resume', '--no_auth', '--bucket=chromium-gn', @@ -87,25 +87,12 @@ hooks = [ 'buildtools/mac/gn.sha1', ], }, - { - 'name': 'gn_win', - 'pattern': '.', - 'action': [ - 'download_from_google_storage', - '--platform=^win32$', - '--no_resume', - '--no_auth', - '--bucket=chromium-gn', - '--sha1_file', - 'buildtools/win/gn.exe.sha1', - ], - }, { 'name': 'gn_linux', 'pattern': '.', + 'condition': 'host_os == "linux"', 'action': [ 'download_from_google_storage', - '--platform=^linux2?$', '--no_resume', '--no_auth', '--bucket=chromium-gn', @@ -114,14 +101,67 @@ hooks = [ ], }, { - 'name': 'fuchsia_clang', + 'name': 'gn_win', + 'pattern': '.', + 'condition': 'host_os == "win"', + 'action': [ + 'download_from_google_storage', + '--no_resume', + '--no_auth', + '--bucket=chromium-gn', + '--sha1_file', + 'buildtools/win/gn.exe.sha1', + ], + }, + { + # This uses “cipd install” so that mac-amd64 and linux-amd64 can coexist + # peacefully. “cipd ensure” would remove the Linux package when running on a + # macOS build host and vice-versa. https://crbug.com/789364. + 'name': 'fuchsia_clang_mac', + 'pattern': '.', + 'condition': 'checkout_fuchsia and host_os == "mac"', + 'action': [ + 'cipd', + 'install', + 'fuchsia/clang/mac-amd64', + 'latest', + '-root', 'crashpad/third_party/fuchsia/clang/mac-amd64', + '-log-level', 'info', + ], + }, + { + # This uses “cipd install” so that mac-amd64 and linux-amd64 can coexist + # peacefully. “cipd ensure” would remove the macOS package when running on a + # Linux build host and vice-versa. https://crbug.com/789364. + 'name': 'fuchsia_clang_linux', + 'pattern': '.', + 'condition': 'checkout_fuchsia and host_os == "linux"', + 'action': [ + 'cipd', + 'install', + 'fuchsia/clang/linux-amd64', + 'latest', + '-root', 'crashpad/third_party/fuchsia/clang/linux-amd64', + '-log-level', 'info', + ], + }, + { + # The SDK is keyed to the host system because it contains build tools. + # Currently, linux-amd64 is the only SDK published (see + # https://chrome-infra-packages.appspot.com/#/?path=fuchsia/sdk). As long as + # this is the case, use that SDK package even on other build hosts. The + # sysroot (containing headers and libraries) and other components are + # related to the target and should be functional with an appropriate + # toolchain that runs on the build host (fuchsia_clang, above). + 'name': 'fuchsia_sdk', 'pattern': '.', 'condition': 'checkout_fuchsia', 'action': [ 'cipd', - 'ensure', - '-ensure-file', 'crashpad/third_party/fuchsia/toolchain.ensure', - '-root', 'crashpad/third_party/fuchsia', + 'install', + 'fuchsia/sdk/linux-amd64', + 'latest', + '-root', 'crashpad/third_party/fuchsia/sdk/linux-amd64', '-log-level', 'info', ], }, diff --git a/third_party/fuchsia/toolchain.ensure b/third_party/fuchsia/toolchain.ensure deleted file mode 100644 index 00f9b788..00000000 --- a/third_party/fuchsia/toolchain.ensure +++ /dev/null @@ -1,19 +0,0 @@ -# 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. - -@Subdir clang -fuchsia/clang/${os}-${arch} latest - -@Subdir sdk -fuchsia/sdk/${os=linux}-${arch} latest From 1ee9fee37c4458af93422fdc843cc71536762934 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Wed, 29 Nov 2017 10:39:48 -0500 Subject: [PATCH 039/326] Update buildtools, gtest, and gyp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update buildtools to 505de88083136eefd056e5ee4ca0f01fe9b33de8 97d73b1a3ddb Do not apply clang coverage to libc++ and libc++abi e043d81e9185 Roll gn 87530f977a..2c6f9299ed (r503394:r509075) 3275a099f3c1 Output warning only in verbose mode in Java deps checker 011c39ca57fa Roll libcxx, libcxxabi, and libunwind df36429e1847 Add vector.cpp to libc++ sources b0f94d04e26f Java checker only warns multiple definitions of touched files c79d52ba0fb4 Merge "Java checker only warns multiple definitions of touched files" 817a502753ff Prepare Android for building libc++ in-tree 05f9bc9b8599 Fix undefined deps on arm64 2a2f666c35b7 Update libc++, libc++abi, and libunwind READMEs 73ddd64be624 Roll gn 2c6f9299ed..157d5de447 (r509075:r514519) 93a751e41bd9 Fix git cl presubmit failure caused by java_checker.py scan deleted files 6cce6ca960b9 Fix libc++ build when using gcc 7f134c70f0f8 Allow java file multiple definition for excluded paths in java checker 9c40f80c9998 Merge "Fix libc++ build when using gcc" 8c7174c87fd6 Always hide libunwind symbols on desktop Linux 3196d83d5c1f Fix typo: fuschia -> fuchsia 461b345a815c Add migration code for no_exceptions configs 505de8808313 Finish migration to exceptions configs Update gtest to d175c8bf823e709d570772b038757fadf63bc632 509f7fe84094 Update googletest README.md 24696c3958f0 Merge branch 'master' into master 3eaba9f07c5f Merge branch 'master' into master 4597ec587ca2 Updated README with information about C runtime dynamic/static linking issues in Windows ecb1c3ddb6cf #1282: Doc typo fix 963932e7f37b Merge pull request #1292 from DariuszOstolski/master 3282f9ae018f Merge pull request #1288 from joealam/master dfed97a69ac3 Workaround for Travis issue https://github.com/travis-ci /travis-ci/issues/8552 5c9543547e5f Merge pull request #1297 from gennadiycivil/master 34aaf58c8b1b Revert "Workaround for Travis issue https://github.com /travis-ci/travis-ci/is…" 27be0dfb53a1 Merge pull request #1298 from google/revert-1297-master 54c2648bff0e Workaround for Travis issue https://goo.gl/d5eV8o 48986f9d4c8e Merge branch 'master' into master 69e48e92de43 Merge pull request #1300 from gennadiycivil/master c208d8df23d6 Merge branch 'master' into master 1beff241c359 googletest: Add GTEST_API_ attribute to ThreadLocal class 6d0bb75c81bc Merge pull request #1139 from chehsunliu/master 77380cddf771 Enable C++11 features for VS2015 and VS2017 2641b021fc2a Fix tests with VS2015 and VS2017 840c711e7bd7 Fix gmock tests when std::unary_function unavailable 7684db32712e Merge pull request #1218 from KindDragon/vs-build-fix 20e2de7d8bba Remove gcc 6 misleading indentations 060783b7d2b7 Merge branch 'master' into gtestapifix e93a15c5a59e Merge pull request #913 from Romain-Geissler/fix-gcc- misleading-indentation-warning 3121b2049e30 Merge pull request #1304 from m-gupta/gtestapifix b153bfd8f503 Enable CI for VS2017 8866af0386d7 remove markdown stars (bold) from code examples 43d6ad75bc4f Merge pull request #1308 from KindDragon/vs2017-ci d175c8bf823e Merge pull request #1313 from aninf-wo/heth/fix-faq-stars- issue-1312 Update gyp to 5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f 365ffa057dc3 Flip to LUCI for tryjobs 5e2b3ddde7cd Remove Rietveld CQ config Change-Id: I20ca6d84ac79cb85d73934392ff1655de2f147c5 Reviewed-on: https://chromium-review.googlesource.com/797011 Reviewed-by: Scott Graham Commit-Queue: Mark Mentovai --- DEPS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 71f89242..fa58f6f8 100644 --- a/DEPS +++ b/DEPS @@ -19,13 +19,13 @@ vars = { deps = { 'buildtools': Var('chromium_git') + '/chromium/buildtools.git@' + - 'f6d165d9d842ddd29056c127a5f3a3c5d8e0d2e3', + '505de88083136eefd056e5ee4ca0f01fe9b33de8', 'crashpad/third_party/gtest/gtest': Var('chromium_git') + '/external/github.com/google/googletest@' + - '7b6561c56e353100aca8458d7bc49c4e0119bae8', + 'd175c8bf823e709d570772b038757fadf63bc632', 'crashpad/third_party/gyp/gyp': Var('chromium_git') + '/external/gyp@' + - 'f72586209ecbf70b71ce690f2182ebe51669cbb3', + '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + 'dd0c3e9680ae3c4c22f2221a2a75e48dd4a562ec', From ef3ce94fbfbb8a172038205687ae709a0a1dcbc3 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Wed, 29 Nov 2017 13:26:55 -0500 Subject: [PATCH 040/326] Python 3 support for Python scripts (without compromising Python 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s better to be prepared for the future than…to not be. This is mostly the result of running 2to3 on all .py files, with some small shims to maintain compatibility with Python 2. http_transport_test_server.py was slightly more involved, requiring many objects to change from “str” to “bytes”. The #! lines and invokers still haven’t changed, so these scripts will still normally be interpreted by Python 2. Change-Id: Idda3c5650f967401a5942c4d8abee86151642a2e Reviewed-on: https://chromium-review.googlesource.com/797434 Reviewed-by: Robert Sesek Commit-Queue: Mark Mentovai --- build/gyp_crashpad.py | 5 +- build/run_tests.py | 16 +++--- doc/support/generate_doxygen.py | 2 +- snapshot/win/end_to_end_test.py | 28 +++++----- util/net/http_transport_test_server.py | 74 ++++++++++++++++++-------- 5 files changed, 80 insertions(+), 45 deletions(-) diff --git a/build/gyp_crashpad.py b/build/gyp_crashpad.py index d3cef710..5a68b2ac 100755 --- a/build/gyp_crashpad.py +++ b/build/gyp_crashpad.py @@ -17,6 +17,9 @@ import os import sys +if sys.version_info[0] < 3: + range = xrange + def ChooseDependencyPath(local_path, external_path): """Chooses between a dependency located at local path and an external path. @@ -78,7 +81,7 @@ def main(args): # Check to make sure that no target_arch was specified. target_arch may be # set during a cross build, such as a cross build for Android. has_target_arch = False - for arg_index in xrange(0, len(args)): + for arg_index in range(0, len(args)): arg = args[arg_index] if (arg.startswith('-Dtarget_arch=') or (arg == '-D' and arg_index + 1 < len(args) and diff --git a/build/run_tests.py b/build/run_tests.py index 10bd052d..d577677a 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import print_function + import os import subprocess import sys @@ -25,7 +27,7 @@ import sys # location in the recipe. def main(args): if len(args) != 1: - print >> sys.stderr, 'usage: run_tests.py ' + print('usage: run_tests.py ', file=sys.stderr) return 1 crashpad_dir = \ @@ -54,16 +56,16 @@ def main(args): ] for test in tests: - print '-' * 80 - print test - print '-' * 80 + print('-' * 80) + print(test) + print('-' * 80) subprocess.check_call(os.path.join(binary_dir, test)) if sys.platform == 'win32': script = 'snapshot/win/end_to_end_test.py' - print '-' * 80 - print script - print '-' * 80 + print('-' * 80) + print(script) + print('-' * 80) subprocess.check_call( [sys.executable, os.path.join(crashpad_dir, script), binary_dir]) diff --git a/doc/support/generate_doxygen.py b/doc/support/generate_doxygen.py index 11dd0ad0..a93028df 100755 --- a/doc/support/generate_doxygen.py +++ b/doc/support/generate_doxygen.py @@ -37,7 +37,7 @@ def main(args): elif os.path.exists(output_dir): os.unlink(output_dir) - os.makedirs(output_dir, 0755) + os.makedirs(output_dir, 0o755) doxy_file = os.path.join('doc', 'support', 'crashpad.doxy') subprocess.check_call(['doxygen', doxy_file]) diff --git a/snapshot/win/end_to_end_test.py b/snapshot/win/end_to_end_test.py index c640a548..a08cfd1a 100755 --- a/snapshot/win/end_to_end_test.py +++ b/snapshot/win/end_to_end_test.py @@ -14,6 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import print_function + import os import platform import pywintypes @@ -99,7 +101,7 @@ def NamedPipeExistsAndReady(pipe_name): """ try: win32pipe.WaitNamedPipe(pipe_name, win32pipe.NMPWAIT_WAIT_FOREVER) - except pywintypes.error, e: + except pywintypes.error as e: if e[0] == winerror.ERROR_FILE_NOT_FOUND: return False raise @@ -135,7 +137,7 @@ def GetDumpFromProgram( printed = False while not NamedPipeExistsAndReady(pipe_name): if not printed: - print 'Waiting for crashpad_handler to be ready...' + print('Waiting for crashpad_handler to be ready...') printed = True time.sleep(0.001) @@ -145,7 +147,7 @@ def GetDumpFromProgram( os.path.join(out_dir, 'crashpad_handler.com'), test_database] + list(args)) - print 'Running %s' % os.path.basename(command[0]) + print('Running %s' % os.path.basename(command[0])) exit_code = subprocess.call(command) if exit_code != expect_exit_code: raise subprocess.CalledProcessError(exit_code, executable_name) @@ -219,16 +221,16 @@ class CdbRun(object): if match_obj: # Matched. Consume up to end of match. self.out = self.out[match_obj.end(0):] - print 'ok - %s' % message + print('ok - %s' % message) sys.stdout.flush() else: - print >>sys.stderr, '-' * 80 - print >>sys.stderr, 'FAILED - %s' % message - print >>sys.stderr, '-' * 80 - print >>sys.stderr, 'did not match:\n %s' % pattern - print >>sys.stderr, '-' * 80 - print >>sys.stderr, 'remaining output was:\n %s' % self.out - print >>sys.stderr, '-' * 80 + print('-' * 80, file=sys.stderr) + print('FAILED - %s' % message, file=sys.stderr) + print('-' * 80, file=sys.stderr) + print('did not match:\n %s' % pattern, file=sys.stderr) + print('-' * 80, file=sys.stderr) + print('remaining output was:\n %s' % self.out, file=sys.stderr) + print('-' * 80, file=sys.stderr) sys.stderr.flush() global g_had_failures g_had_failures = True @@ -430,12 +432,12 @@ def RunTests(cdb_path, def main(args): try: if len(args) != 1: - print >>sys.stderr, 'must supply binary dir' + print('must supply binary dir', file=sys.stderr) return 1 cdb_path = GetCdbPath() if not cdb_path: - print >>sys.stderr, 'could not find cdb' + print('could not find cdb', file=sys.stderr) return 1 # Make sure we can download Windows symbols. diff --git a/util/net/http_transport_test_server.py b/util/net/http_transport_test_server.py index 7ea15719..8cb10a9b 100755 --- a/util/net/http_transport_test_server.py +++ b/util/net/http_transport_test_server.py @@ -30,17 +30,25 @@ because parsing chunked encoding is safer and easier in a memory-safe language. This could easily have been written in C++ instead. """ -import BaseHTTPServer +import os import struct import sys import zlib +if sys.platform == 'win32': + import msvcrt + +if sys.version_info[0] < 3: + import BaseHTTPServer as http_server +else: + import http.server as http_server + class BufferedReadFile(object): """A File-like object that stores all read contents into a buffer.""" def __init__(self, real_file): self.file = real_file - self.buffer = "" + self.buffer = b'' def read(self, size=-1): buf = self.file.read(size) @@ -59,27 +67,27 @@ class BufferedReadFile(object): self.file.close() -class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): +class RequestHandler(http_server.BaseHTTPRequestHandler): # Everything to be written to stdout is collected into this string. It can’t # be written to stdout until after the HTTP transaction is complete, because # stdout is a pipe being read by a test program that’s also the HTTP client. # The test program expects to complete the entire HTTP transaction before it # even starts reading this script’s stdout. If the stdout pipe buffer fills up # during an HTTP transaction, deadlock would result. - raw_request = '' + raw_request = b'' response_code = 500 - response_body = '' + response_body = b'' def handle_one_request(self): # Wrap the rfile in the buffering file object so that the raw header block # can be written to stdout after it is parsed. self.rfile = BufferedReadFile(self.rfile) - BaseHTTPServer.BaseHTTPRequestHandler.handle_one_request(self) + http_server.BaseHTTPRequestHandler.handle_one_request(self) def do_POST(self): RequestHandler.raw_request = self.rfile.buffer - self.rfile.buffer = '' + self.rfile.buffer = b'' if self.headers.get('Transfer-Encoding', '').lower() == 'chunked': if 'Content-Length' in self.headers: @@ -102,13 +110,13 @@ class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): self.end_headers() if self.response_code == 200: self.wfile.write(self.response_body) - self.wfile.write('\r\n') + self.wfile.write(b'\r\n') def handle_chunked_encoding(self): - """This parses a "Transfer-Encoding: Chunked" body in accordance with - RFC 7230 §4.1. This returns the result as a string. + """This parses a "Transfer-Encoding: Chunked" body in accordance with RFC + 7230 §4.1. This returns the result as a string. """ - body = '' + body = b'' chunk_size = self.read_chunk_size() while chunk_size > 0: # Read the body. @@ -120,7 +128,7 @@ class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): if chunk_size == 0: # Read through any trailer fields. trailer_line = self.rfile.readline() - while trailer_line.strip() != '': + while trailer_line.strip() != b'': trailer_line = self.rfile.readline() # Read the chunk size. @@ -131,10 +139,10 @@ class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): # Read the whole line, including the \r\n. chunk_size_and_ext_line = self.rfile.readline() # Look for a chunk extension. - chunk_size_end = chunk_size_and_ext_line.find(';') + chunk_size_end = chunk_size_and_ext_line.find(b';') if chunk_size_end == -1: # No chunk extensions; just encounter the end of line. - chunk_size_end = chunk_size_and_ext_line.find('\r') + chunk_size_end = chunk_size_and_ext_line.find(b'\r') if chunk_size_end == -1: self.send_response(400) # Bad request. return -1 @@ -145,29 +153,49 @@ class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): pass +def StdioBinaryEquivalent(file): + """Return a file object equivalent to sys.stdin or sys.stdout capable of + reading or writing binary “bytes”. + + struct.unpack consumes “bytes”, and struct.pack produces “bytes”. These are + distinct from “str” in Python 3 (but not 2). In order to read and write these + from stdin and stdout, the underlying binary buffer must be used in place of + the upper-layer text wrapper. This function returns a suitable file. + + There is no underlying buffer in Python 2, but on Windows, the file mode must + still be set to binary in order to cleanly pass binary data. Note that in this + case, the mode of |file| itself is changed, as it’s not distinct from the + returned file. + """ + if hasattr(file, 'buffer'): + file = file.buffer + elif sys.platform == 'win32': + msvcrt.setmode(file.fileno(), os.O_BINARY) + return file + + def Main(): - if sys.platform == 'win32': - import os, msvcrt - msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) + in_file = StdioBinaryEquivalent(sys.stdin) + out_file = StdioBinaryEquivalent(sys.stdout) # Start the server. - server = BaseHTTPServer.HTTPServer(('127.0.0.1', 0), RequestHandler) + server = http_server.HTTPServer(('127.0.0.1', 0), RequestHandler) # Write the port as an unsigned short to the parent process. - sys.stdout.write(struct.pack('=H', server.server_address[1])) - sys.stdout.flush() + out_file.write(struct.pack('=H', server.server_address[1])) + out_file.flush() # Read the desired test response code as an unsigned short and the desired # response body as a 16-byte string from the parent process. RequestHandler.response_code, RequestHandler.response_body = \ - struct.unpack('=H16s', sys.stdin.read(struct.calcsize('=H16s'))) + struct.unpack('=H16s', in_file.read(struct.calcsize('=H16s'))) # Handle the request. server.handle_request() # Share the entire request with the test program, which will validate it. - sys.stdout.write(RequestHandler.raw_request) - sys.stdout.flush() + out_file.write(RequestHandler.raw_request) + out_file.flush() if __name__ == '__main__': Main() From 3f5c939c848aba2678324c5b31e6c0b2d77dfd04 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 29 Nov 2017 11:04:47 -0800 Subject: [PATCH 041/326] gn: Next step in getting compilation working - Correctly sets target_cpu and current_cpu so correct toolchain can be used on Fuchsia. - Introduces GN argument "crashpad_in_chromium" which defaults to false. Used to set CRASHPAD_IN_CHROMIUM define, determine which zlib path to use, and how to package the test targets into binaries (one big one in Chromium, separate in Crashpad). Bug: crashpad:79, crashpad:196 Change-Id: If6560dc064308ed6f8bf7c75cf74f684a3522e8b Reviewed-on: https://chromium-review.googlesource.com/797354 Reviewed-by: Mark Mentovai Reviewed-by: Robert Sesek Commit-Queue: Scott Graham --- BUILD.gn | 65 ++++++++++++++++++++++++++++------ build/BUILD.gn | 25 +++++++++++++ build/BUILDCONFIG.gn | 40 +++++++++++++++++---- build/crashpad_in_chromium.gni | 17 +++++++++ third_party/zlib/BUILD.gn | 21 ++++++++--- 5 files changed, 147 insertions(+), 21 deletions(-) create mode 100644 build/BUILD.gn create mode 100644 build/crashpad_in_chromium.gni diff --git a/BUILD.gn b/BUILD.gn index 88559c42..6ca07512 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -13,19 +13,64 @@ # limitations under the License. import("//testing/test.gni") +import("build/crashpad_in_chromium.gni") config("crashpad_config") { include_dirs = [ "." ] } -test("crashpad_tests") { - deps = [ - "client:client_test", - "handler:handler_test", - "minidump:minidump_test", - "snapshot:snapshot_test", - "test:gmock_main", - "test:test_test", - "util:util_test", - ] +if (crashpad_in_chromium) { + test("crashpad_tests") { + deps = [ + "client:client_test", + "handler:handler_test", + "minidump:minidump_test", + "snapshot:snapshot_test", + "test:gmock_main", + "test:test_test", + "util:util_test", + ] + } +} else { + test("crashpad_client_test") { + deps = [ + "client:client_test", + "test:gmock_main", + ] + } + + test("crashpad_handler_test") { + deps = [ + "handler:handler_test", + "test:gtest_main", + ] + } + + test("crashpad_minidump_test") { + deps = [ + "minidump:minidump_test", + "test:gtest_main", + ] + } + + test("crashpad_snapshot_test") { + deps = [ + "snapshot:snapshot_test", + "test:gtest_main", + ] + } + + test("crashpad_test_test") { + deps = [ + "test:gmock_main", + "test:test_test", + ] + } + + test("crashpad_util_test") { + deps = [ + "test:gmock_main", + "util:util_test", + ] + } } diff --git a/build/BUILD.gn b/build/BUILD.gn new file mode 100644 index 00000000..e60cd0c6 --- /dev/null +++ b/build/BUILD.gn @@ -0,0 +1,25 @@ +# 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. + +# When building in Chromium, these configs is used to set #defines that indicate +# whether code is being built standalone, or in Chromium, or potentially in some +# other configutation. + +import("crashpad_in_chromium.gni") + +config("crashpad_in_chromium") { + if (crashpad_in_chromium) { + defines = [ "CRASHPAD_IN_CHROMIUM" ] + } +} diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn index d85494ba..840978fa 100644 --- a/build/BUILDCONFIG.gn +++ b/build/BUILDCONFIG.gn @@ -20,6 +20,14 @@ if (current_os == "") { current_os = target_os } +if (target_cpu == "") { + target_cpu = host_cpu +} + +if (current_cpu == "") { + current_cpu = target_cpu +} + declare_args() { is_debug = false is_clang = current_os == "mac" || current_os == "fuchsia" @@ -55,12 +63,30 @@ if (is_win) { "//third_party/mini_chromium/mini_chromium/build:gcc_like_toolchain") } -set_defaults("static_library") { - configs = [ - "//third_party/mini_chromium/mini_chromium/build:default", +_default_configs = [ + "//third_party/mini_chromium/mini_chromium/build:default", - # This (no-op) is added here so that build files that expect to be able to - # remove it can do so without causing an error. - "//build/config/compiler:chromium_code", - ] + # This (no-op) is added here so that build files that expect to be able to + # remove it can do so without causing an error. + "//build/config/compiler:chromium_code", +] + +set_defaults("source_set") { + configs = _default_configs +} + +set_defaults("static_library") { + configs = _default_configs +} + +set_defaults("executable") { + configs = _default_configs +} + +set_defaults("loadable_module") { + configs = _default_configs +} + +set_defaults("shared_library") { + configs = _default_configs } diff --git a/build/crashpad_in_chromium.gni b/build/crashpad_in_chromium.gni new file mode 100644 index 00000000..568c52e7 --- /dev/null +++ b/build/crashpad_in_chromium.gni @@ -0,0 +1,17 @@ +# 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. + +declare_args() { + crashpad_in_chromium = false +} diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn index db158c7d..017dfff8 100644 --- a/third_party/zlib/BUILD.gn +++ b/third_party/zlib/BUILD.gn @@ -12,13 +12,26 @@ # See the License for the specific language governing permissions and # limitations under the License. +import("../../build/crashpad_in_chromium.gni") + config("zlib_config") { - defines = [ "CRASHPAD_ZLIB_SOURCE_CHROMIUM" ] + if (crashpad_in_chromium) { + defines = [ "CRASHPAD_ZLIB_SOURCE_CHROMIUM" ] + } else { + defines = [ "CRASHPAD_ZLIB_SOURCE_EMBEDDED" ] + } } group("zlib") { public_configs = [ ":zlib_config" ] - public_deps = [ - "//third_party/zlib:zlib", - ] + + if (crashpad_in_chromium) { + public_deps = [ + "//third_party/zlib", + ] + } else { + public_deps = [ + "//third_party/zlib/zlib", + ] + } } From 593ede52e053c8e2962ae9a08cecf08ba96b7a90 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Wed, 29 Nov 2017 14:43:51 -0500 Subject: [PATCH 042/326] Fixes for drive-by review comments after ef3ce94fbfbb Change-Id: I28edc00549d51576ab553f401235aa1d9f669232 Reviewed-on: https://chromium-review.googlesource.com/797335 Reviewed-by: Scott Graham Commit-Queue: Mark Mentovai --- build/gyp_crashpad.py | 3 --- util/net/http_transport_test_server.py | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/build/gyp_crashpad.py b/build/gyp_crashpad.py index 5a68b2ac..84856606 100755 --- a/build/gyp_crashpad.py +++ b/build/gyp_crashpad.py @@ -17,9 +17,6 @@ import os import sys -if sys.version_info[0] < 3: - range = xrange - def ChooseDependencyPath(local_path, external_path): """Chooses between a dependency located at local path and an external path. diff --git a/util/net/http_transport_test_server.py b/util/net/http_transport_test_server.py index 8cb10a9b..d9777cbc 100755 --- a/util/net/http_transport_test_server.py +++ b/util/net/http_transport_test_server.py @@ -43,6 +43,7 @@ if sys.version_info[0] < 3: else: import http.server as http_server + class BufferedReadFile(object): """A File-like object that stores all read contents into a buffer.""" From 9465fc72ad901ae1f28a8a3269d79d931c7024c6 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 29 Nov 2017 11:59:18 -0800 Subject: [PATCH 043/326] gn: Move sources out to explicit blocks This avoids relying on set_sources_assignment_filter, and so gets closer to a correct set of files to build on Fuchsia. Bug: crashpad:79, crashpad:196 Change-Id: Ib7daa5137935113c6645b72eb1dedd943a9db96e Reviewed-on: https://chromium-review.googlesource.com/797672 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- client/BUILD.gn | 19 +++-- snapshot/BUILD.gn | 179 +++++++++++++++++++++++++--------------------- test/BUILD.gn | 65 ++++++++++------- 3 files changed, 150 insertions(+), 113 deletions(-) diff --git a/client/BUILD.gn b/client/BUILD.gn index 2fa7110b..5361dda2 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -22,11 +22,7 @@ static_library("client") { "annotation_list.h", "crash_report_database.cc", "crash_report_database.h", - "crash_report_database_mac.mm", - "crash_report_database_win.cc", "crashpad_client.h", - "crashpad_client_mac.cc", - "crashpad_client_win.cc", "crashpad_info.cc", "crashpad_info.h", "prune_crash_reports.cc", @@ -36,15 +32,24 @@ static_library("client") { "simple_address_range_bag.h", "simple_string_dictionary.h", "simulate_crash.h", - "simulate_crash_mac.cc", - "simulate_crash_mac.h", - "simulate_crash_win.h", ] if (is_mac) { sources += [ "capture_context_mac.S", "capture_context_mac.h", + "crash_report_database_mac.mm", + "crashpad_client_mac.cc", + "simulate_crash_mac.cc", + "simulate_crash_mac.h", + ] + } + + if (is_win) { + sources += [ + "crash_report_database_win.cc", + "crashpad_client_win.cc", + "simulate_crash_win.h", ] } diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 2b01fc20..2c46d7be 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -29,43 +29,6 @@ static_library("snapshot") { "exception_snapshot.h", "handle_snapshot.cc", "handle_snapshot.h", - "mac/cpu_context_mac.cc", - "mac/cpu_context_mac.h", - "mac/exception_snapshot_mac.cc", - "mac/exception_snapshot_mac.h", - "mac/mach_o_image_annotations_reader.cc", - "mac/mach_o_image_annotations_reader.h", - "mac/mach_o_image_reader.cc", - "mac/mach_o_image_reader.h", - "mac/mach_o_image_segment_reader.cc", - "mac/mach_o_image_segment_reader.h", - "mac/mach_o_image_symbol_table_reader.cc", - "mac/mach_o_image_symbol_table_reader.h", - "mac/memory_snapshot_mac.cc", - "mac/memory_snapshot_mac.h", - "mac/module_snapshot_mac.cc", - "mac/module_snapshot_mac.h", - "mac/process_reader.cc", - "mac/process_reader.h", - "mac/process_snapshot_mac.cc", - "mac/process_snapshot_mac.h", - "mac/process_types.cc", - "mac/process_types.h", - "mac/process_types/all.proctype", - "mac/process_types/annotation.proctype", - "mac/process_types/crashpad_info.proctype", - "mac/process_types/crashreporterclient.proctype", - "mac/process_types/custom.cc", - "mac/process_types/dyld_images.proctype", - "mac/process_types/flavors.h", - "mac/process_types/internal.h", - "mac/process_types/loader.proctype", - "mac/process_types/nlist.proctype", - "mac/process_types/traits.h", - "mac/system_snapshot_mac.cc", - "mac/system_snapshot_mac.h", - "mac/thread_snapshot_mac.cc", - "mac/thread_snapshot_mac.h", "memory_snapshot.h", "minidump/minidump_annotation_reader.cc", "minidump/minidump_annotation_reader.h", @@ -88,36 +51,83 @@ static_library("snapshot") { "thread_snapshot.h", "unloaded_module_snapshot.cc", "unloaded_module_snapshot.h", - "win/capture_memory_delegate_win.cc", - "win/capture_memory_delegate_win.h", - "win/cpu_context_win.cc", - "win/cpu_context_win.h", - "win/exception_snapshot_win.cc", - "win/exception_snapshot_win.h", - "win/memory_map_region_snapshot_win.cc", - "win/memory_map_region_snapshot_win.h", - "win/memory_snapshot_win.cc", - "win/memory_snapshot_win.h", - "win/module_snapshot_win.cc", - "win/module_snapshot_win.h", - "win/pe_image_annotations_reader.cc", - "win/pe_image_annotations_reader.h", - "win/pe_image_reader.cc", - "win/pe_image_reader.h", - "win/pe_image_resource_reader.cc", - "win/pe_image_resource_reader.h", - "win/process_reader_win.cc", - "win/process_reader_win.h", - "win/process_snapshot_win.cc", - "win/process_snapshot_win.h", - "win/process_subrange_reader.cc", - "win/process_subrange_reader.h", - "win/system_snapshot_win.cc", - "win/system_snapshot_win.h", - "win/thread_snapshot_win.cc", - "win/thread_snapshot_win.h", ] + if (is_mac) { + sources += [ + "mac/cpu_context_mac.cc", + "mac/cpu_context_mac.h", + "mac/exception_snapshot_mac.cc", + "mac/exception_snapshot_mac.h", + "mac/mach_o_image_annotations_reader.cc", + "mac/mach_o_image_annotations_reader.h", + "mac/mach_o_image_reader.cc", + "mac/mach_o_image_reader.h", + "mac/mach_o_image_segment_reader.cc", + "mac/mach_o_image_segment_reader.h", + "mac/mach_o_image_symbol_table_reader.cc", + "mac/mach_o_image_symbol_table_reader.h", + "mac/memory_snapshot_mac.cc", + "mac/memory_snapshot_mac.h", + "mac/module_snapshot_mac.cc", + "mac/module_snapshot_mac.h", + "mac/process_reader.cc", + "mac/process_reader.h", + "mac/process_snapshot_mac.cc", + "mac/process_snapshot_mac.h", + "mac/process_types.cc", + "mac/process_types.h", + "mac/process_types/all.proctype", + "mac/process_types/annotation.proctype", + "mac/process_types/crashpad_info.proctype", + "mac/process_types/crashreporterclient.proctype", + "mac/process_types/custom.cc", + "mac/process_types/dyld_images.proctype", + "mac/process_types/flavors.h", + "mac/process_types/internal.h", + "mac/process_types/loader.proctype", + "mac/process_types/nlist.proctype", + "mac/process_types/traits.h", + "mac/system_snapshot_mac.cc", + "mac/system_snapshot_mac.h", + "mac/thread_snapshot_mac.cc", + "mac/thread_snapshot_mac.h", + ] + } + + if (is_win) { + sources += [ + "win/capture_memory_delegate_win.cc", + "win/capture_memory_delegate_win.h", + "win/cpu_context_win.cc", + "win/cpu_context_win.h", + "win/exception_snapshot_win.cc", + "win/exception_snapshot_win.h", + "win/memory_map_region_snapshot_win.cc", + "win/memory_map_region_snapshot_win.h", + "win/memory_snapshot_win.cc", + "win/memory_snapshot_win.h", + "win/module_snapshot_win.cc", + "win/module_snapshot_win.h", + "win/pe_image_annotations_reader.cc", + "win/pe_image_annotations_reader.h", + "win/pe_image_reader.cc", + "win/pe_image_reader.h", + "win/pe_image_resource_reader.cc", + "win/pe_image_resource_reader.h", + "win/process_reader_win.cc", + "win/process_reader_win.h", + "win/process_snapshot_win.cc", + "win/process_snapshot_win.h", + "win/process_subrange_reader.cc", + "win/process_subrange_reader.h", + "win/system_snapshot_win.cc", + "win/system_snapshot_win.h", + "win/thread_snapshot_win.cc", + "win/thread_snapshot_win.h", + ] + } + if (target_cpu == "x86" || target_cpu == "x64") { sources += [ "x86/cpuid_reader.cc", @@ -208,26 +218,33 @@ source_set("snapshot_test") { sources = [ "cpu_context_test.cc", "crashpad_info_client_options_test.cc", - "mac/cpu_context_mac_test.cc", - "mac/mach_o_image_annotations_reader_test.cc", - "mac/mach_o_image_reader_test.cc", - "mac/mach_o_image_segment_reader_test.cc", - "mac/process_reader_test.cc", - "mac/process_types_test.cc", - "mac/system_snapshot_mac_test.cc", "minidump/process_snapshot_minidump_test.cc", - "win/cpu_context_win_test.cc", - "win/exception_snapshot_win_test.cc", - "win/extra_memory_ranges_test.cc", - "win/pe_image_annotations_reader_test.cc", - "win/pe_image_reader_test.cc", - "win/process_reader_win_test.cc", - "win/process_snapshot_win_test.cc", - "win/system_snapshot_win_test.cc", ] + if (is_mac) { + sources += [ + "mac/cpu_context_mac_test.cc", + "mac/mach_o_image_annotations_reader_test.cc", + "mac/mach_o_image_reader_test.cc", + "mac/mach_o_image_segment_reader_test.cc", + "mac/process_reader_test.cc", + "mac/process_types_test.cc", + "mac/system_snapshot_mac_test.cc", + ] + } + if (is_win) { - sources += [ "api/module_annotations_win_test.cc" ] + sources += [ + "api/module_annotations_win_test.cc", + "win/cpu_context_win_test.cc", + "win/exception_snapshot_win_test.cc", + "win/extra_memory_ranges_test.cc", + "win/pe_image_annotations_reader_test.cc", + "win/pe_image_reader_test.cc", + "win/process_reader_win_test.cc", + "win/process_snapshot_win_test.cc", + "win/system_snapshot_win_test.cc", + ] } else { sources += [ "posix/timezone_test.cc" ] } diff --git a/test/BUILD.gn b/test/BUILD.gn index 50f0c81f..441393b8 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -29,14 +29,6 @@ static_library("test") { "gtest_disabled.h", "hex_string.cc", "hex_string.h", - "mac/dyld.cc", - "mac/dyld.h", - "mac/exception_swallower.cc", - "mac/exception_swallower.h", - "mac/mach_errors.cc", - "mac/mach_errors.h", - "mac/mach_multiprocess.cc", - "mac/mach_multiprocess.h", "main_arguments.cc", "main_arguments.h", "multiprocess.h", @@ -52,16 +44,36 @@ static_library("test") { "scoped_temp_dir_win.cc", "test_paths.cc", "test_paths.h", - "win/child_launcher.cc", - "win/child_launcher.h", - "win/win_child_process.cc", - "win/win_child_process.h", - "win/win_multiprocess.cc", - "win/win_multiprocess.h", - "win/win_multiprocess_with_temp_dir.cc", - "win/win_multiprocess_with_temp_dir.h", ] + if (is_mac) { + libs = [ "bsm" ] + deps += [ "//third_party/crashpad/crashpad/handler" ] + sources += [ + "mac/dyld.cc", + "mac/dyld.h", + "mac/exception_swallower.cc", + "mac/exception_swallower.h", + "mac/mach_errors.cc", + "mac/mach_errors.h", + "mac/mach_multiprocess.cc", + "mac/mach_multiprocess.h", + ] + } + + if (is_win) { + sources += [ + "win/child_launcher.cc", + "win/child_launcher.h", + "win/win_child_process.cc", + "win/win_child_process.h", + "win/win_multiprocess.cc", + "win/win_multiprocess.h", + "win/win_multiprocess_with_temp_dir.cc", + "win/win_multiprocess_with_temp_dir.h", + ] + } + public_configs = [ "..:crashpad_config" ] defines = [ "CRASHPAD_IN_CHROMIUM" ] @@ -77,11 +89,6 @@ static_library("test") { "//base", "//testing/gtest", ] - - if (is_mac) { - libs = [ "bsm" ] - deps += [ "//third_party/crashpad/crashpad/handler" ] - } } source_set("test_test") { @@ -89,16 +96,24 @@ source_set("test_test") { sources = [ "hex_string_test.cc", - "mac/mach_multiprocess_test.cc", "main_arguments_test.cc", "multiprocess_exec_test.cc", "scoped_temp_dir_test.cc", "test_paths_test.cc", - "win/win_child_process_test.cc", - "win/win_multiprocess_test.cc", ] - if (!is_win) { + if (is_mac) { + sources += [ "mac/mach_multiprocess_test.cc" ] + } + + if (is_win) { + sources += [ + "win/win_child_process_test.cc", + "win/win_multiprocess_test.cc", + ] + } + + if (is_posix) { sources += [ "multiprocess_posix_test.cc" ] } From d25b0242c6c578161abb72202d7a56daec7c2f9a Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Wed, 29 Nov 2017 18:04:25 -0500 Subject: [PATCH 044/326] gn: Fix a couple of things after 9465fc72ad90 and 2bb56fafe3bd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When I redid the Crashpad GN build a few weeks ago (https://chromium-review.googlesource.com/c/chromium/src/+/751403), I tried to order things according to the GN style guide (https://chromium.googlesource.com/chromium/src/tools/gn/+/HEAD/docs/style_guide.md). As for conditionals, I tried to stick to doing a set of conditionals after “sources” for just “sources”, and then another one at the bottom for everything else. It turns out that this was a good idea because it’s an error to say “deps += [something]” inside a conditional until you’ve already said “deps = [something_else]” first. (Maybe that’s why I did it.) 9465fc72ad90 regressed this. 2bb56fafe3bd also left behind a couple of straggler paths that were absolute to Chromium’s root but should have been made relative. This also fixes a comment (about something that won’t yet work outside of Chromium anyway, but still…) Bug: crashpad:79 Change-Id: I8a6f84bfad368cbcdae4fbff11f1d00e2af14b93 Reviewed-on: https://chromium-review.googlesource.com/798172 Commit-Queue: Mark Mentovai Reviewed-by: Scott Graham --- compat/BUILD.gn | 4 ++-- test/BUILD.gn | 7 +++++-- util/BUILD.gn | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/compat/BUILD.gn b/compat/BUILD.gn index 0903e795..a03653cf 100644 --- a/compat/BUILD.gn +++ b/compat/BUILD.gn @@ -74,9 +74,9 @@ static_library("compat") { deps = [] if (is_mac) { - deps += [ "//third_party/crashpad/crashpad/third_party/apple_cctools" ] + deps += [ "../third_party/apple_cctools" ] } if (is_win) { - deps += [ "//third_party/crashpad/crashpad/third_party/getopt" ] + deps += [ "../third_party/getopt" ] } } diff --git a/test/BUILD.gn b/test/BUILD.gn index 441393b8..3dcf2f22 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -47,8 +47,6 @@ static_library("test") { ] if (is_mac) { - libs = [ "bsm" ] - deps += [ "//third_party/crashpad/crashpad/handler" ] sources += [ "mac/dyld.cc", "mac/dyld.h", @@ -89,6 +87,11 @@ static_library("test") { "//base", "//testing/gtest", ] + + if (is_mac) { + libs = [ "bsm" ] + deps += [ "../handler" ] + } } source_set("test_test") { diff --git a/util/BUILD.gn b/util/BUILD.gn index 7f8e7ecc..c744ddc8 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -297,7 +297,7 @@ static_library("util") { public_configs = [ "..:crashpad_config" ] - # Include files from here and generated files starting with "util". + # Include generated files starting with "util". include_dirs = [ "$root_gen_dir/third_party/crashpad/crashpad" ] public_deps = [ From 7e9dbd53fb74efb31c24aa5be5ebf3a9b312f45f Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 30 Nov 2017 09:59:59 -0800 Subject: [PATCH 045/326] fuchsia: Misc fixes to get more pieces compiling - Some missed set_sources_assignment_filters if'ing - Exclude posix/symbolic_constants_posix.(h|cc) as they don't compile and won't be necessary - Exclude a handful of other posix files that don't make sense on Fuchsia. Bug: crashpad:196 Change-Id: I9ec985f00488267dc104164445c6cc5bca36a1fc Reviewed-on: https://chromium-review.googlesource.com/798220 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- test/BUILD.gn | 18 +++++++++++----- util/BUILD.gn | 60 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/test/BUILD.gn b/test/BUILD.gn index 3dcf2f22..a44779f0 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -33,19 +33,25 @@ static_library("test") { "main_arguments.h", "multiprocess.h", "multiprocess_exec.h", - "multiprocess_exec_posix.cc", - "multiprocess_exec_win.cc", - "multiprocess_posix.cc", "scoped_module_handle.cc", "scoped_module_handle.h", "scoped_temp_dir.cc", "scoped_temp_dir.h", - "scoped_temp_dir_posix.cc", - "scoped_temp_dir_win.cc", "test_paths.cc", "test_paths.h", ] + if (is_posix) { + sources += [ + "multiprocess_posix.cc", + "scoped_temp_dir_posix.cc", + ] + + if (!is_fuchsia) { + sources += [ "multiprocess_exec_posix.cc" ] + } + } + if (is_mac) { sources += [ "mac/dyld.cc", @@ -61,6 +67,8 @@ static_library("test") { if (is_win) { sources += [ + "multiprocess_exec_win.cc", + "scoped_temp_dir_win.cc", "win/child_launcher.cc", "win/child_launcher.h", "win/win_child_process.cc", diff --git a/util/BUILD.gn b/util/BUILD.gn index c744ddc8..8b19b731 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -58,10 +58,8 @@ static_library("util") { "file/delimited_file_reader.cc", "file/delimited_file_reader.h", "file/directory_reader.h", - "file/directory_reader_posix.cc", "file/file_io.cc", "file/file_io.h", - "file/file_io_posix.cc", "file/file_reader.cc", "file/file_reader.h", "file/file_seeker.cc", @@ -69,7 +67,6 @@ static_library("util") { "file/file_writer.cc", "file/file_writer.h", "file/filesystem.h", - "file/filesystem_posix.cc", "file/scoped_remove_file.cc", "file/scoped_remove_file.h", "file/string_file.cc", @@ -79,7 +76,6 @@ static_library("util") { "misc/arraysize_unsafe.h", "misc/as_underlying_type.h", "misc/clock.h", - "misc/clock_posix.cc", "misc/from_pointer_cast.h", "misc/implicit_cast.h", "misc/initialization_state.h", @@ -124,23 +120,6 @@ static_library("util") { "numeric/in_range_cast.h", "numeric/int128.h", "numeric/safe_assignment.h", - "posix/close_multiple.cc", - "posix/close_multiple.h", - "posix/close_stdio.cc", - "posix/close_stdio.h", - "posix/double_fork_and_exec.cc", - "posix/double_fork_and_exec.h", - "posix/drop_privileges.cc", - "posix/drop_privileges.h", - "posix/process_info.h", - "posix/scoped_dir.cc", - "posix/scoped_dir.h", - "posix/scoped_mmap.cc", - "posix/scoped_mmap.h", - "posix/signals.cc", - "posix/signals.h", - "posix/symbolic_constants_posix.cc", - "posix/symbolic_constants_posix.h", "stdlib/aligned_allocator.cc", "stdlib/aligned_allocator.h", "stdlib/map_insert.h", @@ -155,16 +134,51 @@ static_library("util") { "string/split_string.cc", "string/split_string.h", "synchronization/semaphore.h", - "synchronization/semaphore_posix.cc", "thread/thread.cc", "thread/thread.h", "thread/thread_log_messages.cc", "thread/thread_log_messages.h", - "thread/thread_posix.cc", "thread/worker_thread.cc", "thread/worker_thread.h", ] + if (is_posix) { + sources += [ + "file/directory_reader_posix.cc", + "file/file_io_posix.cc", + "file/filesystem_posix.cc", + "misc/clock_posix.cc", + "posix/close_stdio.cc", + "posix/close_stdio.h", + "posix/process_info.h", + "posix/scoped_dir.cc", + "posix/scoped_dir.h", + "posix/scoped_mmap.cc", + "posix/scoped_mmap.h", + "posix/signals.cc", + "posix/signals.h", + "synchronization/semaphore_posix.cc", + "thread/thread_posix.cc", + ] + + if (!is_fuchsia) { + sources += [ + "posix/close_multiple.cc", + "posix/close_multiple.h", + "posix/double_fork_and_exec.cc", + "posix/double_fork_and_exec.h", + "posix/drop_privileges.cc", + "posix/drop_privileges.h", + + # These map signals to and from strings. While Fuchsia defines some of + # the common SIGx defines, signals are never raised on Fuchsia, so + # there's need to include this mapping code. + "posix/symbolic_constants_posix.cc", + "posix/symbolic_constants_posix.h", + ] + } + } + if (is_mac) { sources += [ "mac/checked_mach_address_range.h", From eeb31321f706679740d13fd04d40d5c08c8077f4 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 30 Nov 2017 12:38:27 -0800 Subject: [PATCH 046/326] gn, fuchsia: Add //testing forwarding and GN build file third_party/gtest/BUILD.gn mostly written by rsesek. Also includes DEPS roll of mini_chromium for Wexit_time_destructors config. $ git log --oneline dd0c3e96..fa146c12 fa146c1 (HEAD, origin/master, origin/HEAD) gn: Extract Wexit-time-destructors into separate config 95bfddb fuchsia: Fix base::RandBytes() after 5a1c5f82ce75 b79608a fuchsia: Use llvm-ar from the Fuchsia clang package c34725b fuchsia: Look for the toolchain and SDK in per-build-host directories e8e1ee4 fuchsia: Don't assume that kernel will provide all requested rand bytes 5a1c5f8 fuchsia: Implement RandBytes() df359ca fuchsia: Enable -fPIC bd50c95 Restore accidentally changed license a70db15 Improvements to GN build config 7de4d23 fuchsia: Fix compile of base/logging.cc 25a8b57 Add link GN rules to non-win build, set c++14 in CC flags. 7d15806 fuchsia: The very basics of compiling mini_chromium/base with GN Bug: crashpad:79, crashpad:196 Change-Id: I3e741f185b028a96705eefc1f993037830d97448 Reviewed-on: https://chromium-review.googlesource.com/797414 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai Reviewed-by: Robert Sesek --- DEPS | 2 +- build/BUILDCONFIG.gn | 1 + .../testing/gmock/BUILD.gn | 7 +- .../testing/gtest/BUILD.gn | 7 +- test/BUILD.gn | 14 +- test/gtest_death.h | 52 ++++--- third_party/gtest/BUILD.gn | 132 ++++++++++++++++++ 7 files changed, 181 insertions(+), 34 deletions(-) create mode 100644 third_party/gtest/BUILD.gn diff --git a/DEPS b/DEPS index fa58f6f8..cfc308f9 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'dd0c3e9680ae3c4c22f2221a2a75e48dd4a562ec', + 'fa146c1264953b42baccfc261dc6aa59a1661d26', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn index 840978fa..73b7da56 100644 --- a/build/BUILDCONFIG.gn +++ b/build/BUILDCONFIG.gn @@ -65,6 +65,7 @@ if (is_win) { _default_configs = [ "//third_party/mini_chromium/mini_chromium/build:default", + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", # This (no-op) is added here so that build files that expect to be able to # remove it can do so without causing an error. diff --git a/build/chromium_compatibility/testing/gmock/BUILD.gn b/build/chromium_compatibility/testing/gmock/BUILD.gn index 81001042..06c404c8 100644 --- a/build/chromium_compatibility/testing/gmock/BUILD.gn +++ b/build/chromium_compatibility/testing/gmock/BUILD.gn @@ -12,8 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -# TODO(GN): This is a placeholder that matches the name of Chromium's location -# for this file. It will need to be filled out to cause a gmock dependency. +# This is a forwarding target to match the location that Chromium uses. group("gmock") { + public_configs = [ "//third_party/gtest:gmock_public_config" ] + public_deps = [ + "//third_party/gtest:gmock", + ] } diff --git a/build/chromium_compatibility/testing/gtest/BUILD.gn b/build/chromium_compatibility/testing/gtest/BUILD.gn index 03d30464..236359fd 100644 --- a/build/chromium_compatibility/testing/gtest/BUILD.gn +++ b/build/chromium_compatibility/testing/gtest/BUILD.gn @@ -12,8 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -# TODO(GN): This is a placeholder that matches the name of Chromium's location -# for this file. It will need to be filled out to cause a gtest dependency. +# This is a forwarding target to match the location that Chromium uses. group("gtest") { + public_configs = [ "//third_party/gtest:gtest_public_config" ] + public_deps = [ + "//third_party/gtest:gtest", + ] } diff --git a/test/BUILD.gn b/test/BUILD.gn index a44779f0..899743a9 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -82,7 +82,7 @@ static_library("test") { public_configs = [ "..:crashpad_config" ] - defines = [ "CRASHPAD_IN_CHROMIUM" ] + configs += [ "../build:crashpad_in_chromium" ] data = [ "test_paths_test_data_root.txt", @@ -153,10 +153,8 @@ static_library("gmock_main") { sources = [ "gtest_main.cc", ] - defines = [ - "CRASHPAD_IN_CHROMIUM", - "CRASHPAD_TEST_LAUNCHER_GMOCK", - ] + configs += [ "../build:crashpad_in_chromium" ] + defines = [ "CRASHPAD_TEST_LAUNCHER_GMOCK" ] deps = [ ":test", "//base", @@ -171,10 +169,8 @@ static_library("gtest_main") { sources = [ "gtest_main.cc", ] - defines = [ - "CRASHPAD_IN_CHROMIUM", - "CRASHPAD_TEST_LAUNCHER_GTEST", - ] + configs += [ "../build:crashpad_in_chromium" ] + defines = [ "CRASHPAD_TEST_LAUNCHER_GTEST" ] deps = [ ":test", "//base", diff --git a/test/gtest_death.h b/test/gtest_death.h index 493c9c6b..69f6361e 100644 --- a/test/gtest_death.h +++ b/test/gtest_death.h @@ -27,32 +27,42 @@ #if defined(OS_MACOSX) || DOXYGEN -//! \brief Wraps the gtest `ASSERT_DEATH()` macro to make assertions about death -//! caused by crashes. +//! \brief Wraps the gtest `ASSERT_DEATH_IF_SUPPORTED()` macro to make +//! assertions about death caused by crashes. //! //! On macOS, this macro prevents the system’s crash reporter from handling //! crashes that occur in \a statement. Crashes are normally visible to the //! system’s crash reporter, but it is undesirable for intentional //! ASSERT_DEATH_CRASH() crashes to be handled by any crash reporter. //! +//! `ASSERT_DEATH_IF_SUPPORTED()` is used instead of `ASSERT_DEATH()` to +//! support platforms where death tests are not implemented by gtest (e.g. +//! Fuchsia). On platforms where death tests are not implemented, a warning +//! will be logged and the remainder of the test body skipped. +//! //! \sa ASSERT_DEATH_CHECK() //! \sa EXPECT_DEATH_CRASH() -#define ASSERT_DEATH_CRASH(statement, regex) \ - do { \ - crashpad::test::ExceptionSwallower exception_swallower; \ - ASSERT_DEATH(crashpad::test::ExceptionSwallower::SwallowExceptions(); \ - { statement; }, \ - regex); \ +#define ASSERT_DEATH_CRASH(statement, regex) \ + do { \ + crashpad::test::ExceptionSwallower exception_swallower; \ + ASSERT_DEATH_IF_SUPPORTED( \ + crashpad::test::ExceptionSwallower::SwallowExceptions(); \ + { statement; }, regex); \ } while (false) -//! \brief Wraps the gtest `EXPECT_DEATH()` macro to make assertions about death -//! caused by crashes. +//! \brief Wraps the gtest `EXPECT_DEATH_IF_SUPPORTED()` macro to make +//! assertions about death caused by crashes. //! //! On macOS, this macro prevents the system’s crash reporter from handling //! crashes that occur in \a statement. Crashes are normally visible to the //! system’s crash reporter, but it is undesirable for intentional //! EXPECT_DEATH_CRASH() crashes to be handled by any crash reporter. //! +//! `EXPECT_DEATH_IF_SUPPORTED()` is used instead of `EXPECT_DEATH()` to +//! support platforms where death tests are not implemented by gtest (e.g. +//! Fuchsia). On platforms where death tests are not implemented, a warning +//! will be logged and the remainder of the test body skipped. +//! //! \sa EXPECT_DEATH_CHECK() //! \sa ASSERT_DEATH_CRASH() #define EXPECT_DEATH_CRASH(statement, regex) \ @@ -65,8 +75,10 @@ #else // OS_MACOSX -#define ASSERT_DEATH_CRASH(statement, regex) ASSERT_DEATH(statement, regex) -#define EXPECT_DEATH_CRASH(statement, regex) EXPECT_DEATH(statement, regex) +#define ASSERT_DEATH_CRASH(statement, regex) \ + ASSERT_DEATH_IF_SUPPORTED(statement, regex) +#define EXPECT_DEATH_CRASH(statement, regex) \ + EXPECT_DEATH_IF_SUPPORTED(statement, regex) #endif // OS_MACOSX @@ -84,10 +96,10 @@ //! for any particular output on the standard error stream. In other build //! configurations, the \a regex pattern is left intact. //! -//! `CHECK()` failures normally show up as crashes to the system’s crash -//! reporter, but it is undesirable for intentional ASSERT_DEATH_CHECK() crashes -//! to be handled by any crash reporter, so this is implemented in terms of -//! ASSERT_DEATH_CRASH() instead of `ASSERT_DEATH()`. +//! On macOS, `CHECK()` failures normally show up as crashes to the system’s +//! crash reporter, but it is undesirable for intentional ASSERT_DEATH_CHECK() +//! crashes to be handled by any crash reporter, so this is implemented in +//! terms of ASSERT_DEATH_CRASH() instead of `ASSERT_DEATH()`. //! //! \sa EXPECT_DEATH_CHECK() #define ASSERT_DEATH_CHECK(statement, regex) \ @@ -102,10 +114,10 @@ //! for any particular output on the standard error stream. In other build //! configurations, the \a regex pattern is left intact. //! -//! `CHECK()` failures normally show up as crashes to the system’s crash -//! reporter, but it is undesirable for intentional EXPECT_DEATH_CHECK() crashes -//! to be handled by any crash reporter, so this is implemented in terms of -//! EXPECT_DEATH_CRASH() instead of `EXPECT_DEATH()`. +//! On macOS, `CHECK()` failures normally show up as crashes to the system’s +//! crash reporter, but it is undesirable for intentional EXPECT_DEATH_CHECK() +//! crashes to be handled by any crash reporter, so this is implemented in +//! terms of EXPECT_DEATH_CRASH() instead of `EXPECT_DEATH()`. //! //! \sa ASSERT_DEATH_CHECK() #define EXPECT_DEATH_CHECK(statement, regex) \ diff --git a/third_party/gtest/BUILD.gn b/third_party/gtest/BUILD.gn new file mode 100644 index 00000000..97fe1839 --- /dev/null +++ b/third_party/gtest/BUILD.gn @@ -0,0 +1,132 @@ +# 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. + +config("gtest_private_config") { + visibility = [ ":*" ] + include_dirs = [ "gtest/googletest" ] + defines = [ "GUNIT_NO_GOOGLE3=1" ] +} + +config("gtest_public_config") { + include_dirs = [ "gtest/googletest/include" ] +} + +static_library("gtest") { + sources = [ + "gtest/googletest/include/gtest/gtest-death-test.h", + "gtest/googletest/include/gtest/gtest-message.h", + "gtest/googletest/include/gtest/gtest-param-test.h", + "gtest/googletest/include/gtest/gtest-printers.h", + "gtest/googletest/include/gtest/gtest-spi.h", + "gtest/googletest/include/gtest/gtest-test-part.h", + "gtest/googletest/include/gtest/gtest-typed-test.h", + "gtest/googletest/include/gtest/gtest.h", + "gtest/googletest/include/gtest/gtest_pred_impl.h", + "gtest/googletest/include/gtest/gtest_prod.h", + "gtest/googletest/include/gtest/internal/custom/gtest-port.h", + "gtest/googletest/include/gtest/internal/custom/gtest-printers.h", + "gtest/googletest/include/gtest/internal/custom/gtest.h", + "gtest/googletest/include/gtest/internal/gtest-death-test-internal.h", + "gtest/googletest/include/gtest/internal/gtest-filepath.h", + "gtest/googletest/include/gtest/internal/gtest-internal.h", + "gtest/googletest/include/gtest/internal/gtest-linked_ptr.h", + "gtest/googletest/include/gtest/internal/gtest-param-util-generated.h", + "gtest/googletest/include/gtest/internal/gtest-param-util.h", + "gtest/googletest/include/gtest/internal/gtest-port-arch.h", + "gtest/googletest/include/gtest/internal/gtest-port.h", + "gtest/googletest/include/gtest/internal/gtest-string.h", + "gtest/googletest/include/gtest/internal/gtest-tuple.h", + "gtest/googletest/include/gtest/internal/gtest-type-util.h", + "gtest/googletest/src/gtest-all.cc", + "gtest/googletest/src/gtest-death-test.cc", + "gtest/googletest/src/gtest-filepath.cc", + "gtest/googletest/src/gtest-internal-inl.h", + "gtest/googletest/src/gtest-port.cc", + "gtest/googletest/src/gtest-printers.cc", + "gtest/googletest/src/gtest-test-part.cc", + "gtest/googletest/src/gtest-typed-test.cc", + "gtest/googletest/src/gtest.cc", + ] + sources -= [ "gtest/googletest/src/gtest-all.cc" ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs += [ ":gtest_private_config" ] + public_configs = [ ":gtest_public_config" ] +} + +config("gmock_private_config") { + visibility = [ ":*" ] + include_dirs = [ "gtest/googlemock" ] +} + +config("gmock_public_config") { + include_dirs = [ "gtest/googlemock/include" ] + + # The MOCK_METHODn() macros do not specify “override”, which triggers this + # warning in users: “error: 'Method' overrides a member function but is not + # marked 'override' [-Werror,-Winconsistent-missing-override]”. Suppress + # these warnings, and add -Wno-unknown-warning-option because only recent + # versions of clang (trunk r220703 and later, version + # 3.6 and later) recognize it. + if (is_clang) { + cflags_cc = [ + "-Wno-inconsistent-missing-override", + "-Wno-unknown-warning-option", + ] + } +} + +static_library("gmock") { + sources = [ + "gtest/googlemock/include/gmock/gmock-actions.h", + "gtest/googlemock/include/gmock/gmock-cardinalities.h", + "gtest/googlemock/include/gmock/gmock-generated-actions.h", + "gtest/googlemock/include/gmock/gmock-generated-function-mockers.h", + "gtest/googlemock/include/gmock/gmock-generated-matchers.h", + "gtest/googlemock/include/gmock/gmock-generated-nice-strict.h", + "gtest/googlemock/include/gmock/gmock-matchers.h", + "gtest/googlemock/include/gmock/gmock-more-actions.h", + "gtest/googlemock/include/gmock/gmock-more-matchers.h", + "gtest/googlemock/include/gmock/gmock-spec-builders.h", + "gtest/googlemock/include/gmock/gmock.h", + "gtest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h", + "gtest/googlemock/include/gmock/internal/custom/gmock-matchers.h", + "gtest/googlemock/include/gmock/internal/custom/gmock-port.h", + "gtest/googlemock/include/gmock/internal/gmock-generated-internal-utils.h", + "gtest/googlemock/include/gmock/internal/gmock-internal-utils.h", + "gtest/googlemock/include/gmock/internal/gmock-port.h", + "gtest/googlemock/src/gmock-all.cc", + "gtest/googlemock/src/gmock-cardinalities.cc", + "gtest/googlemock/src/gmock-internal-utils.cc", + "gtest/googlemock/src/gmock-matchers.cc", + "gtest/googlemock/src/gmock-spec-builders.cc", + "gtest/googlemock/src/gmock.cc", + ] + sources -= [ "gtest/googlemock/src/gmock-all.cc" ] + deps = [ + ":gtest", + ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs += [ ":gmock_private_config" ] + public_configs = [ ":gmock_public_config" ] +} + +static_library("gmock_main") { + sources = [ + "gtest/googlemock/src/gmock_main.cc", + ] + deps = [ + ":gmock", + ":gtest", + ] +} From 5969d6b1eb22402f44b30af02e075b5c9c744a59 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Mon, 4 Dec 2017 11:14:25 -0500 Subject: [PATCH 047/326] android: NDK r16 compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This updates build/gyp_crashpad_android.py to define the android_api_level GYP variable whenver unified headers are in use. Previously, it was only set when compiling with GCC and using unified headers. This pairs with https://crrev.com/c/804574 to allow proper detection of when _FILE_OFFSET_BITS=64 would be inappropriate. Since there’s no longer any possibility of using a 64-bit off_t with API < 21, this also drops the compatibility wrapper for mmap() that allowed this configuration to work. Too bad, I liked this, but it’s pointless to carry now. The development documentation is also updated to refer to NDK r16. mini_chromium is updated to 88e056258a01450b07414642fa5fb98493c1f6ce. f609089390cd fuchsia: Add ZX_LOG, et al. to mini_chromium 0a8c5de30c67 fuchsia: Fix RandBytes() ZX_CHECK message string 88e056258a01 android: Don’t use _FILE_OFFSET_BITS=64 until API 21 Change-Id: I932116e0c01bcddd5719f9091a070d504eae600f Reviewed-on: https://chromium-review.googlesource.com/804555 Commit-Queue: Mark Mentovai Reviewed-by: Joshua Peraza --- DEPS | 2 +- build/gyp_crashpad_android.py | 46 ++++++++------- compat/android/sys/mman.cc | 103 ---------------------------------- compat/android/sys/mman.h | 43 -------------- compat/compat.gyp | 2 - doc/developing.md | 8 +-- 6 files changed, 31 insertions(+), 173 deletions(-) delete mode 100644 compat/android/sys/mman.cc delete mode 100644 compat/android/sys/mman.h diff --git a/DEPS b/DEPS index cfc308f9..95622b61 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'fa146c1264953b42baccfc261dc6aa59a1661d26', + '88e056258a01450b07414642fa5fb98493c1f6ce', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/build/gyp_crashpad_android.py b/build/gyp_crashpad_android.py index 4461425a..f78c05b9 100755 --- a/build/gyp_crashpad_android.py +++ b/build/gyp_crashpad_android.py @@ -69,26 +69,32 @@ def main(args): os.environ['CXX_target'] = os.path.join(ndk_bin_dir, '%s-g++' % arch_triplet) - # Unlike the Clang build, when using GCC with “unified headers,” - # __ANDROID_API__ isn’t set automatically and must be pushed in to the - # build. Fish the correct value out of the Clang wrapper script. If unified - # headers are not being used, the Clang wrapper won’t mention - # __ANDROID_API__, but the standalone toolchain’s will - # #define it for both Clang and GCC. - # - # Unified headers are the way of the future, according to - # https://android.googlesource.com/platform/ndk/+/ndk-r14/CHANGELOG.md and - # https://android.googlesource.com/platform/ndk/+/master/docs/UnifiedHeaders.md. - with open(clang_path, 'r') as file: - clang_script_contents = file.read() - matches = re.finditer(r'\s-D__ANDROID_API__=([\d]+)\s', - clang_script_contents) - match = next(matches, None) - if match: - android_api = int(match.group(1)) - extra_args.extend(['-D', 'android_api_level=%d' % android_api]) - if next(matches, None): - raise AssertionError('__ANDROID_API__ defined too many times') + # Unlike the Clang build, when using GCC with unified headers, __ANDROID_API__ + # isn’t set automatically and must be pushed in to the build. Fish the correct + # value out of the Clang wrapper script. If deprecated headers are in use, the + # Clang wrapper won’t mention __ANDROID_API__, but the standalone toolchain’s + # will #define it for both Clang and GCC. + # + # android_api_level is extracted in this manner even when compiling with Clang + # so that it’s available for use in GYP conditions that need to test the API + # level, but beware that it’ll only be available when unified headers are in + # use. + # + # Unified headers are the way of the future, according to + # https://android.googlesource.com/platform/ndk/+/ndk-r14/CHANGELOG.md and + # https://android.googlesource.com/platform/ndk/+/master/docs/UnifiedHeaders.md. + # Traditional (deprecated) headers have been removed entirely as of NDK r16. + # https://android.googlesource.com/platform/ndk/+/ndk-release-r16/CHANGELOG.md. + with open(clang_path, 'r') as file: + clang_script_contents = file.read() + matches = re.finditer(r'\s-D__ANDROID_API__=([\d]+)\s', + clang_script_contents) + match = next(matches, None) + if match: + android_api = int(match.group(1)) + extra_args.extend(['-D', 'android_api_level=%d' % android_api]) + if next(matches, None): + raise AssertionError('__ANDROID_API__ defined too many times') for tool in ('ar', 'nm', 'readelf'): os.environ['%s_target' % tool.upper()] = ( diff --git a/compat/android/sys/mman.cc b/compat/android/sys/mman.cc deleted file mode 100644 index f4d722c9..00000000 --- a/compat/android/sys/mman.cc +++ /dev/null @@ -1,103 +0,0 @@ -// 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 - -#include -#include -#include -#include - -#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21 - -// Bionic has provided a wrapper for __mmap2() since the beginning of time. See -// bionic/libc/SYSCALLS.TXT in any Android version. -extern "C" void* __mmap2(void* addr, - size_t size, - int prot, - int flags, - int fd, - size_t pgoff); - -namespace { - -template -T Align(T value, uint8_t alignment) { - return (value + alignment - 1) & ~(alignment - 1); -} - -// Adapted from Android 8.0.0 bionic/libc/bionic/mmap.cpp. -void* LocalMmap64(void* addr, - size_t size, - int prot, - int flags, - int fd, - off64_t offset) { - constexpr int kMmap2Shift = 12; - - if (offset < 0 || (offset & ((1UL << kMmap2Shift) - 1)) != 0) { - errno = EINVAL; - return MAP_FAILED; - } - - const size_t rounded = Align(size, getpagesize()); - if (rounded < size || rounded > PTRDIFF_MAX) { - errno = ENOMEM; - return MAP_FAILED; - } - - const bool is_private_anonymous = - (flags & (MAP_PRIVATE | MAP_ANONYMOUS)) == (MAP_PRIVATE | MAP_ANONYMOUS); - const bool is_stack_or_grows_down = - (flags & (MAP_STACK | MAP_GROWSDOWN)) != 0; - - void* const result = - __mmap2(addr, size, prot, flags, fd, offset >> kMmap2Shift); - - static bool kernel_has_MADV_MERGEABLE = true; - if (result != MAP_FAILED && kernel_has_MADV_MERGEABLE && - is_private_anonymous && !is_stack_or_grows_down) { - const int saved_errno = errno; - const int rc = madvise(result, size, MADV_MERGEABLE); - if (rc == -1 && errno == EINVAL) { - kernel_has_MADV_MERGEABLE = false; - } - errno = saved_errno; - } - - return result; -} - -} // namespace - -extern "C" { - -void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset) { - // Use the system’s mmap64() wrapper if available. It will be available on - // Android 5.0 (“Lollipop”) and later. - using Mmap64Type = void* (*)(void*, size_t, int, int, int, off64_t); - static const Mmap64Type mmap64 = - reinterpret_cast(dlsym(RTLD_DEFAULT, "mmap64")); - if (mmap64) { - return mmap64(addr, size, prot, flags, fd, offset); - } - - // Otherwise, use the local implementation, which should amount to exactly the - // same thing. - return LocalMmap64(addr, size, prot, flags, fd, offset); -} - -} // extern "C" - -#endif // defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21 diff --git a/compat/android/sys/mman.h b/compat/android/sys/mman.h deleted file mode 100644 index 5e7cd69f..00000000 --- a/compat/android/sys/mman.h +++ /dev/null @@ -1,43 +0,0 @@ -// 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_COMPAT_ANDROID_SYS_MMAN_H_ -#define CRASHPAD_COMPAT_ANDROID_SYS_MMAN_H_ - -#include_next - -#include -#include - -// There’s no mmap() wrapper compatible with a 64-bit off_t for 32-bit code -// until API 21 (Android 5.0/“Lollipop”). A custom mmap() wrapper is provided -// here. Note that this scenario is only possible with NDK unified headers. -// -// https://android.googlesource.com/platform/bionic/+/0bfcbaf4d069e005d6e959d97f8d11c77722b70d/docs/32-bit-abi.md#is-32_bit-1 - -#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21 - -#ifdef __cplusplus -extern "C" { -#endif - -void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21 - -#endif // CRASHPAD_COMPAT_ANDROID_SYS_MMAN_H_ diff --git a/compat/compat.gyp b/compat/compat.gyp index b0aec0af..2e6eb038 100644 --- a/compat/compat.gyp +++ b/compat/compat.gyp @@ -26,8 +26,6 @@ 'android/linux/prctl.h', 'android/linux/ptrace.h', 'android/sched.h', - 'android/sys/mman.cc', - 'android/sys/mman.h', 'android/sys/syscall.h', 'android/sys/user.h', 'linux/signal.h', diff --git a/doc/developing.md b/doc/developing.md index 4dd74782..d034b4d4 100644 --- a/doc/developing.md +++ b/doc/developing.md @@ -119,7 +119,7 @@ Kit)](https://developer.android.com/ndk/) runs on. If it’s not already present on your system, [download the NDK package for your system](https://developer.android.com/ndk/downloads/) and expand it to a suitable location. These instructions assume that it’s been expanded to -`~/android-ndk-r15b`. +`~/android-ndk-r16`. To build Crashpad, portions of the NDK must be reassembled into a [standalone toolchain](https://developer.android.com/ndk/guides/standalone_toolchain.html). @@ -133,8 +133,8 @@ desired. To build a standalone toolchain targeting 64-bit ARM and API level 21 ``` $ cd ~ -$ python android-ndk-r15b/build/tools/make_standalone_toolchain.py \ - --arch=arm64 --api=21 --install-dir=android-ndk-r15b_arm64_api21 +$ python android-ndk-r16/build/tools/make_standalone_toolchain.py \ + --arch=arm64 --api=21 --install-dir=android-ndk-r16_arm64_api21 ``` Note that Chrome uses Android API level 21 for 64-bit platforms and 16 for @@ -152,7 +152,7 @@ operation. ``` $ cd ~/crashpad/crashpad $ python build/gyp_crashpad_android.py \ - --ndk ~/android-ndk-r15b_arm64_api21 \ + --ndk ~/android-ndk-r16_arm64_api21 \ --generator-output out/android_arm64_api21 ``` From 741a84a2987fd57c709d069d4cafdfe68b719332 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 5 Dec 2017 10:52:44 -0800 Subject: [PATCH 048/326] fuchsia: Add runner, get crashpad_test_test building and running - Implement build/run_tests.py to run on Fuchsia device - Implement paths_fuchsia.cc using standard Fuchsia namespace layout - Exclude multiprocess tests, currently unimplemented - Don't use unnecessary O_ flags on Fuchsia in open() call. Bug: crashpad:196, chromium:726124, ZX-797 Change-Id: Ie59dce685b4c3fe54f3e36f357c1101d402ee8b7 Reviewed-on: https://chromium-review.googlesource.com/802180 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- build/run_tests.py | 158 +++++++++++++++++++++++++++++++++++-- test/BUILD.gn | 13 ++- test/test_paths.cc | 8 ++ util/BUILD.gn | 4 + util/file/file_io_posix.cc | 8 +- util/misc/paths_fuchsia.cc | 35 ++++++++ 6 files changed, 214 insertions(+), 12 deletions(-) create mode 100644 util/misc/paths_fuchsia.cc diff --git a/build/run_tests.py b/build/run_tests.py index d577677a..80bccae9 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -18,8 +18,129 @@ from __future__ import print_function import os +import pipes import subprocess import sys +import uuid + +CRASHPAD_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), + os.pardir) +IS_WINDOWS_HOST = sys.platform.startswith('win') + + +def _GetFuchsiaSDKRoot(): + arch = 'mac-amd64' if sys.platform == 'darwin' else 'linux-amd64' + return os.path.join(CRASHPAD_DIR, 'third_party', 'fuchsia', 'sdk', arch) + + +def _BinaryDirLooksLikeFuchsiaBuild(binary_dir): + """Checks whether the provided output directory targets Fuchsia.""" + popen = subprocess.Popen( + ['gn', 'args', binary_dir, '--list=target_os', '--short'], + shell=IS_WINDOWS_HOST, stdout=subprocess.PIPE, stderr=open(os.devnull)) + value = popen.communicate()[0] + return popen.returncode == 0 and 'target_os = "fuchsia"' in value + + +def _GenerateFuchsiaRuntimeDepsFiles(binary_dir, tests): + """Ensures a /.runtime_deps file exists for each test.""" + targets_file = os.path.abspath(os.path.join(binary_dir, 'targets.txt')) + with open(targets_file, 'wb') as f: + f.write('//:' + '\n//:'.join(tests) + '\n') + subprocess.check_call( + ['gn', 'gen', binary_dir, '--runtime-deps-list-file=' + targets_file]) + + +def _HandleOutputFromFuchsiaLogListener(process, done_message): + """Pass through the output from |process| (which should be an instance of + Fuchsia's loglistener) until a special termination |done_message| is + encountered. + + Also attempts to determine if any tests failed by inspecting the log output, + and returns False if there were failures. + """ + success = True + while True: + line = process.stdout.readline().rstrip() + if 'FAILED TEST' in line: + success = False + elif done_message in line and 'echo ' not in line: + break + print(line) + return success + + +def _RuntimeDepsPathToFuchsiaTargetPath(runtime_dep): + """Determines the target location for a given Fuchsia runtime dependency file. + + If the file is in the build directory, then it's stored in /bin, otherwise + in /assets. This is only a rough heuristic, but is sufficient for the current + data set. + """ + norm = os.path.normpath(runtime_dep) + in_build_dir = not norm.startswith('../') + no_prefix = norm.lstrip('/.') + return ('/bin/' if in_build_dir else '/assets/') + no_prefix + + +def _RunOnFuchsiaTarget(binary_dir, test, device_name): + """Runs the given Fuchsia |test| executable on the given |device_name|. The + device must already be booted. + + Copies the executable and its runtime dependencies as specified by GN to the + target in /tmp using `netcp`, runs the binary on the target, and logs output + back to stdout on this machine via `loglistener`. + """ + sdk_root = _GetFuchsiaSDKRoot() + + # Run loglistener and filter the output to know when the test is done. + loglistener_process = subprocess.Popen( + [os.path.join(sdk_root, 'tools', 'loglistener'), device_name], + stdout=subprocess.PIPE, stdin=open(os.devnull), stderr=open(os.devnull)) + + runtime_deps_file = os.path.join(binary_dir, test + '.runtime_deps') + with open(runtime_deps_file, 'rb') as f: + runtime_deps = f.read().splitlines() + + def netruncmd(*args): + """Runs a list of commands on the target device. Each command is escaped + by using pipes.quote(), and then each command is chained by shell ';'. + """ + local_binary = os.path.join(sdk_root, 'tools', 'netruncmd') + final_args = ' ; '.join(' '.join(pipes.quote(x) for x in command) + for command in args) + subprocess.check_call([local_binary, device_name, final_args]) + + try: + unique_id = uuid.uuid4().hex + tmp_root = '/tmp/%s_%s/tmp' % (test, unique_id) + staging_root = '/tmp/%s_%s/pkg' % (test, unique_id) + + # Make a staging directory tree on the target. + directories_to_create = [tmp_root, '%s/bin' % staging_root, + '%s/assets' % staging_root] + netruncmd(['mkdir', '-p'] + directories_to_create) + + # Copy runtime deps into the staging tree. + netcp = os.path.join(sdk_root, 'tools', 'netcp') + for dep in runtime_deps: + target_path = staging_root + _RuntimeDepsPathToFuchsiaTargetPath(dep) + subprocess.check_call([netcp, os.path.join(binary_dir, dep), + device_name + ':' + target_path], + stderr=open(os.devnull)) + + done_message = 'TERMINATED: ' + unique_id + namespace_command = [ + 'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root, '--', + staging_root + '/bin/' + test] + netruncmd(namespace_command, ['echo', done_message]) + + success = _HandleOutputFromFuchsiaLogListener( + loglistener_process, done_message) + if not success: + raise subprocess.CalledProcessError(1, test) + finally: + netruncmd(['rm', '-rf', tmp_root, staging_root]) # This script is primarily used from the waterfall so that the list of tests @@ -30,8 +151,6 @@ def main(args): print('usage: run_tests.py ', file=sys.stderr) return 1 - crashpad_dir = \ - os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir) binary_dir = args[0] # Tell 64-bit Windows tests where to find 32-bit test executables, for @@ -46,20 +165,45 @@ def main(args): if os.path.isdir(binary_dir_32): os.environ['CRASHPAD_TEST_32_BIT_OUTPUT'] = binary_dir_32 + is_fuchsia = _BinaryDirLooksLikeFuchsiaBuild(binary_dir) + tests = [ + 'crashpad_minidump_test', + 'crashpad_test_test', + ] + + if not is_fuchsia: + tests.extend([ + # TODO(scottmg): Move the rest of these to the common section once they + # are building and running successfully. 'crashpad_client_test', 'crashpad_handler_test', - 'crashpad_minidump_test', 'crashpad_snapshot_test', - 'crashpad_test_test', 'crashpad_util_test', - ] + ]) + + if is_fuchsia: + zircon_nodename = os.environ.get('ZIRCON_NODENAME') + if not zircon_nodename: + netls = os.path.join(_GetFuchsiaSDKRoot(), 'tools', 'netls') + popen = subprocess.Popen([netls, '--nowait'], stdout=subprocess.PIPE) + devices = popen.communicate()[0].splitlines() + if popen.returncode != 0 or len(devices) != 1: + print("Please set ZIRCON_NODENAME to your device's hostname", + file=sys.stderr) + return 2 + zircon_nodename = devices[0].strip().split()[1] + print('Using autodetected Fuchsia device:', zircon_nodename) + _GenerateFuchsiaRuntimeDepsFiles(binary_dir, tests) for test in tests: print('-' * 80) print(test) print('-' * 80) - subprocess.check_call(os.path.join(binary_dir, test)) + if is_fuchsia: + _RunOnFuchsiaTarget(binary_dir, test, zircon_nodename) + else: + subprocess.check_call(os.path.join(binary_dir, test)) if sys.platform == 'win32': script = 'snapshot/win/end_to_end_test.py' @@ -67,7 +211,7 @@ def main(args): print(script) print('-' * 80) subprocess.check_call( - [sys.executable, os.path.join(crashpad_dir, script), binary_dir]) + [sys.executable, os.path.join(CRASHPAD_DIR, script), binary_dir]) return 0 diff --git a/test/BUILD.gn b/test/BUILD.gn index 899743a9..e8c290c7 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -108,11 +108,14 @@ source_set("test_test") { sources = [ "hex_string_test.cc", "main_arguments_test.cc", - "multiprocess_exec_test.cc", "scoped_temp_dir_test.cc", "test_paths_test.cc", ] + if (is_posix && !is_fuchsia) { + sources += [ "multiprocess_posix_test.cc" ] + } + if (is_mac) { sources += [ "mac/mach_multiprocess_test.cc" ] } @@ -124,8 +127,12 @@ source_set("test_test") { ] } - if (is_posix) { - sources += [ "multiprocess_posix_test.cc" ] + if (!is_fuchsia) { + sources += [ + # TODO(scottmg): A MultiprocessExecFuchsia is probably desirable, but + # hasn't been implemented yet. + "multiprocess_exec_test.cc", + ] } deps = [ diff --git a/test/test_paths.cc b/test/test_paths.cc index ce180a37..923d22f6 100644 --- a/test/test_paths.cc +++ b/test/test_paths.cc @@ -43,6 +43,13 @@ bool IsTestDataRoot(const base::FilePath& candidate) { } base::FilePath TestDataRootInternal() { +#if defined(OS_FUCHSIA) + base::FilePath asset_path("/pkg/assets"); + if (!IsTestDataRoot(asset_path)) { + LOG(WARNING) << "Test data root seems invalid, continuing anyway"; + } + return asset_path; +#else // defined(OS_FUCHSIA) #if !defined(OS_WIN) const char* environment_value = getenv("CRASHPAD_TEST_DATA_ROOT"); #else // defined(OS_WIN) @@ -88,6 +95,7 @@ base::FilePath TestDataRootInternal() { } return base::FilePath(base::FilePath::kCurrentDirectory); +#endif // defined(OS_FUCHSIA) } #if defined(OS_WIN) && defined(ARCH_CPU_64_BITS) diff --git a/util/BUILD.gn b/util/BUILD.gn index 8b19b731..a8d0694f 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -309,6 +309,10 @@ static_library("util") { } } + if (is_fuchsia) { + sources += [ "misc/paths_fuchsia.cc" ] + } + public_configs = [ "..:crashpad_config" ] # Include generated files starting with "util". diff --git a/util/file/file_io_posix.cc b/util/file/file_io_posix.cc index 60a36996..b2afdc0d 100644 --- a/util/file/file_io_posix.cc +++ b/util/file/file_io_posix.cc @@ -109,8 +109,12 @@ FileOperationResult ReadFile(FileHandle file, void* buffer, size_t size) { } FileHandle OpenFileForRead(const base::FilePath& path) { - return HANDLE_EINTR( - open(path.value().c_str(), O_RDONLY | O_NOCTTY | O_CLOEXEC)); + int flags = O_RDONLY; +#if !defined(OS_FUCHSIA) + // O_NOCTTY is invalid on Fuchsia, and O_CLOEXEC isn't necessary. + flags |= O_NOCTTY | O_CLOEXEC; +#endif + return HANDLE_EINTR(open(path.value().c_str(), flags)); } FileHandle OpenFileForWrite(const base::FilePath& path, diff --git a/util/misc/paths_fuchsia.cc b/util/misc/paths_fuchsia.cc new file mode 100644 index 00000000..8baa0d9f --- /dev/null +++ b/util/misc/paths_fuchsia.cc @@ -0,0 +1,35 @@ +// 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 "util/misc/paths.h" + +#include +#include +#include + +#include "base/logging.h" + +namespace crashpad { + +// static +bool Paths::Executable(base::FilePath* path) { + // Assume the environment has been set up following + // https://fuchsia.googlesource.com/docs/+/master/namespaces.md#typical-directory-structure + // . The actual executable name is not known, but it's conceptually in this + // location. + *path = base::FilePath("/pkg/bin/unknown"); + return true; +} + +} // namespace crashpad From 7a0daa6989a9bad45d970bb500c52879318d905e Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 5 Dec 2017 11:21:14 -0800 Subject: [PATCH 049/326] Enable reading notes from ELF images Bug: crashpad:30 Change-Id: Ie6c594b05c6d39a869ed30b7a7b49e6a6301cc65 Reviewed-on: https://chromium-review.googlesource.com/792539 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- compat/android/elf.h | 20 ++ snapshot/elf/elf_image_reader.cc | 182 ++++++++++++++++++ snapshot/elf/elf_image_reader.h | 102 +++++++++- snapshot/elf/elf_image_reader_test.cc | 82 +++++--- snapshot/elf/elf_image_reader_test_note.S | 30 +++ .../{linux => elf}/test_exported_symbols.sym | 0 snapshot/snapshot_test.gyp | 3 +- 7 files changed, 391 insertions(+), 28 deletions(-) create mode 100644 snapshot/elf/elf_image_reader_test_note.S rename snapshot/{linux => elf}/test_exported_symbols.sym (100%) diff --git a/compat/android/elf.h b/compat/android/elf.h index 79e44fca..9fa92387 100644 --- a/compat/android/elf.h +++ b/compat/android/elf.h @@ -17,6 +17,8 @@ #include_next +#include + #if !defined(ELF32_ST_VISIBILITY) #define ELF32_ST_VISIBILITY(other) ((other) & 0x3) #endif @@ -35,4 +37,22 @@ #define STT_TLS 6 #endif +// ELF note header types are normally provided by . While unified +// headers include in , traditional headers do not, prior +// to API 21. and can't both be included in the same +// translation unit due to collisions, so we provide these types here. +#if __ANDROID_API__ < 21 && !defined(__ANDROID_API_N__) +typedef struct { + Elf32_Word n_namesz; + Elf32_Word n_descsz; + Elf32_Word n_type; +} Elf32_Nhdr; + +typedef struct { + Elf64_Word n_namesz; + Elf64_Word n_descsz; + Elf64_Word n_type; +} Elf64_Nhdr; +#endif // __ANDROID_API__ < 21 && !defined(NT_PRSTATUS) + #endif // CRASHPAD_COMPAT_ANDROID_ELF_H_ diff --git a/snapshot/elf/elf_image_reader.cc b/snapshot/elf/elf_image_reader.cc index ccf33532..a78f7e45 100644 --- a/snapshot/elf/elf_image_reader.cc +++ b/snapshot/elf/elf_image_reader.cc @@ -16,7 +16,9 @@ #include +#include #include +#include #include #include "base/logging.h" @@ -36,6 +38,13 @@ class ElfImageReader::ProgramHeaderTable { virtual bool GetPreferredLoadedMemoryRange(VMAddress* address, VMSize* size) const = 0; + // Locate the next PT_NOTE segment starting at segment index start_index. If a + // PT_NOTE segment is found, start_index is set to the next index after the + // found segment. + virtual bool GetNoteSegment(size_t* start_index, + VMAddress* address, + VMSize* size) const = 0; + protected: ProgramHeaderTable() {} }; @@ -150,6 +159,21 @@ class ElfImageReader::ProgramHeaderTableSpecific return false; } + bool GetNoteSegment(size_t* start_index, + VMAddress* address, + VMSize* size) const override { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + for (size_t index = *start_index; index < table_.size(); ++index) { + if (table_[index].p_type == PT_NOTE) { + *start_index = index + 1; + *address = table_[index].p_vaddr; + *size = table_[index].p_memsz; + return true; + } + } + return false; + } + private: std::vector table_; InitializationStateDcheck initialized_; @@ -157,6 +181,150 @@ class ElfImageReader::ProgramHeaderTableSpecific DISALLOW_COPY_AND_ASSIGN(ProgramHeaderTableSpecific); }; +ElfImageReader::NoteReader::~NoteReader() = default; + +ElfImageReader::NoteReader::Result ElfImageReader::NoteReader::NextNote( + std::string* name, + NoteType* type, + std::string* desc) { + if (!is_valid_) { + LOG(ERROR) << "invalid note reader"; + return Result::kError; + } + + Result result = Result::kError; + do { + while (current_address_ == segment_end_address_) { + VMSize segment_size; + if (!phdr_table_->GetNoteSegment( + &phdr_index_, ¤t_address_, &segment_size)) { + return Result::kNoMoreNotes; + } + current_address_ += elf_reader_->GetLoadBias(); + segment_end_address_ = current_address_ + segment_size; + segment_range_ = std::make_unique(); + if (!segment_range_->Initialize(*range_) || + !segment_range_->RestrictRange(current_address_, segment_size)) { + return Result::kError; + } + } + + retry_ = false; + result = range_->Is64Bit() ? ReadNote(name, type, desc) + : ReadNote(name, type, desc); + } while (retry_); + + if (result == Result::kSuccess) { + return Result::kSuccess; + } + is_valid_ = false; + return Result::kError; +} + +ElfImageReader::NoteReader::NoteReader(const ElfImageReader* elf_reader, + const ProcessMemoryRange* range, + const ProgramHeaderTable* phdr_table, + ssize_t max_note_size, + const std::string& name_filter, + NoteType type_filter, + bool use_filter) + : current_address_(0), + segment_end_address_(0), + elf_reader_(elf_reader), + range_(range), + phdr_table_(phdr_table), + segment_range_(), + phdr_index_(0), + max_note_size_(max_note_size), + name_filter_(name_filter), + type_filter_(type_filter), + use_filter_(use_filter), + is_valid_(true), + retry_(false) {} + +template +ElfImageReader::NoteReader::Result ElfImageReader::NoteReader::ReadNote( + std::string* name, + NoteType* type, + std::string* desc) { + static_assert(sizeof(*type) >= sizeof(NhdrType::n_namesz), + "Note field size mismatch"); + DCHECK_LT(current_address_, segment_end_address_); + + NhdrType note_info; + if (!segment_range_->Read(current_address_, sizeof(note_info), ¬e_info)) { + return Result::kError; + } + current_address_ += sizeof(note_info); + + constexpr size_t align = sizeof(note_info.n_namesz); +#define PAD(x) ((x) + align - 1 & ~(align - 1)) + size_t padded_namesz = PAD(note_info.n_namesz); + size_t padded_descsz = PAD(note_info.n_descsz); + size_t note_size = padded_namesz + padded_descsz; + + // Notes typically have 4-byte alignment. However, .note.android.ident may + // inadvertently use 2-byte alignment. + // https://android-review.googlesource.com/c/platform/bionic/+/554986/ + // We can still find .note.android.ident if it appears first in a note segment + // but there may be 4-byte aligned notes following it. If this note was + // aligned at less than 4-bytes, expect that the next note will be aligned at + // 4-bytes and add extra padding, if necessary. + VMAddress end_of_note = + std::min(PAD(current_address_ + note_size), segment_end_address_); +#undef PAD + + if (max_note_size_ >= 0 && note_size > static_cast(max_note_size_)) { + current_address_ = end_of_note; + retry_ = true; + return Result::kError; + } + + if (use_filter_ && note_info.n_type != type_filter_) { + current_address_ = end_of_note; + retry_ = true; + return Result::kError; + } + + std::string local_name(note_info.n_namesz, '\0'); + if (!segment_range_->Read( + current_address_, note_info.n_namesz, &local_name[0])) { + return Result::kError; + } + if (!local_name.empty()) { + if (local_name.back() != '\0') { + LOG(ERROR) << "unterminated note name"; + return Result::kError; + } + local_name.pop_back(); + } + + if (use_filter_ && local_name != name_filter_) { + current_address_ = end_of_note; + retry_ = true; + return Result::kError; + } + + current_address_ += padded_namesz; + + std::string local_desc(note_info.n_descsz, '\0'); + if (!segment_range_->Read( + current_address_, note_info.n_descsz, &local_desc[0])) { + return Result::kError; + } + + current_address_ = end_of_note; + + if (name) { + name->swap(local_name); + } + if (type) { + *type = note_info.n_type; + } + desc->swap(local_desc); + return Result::kSuccess; +} + ElfImageReader::ElfImageReader() : header_64_(), ehdr_address_(0), @@ -467,4 +635,18 @@ bool ElfImageReader::GetAddressFromDynamicArray(uint64_t tag, return true; } +std::unique_ptr ElfImageReader::Notes( + ssize_t max_note_size) { + return std::make_unique( + this, &memory_, program_headers_.get(), max_note_size); +} + +std::unique_ptr +ElfImageReader::NotesWithNameAndType(const std::string& name, + NoteReader::NoteType type, + ssize_t max_note_size) { + return std::make_unique( + this, &memory_, program_headers_.get(), max_note_size, name, type, true); +} + } // namespace crashpad diff --git a/snapshot/elf/elf_image_reader.h b/snapshot/elf/elf_image_reader.h index 7f05be56..b6b59979 100644 --- a/snapshot/elf/elf_image_reader.h +++ b/snapshot/elf/elf_image_reader.h @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -35,7 +36,77 @@ namespace crashpad { //! //! This class is capable of reading both 32-bit and 64-bit images. class ElfImageReader { + private: + class ProgramHeaderTable; + public: + //! \brief This class enables reading note segments from an ELF image. + //! + //! Objects of this class should be created by calling + //! ElfImageReader::Notes() or ElfImageReader::NotesWithNameAndType(). + class NoteReader { + public: + ~NoteReader(); + + //! \brief The return value for NextNote(). + enum class Result { + //! \brief An error occurred. The NoteReader is invalidated and message is + //! logged. + kError, + + //! \brief A note was found. + kSuccess, + + //! \brief No more notes were found. + kNoMoreNotes, + }; + + //! \brief A type large enough to hold a note type, potentially across + //! bitness. + using NoteType = decltype(Elf64_Nhdr::n_type); + + //! \brief Searches for the next note in the image. + //! + //! \param[out] name The name of the note owner, if not `nullptr`. + //! \param[out] type A type for the note, if not `nullptr`. + //! \param[out] desc The note descriptor. + //! \return a #Result value. \a name, \a type, and \a desc are only valid if + //! this method returns Result::kSuccess. + Result NextNote(std::string* name, NoteType* type, std::string* desc); + + // private + NoteReader(const ElfImageReader* elf_reader_, + const ProcessMemoryRange* range, + const ProgramHeaderTable* phdr_table, + ssize_t max_note_size, + const std::string& name_filter = std::string(), + NoteType type_filter = 0, + bool use_filter = false); + + private: + // Reads the next note at the current segment address. Sets retry_ to true + // and returns kError if use_filter_ is true and the note's name and type do + // not match name_filter_ and type_filter_. + template + Result ReadNote(std::string* name, NoteType* type, std::string* desc); + + VMAddress current_address_; + VMAddress segment_end_address_; + const ElfImageReader* elf_reader_; // weak + const ProcessMemoryRange* range_; // weak + const ProgramHeaderTable* phdr_table_; // weak + std::unique_ptr segment_range_; + size_t phdr_index_; + ssize_t max_note_size_; + std::string name_filter_; + NoteType type_filter_; + bool use_filter_; + bool is_valid_; + bool retry_; + + DISALLOW_COPY_AND_ASSIGN(NoteReader); + }; + ElfImageReader(); ~ElfImageReader(); @@ -101,8 +172,37 @@ class ElfImageReader { //! \return `true` if the debug address was found. bool GetDebugAddress(VMAddress* debug); + //! \brief Return a NoteReader for this image, which scans all PT_NOTE + //! segments in the image. + //! + //! The returned NoteReader is only valid for the lifetime of the + //! ElfImageReader that created it. + //! + //! \param[in] max_note_size The maximum note size to read. Notes whose + //! combined name, descriptor, and padding size are greater than + //! \a max_note_size will be silently skipped. A \a max_note_size of -1 + //! indicates infinite maximum note size. + //! \return A NoteReader object capable of reading notes in this image. + std::unique_ptr Notes(ssize_t max_note_size); + + //! \brief Return a NoteReader for this image, which scans all PT_NOTE + //! segments in the image, filtering by name and type. + //! + //! The returned NoteReader is only valid for the lifetime of the + //! ElfImageReader that created it. + //! + //! \param[in] name The note name to match. + //! \param[in] type The note type to match. + //! \param[in] max_note_size The maximum note size to read. Notes whose + //! combined name, descriptor, and padding size are greater than + //! \a max_note_size will be silently skipped. A \a max_note_size of -1 + //! indicates infinite maximum note size. + //! \return A NoteReader object capable of reading notes in this image. + std::unique_ptr NotesWithNameAndType(const std::string& name, + NoteReader::NoteType type, + ssize_t max_note_size); + private: - class ProgramHeaderTable; template class ProgramHeaderTableSpecific; diff --git a/snapshot/elf/elf_image_reader_test.cc b/snapshot/elf/elf_image_reader_test.cc index 6eb5d410..e9572be9 100644 --- a/snapshot/elf/elf_image_reader_test.cc +++ b/snapshot/elf/elf_image_reader_test.cc @@ -54,27 +54,17 @@ void LocateExecutable(pid_t pid, bool is_64_bit, VMAddress* elf_address) { *elf_address = exe_mapping->range.Base(); } -void ExpectElfImageWithSymbol(pid_t pid, - VMAddress address, - bool is_64_bit, - std::string symbol_name, - VMAddress expected_symbol_address) { - ProcessMemoryLinux memory; - ASSERT_TRUE(memory.Initialize(pid)); - ProcessMemoryRange range; - ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); - - ElfImageReader reader; - ASSERT_TRUE(reader.Initialize(range, address)); - +void ExpectSymbol(ElfImageReader* reader, + const std::string& symbol_name, + VMAddress expected_symbol_address) { VMAddress symbol_address; VMSize symbol_size; ASSERT_TRUE( - reader.GetDynamicSymbol(symbol_name, &symbol_address, &symbol_size)); + reader->GetDynamicSymbol(symbol_name, &symbol_address, &symbol_size)); EXPECT_EQ(symbol_address, expected_symbol_address); EXPECT_FALSE( - reader.GetDynamicSymbol("notasymbol", &symbol_address, &symbol_size)); + reader->GetDynamicSymbol("notasymbol", &symbol_address, &symbol_size)); } void ReadThisExecutableInTarget(pid_t pid) { @@ -87,12 +77,48 @@ void ReadThisExecutableInTarget(pid_t pid) { VMAddress elf_address; LocateExecutable(pid, am_64_bit, &elf_address); - ExpectElfImageWithSymbol( - pid, - elf_address, - am_64_bit, - "ElfImageReaderTestExportedSymbol", - FromPointerCast(ElfImageReaderTestExportedSymbol)); + ProcessMemoryLinux memory; + ASSERT_TRUE(memory.Initialize(pid)); + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, am_64_bit)); + + ElfImageReader reader; + ASSERT_TRUE(reader.Initialize(range, elf_address)); + + ExpectSymbol(&reader, + "ElfImageReaderTestExportedSymbol", + FromPointerCast(ElfImageReaderTestExportedSymbol)); + + ElfImageReader::NoteReader::Result result; + std::string note_name; + std::string note_desc; + ElfImageReader::NoteReader::NoteType note_type; + + std::unique_ptr notes = reader.Notes(-1); + while ((result = notes->NextNote(¬e_name, ¬e_type, ¬e_desc)) == + ElfImageReader::NoteReader::Result::kSuccess) { + } + EXPECT_EQ(result, ElfImageReader::NoteReader::Result::kNoMoreNotes); + + notes = reader.Notes(0); + EXPECT_EQ(notes->NextNote(¬e_name, ¬e_type, ¬e_desc), + ElfImageReader::NoteReader::Result::kNoMoreNotes); + + // Find the note defined in elf_image_reader_test_note.S. + constexpr char kCrashpadNoteName[] = "Crashpad"; + constexpr ElfImageReader::NoteReader::NoteType kCrashpadNoteType = 1; + constexpr uint32_t kCrashpadNoteDesc = 42; + notes = reader.NotesWithNameAndType(kCrashpadNoteName, kCrashpadNoteType, -1); + ASSERT_EQ(notes->NextNote(¬e_name, ¬e_type, ¬e_desc), + ElfImageReader::NoteReader::Result::kSuccess); + EXPECT_EQ(note_name, kCrashpadNoteName); + EXPECT_EQ(note_type, kCrashpadNoteType); + EXPECT_EQ(note_desc.size(), sizeof(kCrashpadNoteDesc)); + EXPECT_EQ(*reinterpret_cast(¬e_desc[0]), + kCrashpadNoteDesc); + + EXPECT_EQ(notes->NextNote(¬e_name, ¬e_type, ¬e_desc), + ElfImageReader::NoteReader::Result::kNoMoreNotes); } // Assumes that libc is loaded at the same address in this process as in the @@ -109,11 +135,15 @@ void ReadLibcInTarget(pid_t pid) { << dlerror(); VMAddress elf_address = FromPointerCast(info.dli_fbase); - ExpectElfImageWithSymbol(pid, - elf_address, - am_64_bit, - "getpid", - FromPointerCast(getpid)); + ProcessMemoryLinux memory; + ASSERT_TRUE(memory.Initialize(pid)); + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, am_64_bit)); + + ElfImageReader reader; + ASSERT_TRUE(reader.Initialize(range, elf_address)); + + ExpectSymbol(&reader, "getpid", FromPointerCast(getpid)); } class ReadExecutableChildTest : public Multiprocess { diff --git a/snapshot/elf/elf_image_reader_test_note.S b/snapshot/elf/elf_image_reader_test_note.S new file mode 100644 index 00000000..e41a5205 --- /dev/null +++ b/snapshot/elf/elf_image_reader_test_note.S @@ -0,0 +1,30 @@ +// 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. + +#define NOTE_ALIGN 4 + .section .note.crashpad.test,"a",%note + .balign NOTE_ALIGN + .type testnote, %object +testnote: + .long name_end - name // namesz + .long desc_end - desc // descsz + .long 1 // type +name: + .ascii "Crashpad\0" +name_end: + .balign NOTE_ALIGN +desc: + .long 42 +desc_end: + .size testnote, .-testnote diff --git a/snapshot/linux/test_exported_symbols.sym b/snapshot/elf/test_exported_symbols.sym similarity index 100% rename from snapshot/linux/test_exported_symbols.sym rename to snapshot/elf/test_exported_symbols.sym diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index 228fd5c9..1e32b8e1 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -73,6 +73,7 @@ 'cpu_context_test.cc', 'crashpad_info_client_options_test.cc', 'elf/elf_image_reader_test.cc', + 'elf/elf_image_reader_test_note.S', 'linux/debug_rendezvous_test.cc', 'linux/exception_snapshot_linux_test.cc', 'linux/process_reader_test.cc', @@ -124,7 +125,7 @@ 'copies': [{ 'destination': '<(PRODUCT_DIR)', 'files': [ - 'linux/test_exported_symbols.sym', + 'elf/test_exported_symbols.sym', ], }], 'ldflags': [ From 2403d066c4a5ce65959a6144e79f809c4d5f9a99 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 5 Dec 2017 11:55:44 -0800 Subject: [PATCH 050/326] fuchsia: Support runtime_deps of directories This is necessary for crashpad_util_test, which has a GN data specification that includes "net/util/testdata/". Bug: crashpad:196 Change-Id: I7e03c8cbe448fd90c2481ad6a7e541827efebb0d Reviewed-on: https://chromium-review.googlesource.com/809328 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- build/run_tests.py | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/build/run_tests.py b/build/run_tests.py index 80bccae9..b998ac2f 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -70,19 +70,6 @@ def _HandleOutputFromFuchsiaLogListener(process, done_message): return success -def _RuntimeDepsPathToFuchsiaTargetPath(runtime_dep): - """Determines the target location for a given Fuchsia runtime dependency file. - - If the file is in the build directory, then it's stored in /bin, otherwise - in /assets. This is only a rough heuristic, but is sufficient for the current - data set. - """ - norm = os.path.normpath(runtime_dep) - in_build_dir = not norm.startswith('../') - no_prefix = norm.lstrip('/.') - return ('/bin/' if in_build_dir else '/assets/') + no_prefix - - def _RunOnFuchsiaTarget(binary_dir, test, device_name): """Runs the given Fuchsia |test| executable on the given |device_name|. The device must already be booted. @@ -106,10 +93,10 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name): """Runs a list of commands on the target device. Each command is escaped by using pipes.quote(), and then each command is chained by shell ';'. """ - local_binary = os.path.join(sdk_root, 'tools', 'netruncmd') + netruncmd_path = os.path.join(sdk_root, 'tools', 'netruncmd') final_args = ' ; '.join(' '.join(pipes.quote(x) for x in command) for command in args) - subprocess.check_call([local_binary, device_name, final_args]) + subprocess.check_call([netruncmd_path, device_name, final_args]) try: unique_id = uuid.uuid4().hex @@ -121,14 +108,31 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name): '%s/assets' % staging_root] netruncmd(['mkdir', '-p'] + directories_to_create) - # Copy runtime deps into the staging tree. - netcp = os.path.join(sdk_root, 'tools', 'netcp') - for dep in runtime_deps: - target_path = staging_root + _RuntimeDepsPathToFuchsiaTargetPath(dep) - subprocess.check_call([netcp, os.path.join(binary_dir, dep), - device_name + ':' + target_path], + def netcp(local_path): + """Uses `netcp` to copy a file or directory to the device. Files located + inside the build dir are stored to /pkg/bin, otherwise to /pkg/assets. + """ + in_binary_dir = local_path.startswith(binary_dir + '/') + if in_binary_dir: + target_path = os.path.join( + staging_root, 'bin', local_path[len(binary_dir)+1:]) + else: + target_path = os.path.join(staging_root, 'assets', local_path) + netcp_path = os.path.join(sdk_root, 'tools', 'netcp') + subprocess.check_call([netcp_path, local_path, + device_name + ':' + target_path], stderr=open(os.devnull)) + # Copy runtime deps into the staging tree. + for dep in runtime_deps: + local_path = os.path.normpath(os.path.join(binary_dir, dep)) + if os.path.isdir(local_path): + for root, dirs, files in os.walk(local_path): + for f in files: + netcp(os.path.join(root, f)) + else: + netcp(local_path) + done_message = 'TERMINATED: ' + unique_id namespace_command = [ 'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root, '--', From bfeb194b017e704069e0488e85ff6f506296684c Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 5 Dec 2017 15:16:08 -0800 Subject: [PATCH 051/326] fuchsia: Get crashpad_util_test building Links, but various tests fail. Also adds support to run_tests.py to run a single binary, likely only useful on Fuchsia. Bug: crashpad:196 Change-Id: Ie82ef26ec214ff4262194e877469953aa8fb367e Reviewed-on: https://chromium-review.googlesource.com/809467 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- build/run_tests.py | 42 +++++++++++++++------------ util/BUILD.gn | 26 +++++++++++++---- util/numeric/checked_address_range.cc | 4 +++ 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/build/run_tests.py b/build/run_tests.py index b998ac2f..a2b6d92f 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -151,11 +151,12 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name): # that are run is maintained in-tree, rather than in a separate infrastructure # location in the recipe. def main(args): - if len(args) != 1: - print('usage: run_tests.py ', file=sys.stderr) + if len(args) != 1 and len(args) != 2: + print('usage: run_tests.py [test_to_run]', file=sys.stderr) return 1 binary_dir = args[0] + single_test = args[1] if len(args) == 2 else None # Tell 64-bit Windows tests where to find 32-bit test executables, for # cross-bitted testing. This relies on the fact that the GYP build by default @@ -174,6 +175,7 @@ def main(args): tests = [ 'crashpad_minidump_test', 'crashpad_test_test', + 'crashpad_util_test', ] if not is_fuchsia: @@ -183,7 +185,6 @@ def main(args): 'crashpad_client_test', 'crashpad_handler_test', 'crashpad_snapshot_test', - 'crashpad_util_test', ]) if is_fuchsia: @@ -198,24 +199,29 @@ def main(args): return 2 zircon_nodename = devices[0].strip().split()[1] print('Using autodetected Fuchsia device:', zircon_nodename) - _GenerateFuchsiaRuntimeDepsFiles(binary_dir, tests) + _GenerateFuchsiaRuntimeDepsFiles( + binary_dir, [t for t in tests if not t.endswith('.py')]) + elif IS_WINDOWS_HOST: + tests.append('snapshot/win/end_to_end_test.py') + + if single_test: + if single_test not in tests: + print('Unrecognized test:', single_test, file=sys.stderr) + return 3 + tests = [single_test] for test in tests: - print('-' * 80) - print(test) - print('-' * 80) - if is_fuchsia: - _RunOnFuchsiaTarget(binary_dir, test, zircon_nodename) + if test.endswith('.py'): + print('-' * 80) + print(test) + print('-' * 80) + subprocess.check_call( + [sys.executable, os.path.join(CRASHPAD_DIR, test), binary_dir]) else: - subprocess.check_call(os.path.join(binary_dir, test)) - - if sys.platform == 'win32': - script = 'snapshot/win/end_to_end_test.py' - print('-' * 80) - print(script) - print('-' * 80) - subprocess.check_call( - [sys.executable, os.path.join(CRASHPAD_DIR, script), binary_dir]) + if is_fuchsia: + _RunOnFuchsiaTarget(binary_dir, test, zircon_nodename) + else: + subprocess.check_call(os.path.join(binary_dir, test)) return 0 diff --git a/util/BUILD.gn b/util/BUILD.gn index a8d0694f..11a6849b 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -150,7 +150,6 @@ static_library("util") { "misc/clock_posix.cc", "posix/close_stdio.cc", "posix/close_stdio.h", - "posix/process_info.h", "posix/scoped_dir.cc", "posix/scoped_dir.h", "posix/scoped_mmap.cc", @@ -169,6 +168,7 @@ static_library("util") { "posix/double_fork_and_exec.h", "posix/drop_privileges.cc", "posix/drop_privileges.h", + "posix/process_info.h", # These map signals to and from strings. While Fuchsia defines some of # the common SIGx defines, signals are never raised on Fuchsia, so @@ -378,16 +378,11 @@ source_set("util_test") { "net/http_body_test_util.cc", "net/http_body_test_util.h", "net/http_multipart_builder_test.cc", - "net/http_transport_test.cc", "net/url_test.cc", "numeric/checked_address_range_test.cc", "numeric/checked_range_test.cc", "numeric/in_range_cast_test.cc", "numeric/int128_test.cc", - "posix/process_info_test.cc", - "posix/scoped_mmap_test.cc", - "posix/signals_test.cc", - "posix/symbolic_constants_posix_test.cc", "stdlib/aligned_allocator_test.cc", "stdlib/map_insert_test.cc", "stdlib/string_number_conversion_test.cc", @@ -401,6 +396,25 @@ source_set("util_test") { "thread/worker_thread_test.cc", ] + if (!is_fuchsia) { + # TODO(scottmg): This requires an implementation of MultiprocessExec for + # testing, and a solution to http_transport_test_server.py -- either a port + # to non-Python, or method of forwarding those requests back to the builder + # host. + sources += [ "net/http_transport_test.cc" ] + } + + if (is_posix) { + if (!is_fuchsia) { + sources += [ + "posix/process_info_test.cc", + "posix/signals_test.cc", + "posix/symbolic_constants_posix_test.cc", + ] + } + sources += [ "posix/scoped_mmap_test.cc" ] + } + if (is_mac) { sources += [ "mac/launchd_test.mm", diff --git a/util/numeric/checked_address_range.cc b/util/numeric/checked_address_range.cc index 17f77618..b2784fc0 100644 --- a/util/numeric/checked_address_range.cc +++ b/util/numeric/checked_address_range.cc @@ -23,6 +23,8 @@ #include "util/win/address_types.h" #elif defined(OS_LINUX) || defined(OS_ANDROID) #include "util/linux/address_types.h" +#elif defined(OS_FUCHSIA) +#include #endif // OS_MACOSX namespace crashpad { @@ -129,6 +131,8 @@ template class CheckedAddressRangeGeneric; template class CheckedAddressRangeGeneric; #elif defined(OS_LINUX) || defined(OS_ANDROID) template class CheckedAddressRangeGeneric; +#elif defined(OS_FUCHSIA) +template class CheckedAddressRangeGeneric; #endif // OS_MACOSX } // namespace internal From 6719610b8c76fe8d109f9002be62865ca4cc98d8 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 5 Dec 2017 15:43:23 -0800 Subject: [PATCH 052/326] fuchsia: Get crashpad_snapshot_test building ProcessSnapshotFuchsia is just a stub, so running fails immediately. Bug: crashpad:196 Change-Id: Ie281cc13c4ff4a6e9699e882dbd6207daaab346d Reviewed-on: https://chromium-review.googlesource.com/809234 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- build/run_tests.py | 2 +- snapshot/BUILD.gn | 7 ++ snapshot/crashpad_info_client_options_test.cc | 10 +- snapshot/crashpad_info_size_test_module.cc | 6 +- snapshot/fuchsia/process_snapshot_fuchsia.cc | 114 ++++++++++++++++++ snapshot/fuchsia/process_snapshot_fuchsia.h | 76 ++++++++++++ snapshot/posix/timezone_test.cc | 1 - 7 files changed, 209 insertions(+), 7 deletions(-) create mode 100644 snapshot/fuchsia/process_snapshot_fuchsia.cc create mode 100644 snapshot/fuchsia/process_snapshot_fuchsia.h diff --git a/build/run_tests.py b/build/run_tests.py index a2b6d92f..5dbf5d2f 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -174,6 +174,7 @@ def main(args): tests = [ 'crashpad_minidump_test', + 'crashpad_snapshot_test', 'crashpad_test_test', 'crashpad_util_test', ] @@ -184,7 +185,6 @@ def main(args): # are building and running successfully. 'crashpad_client_test', 'crashpad_handler_test', - 'crashpad_snapshot_test', ]) if is_fuchsia: diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 2c46d7be..c5d38f16 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -128,6 +128,13 @@ static_library("snapshot") { ] } + if (is_fuchsia) { + sources += [ + "fuchsia/process_snapshot_fuchsia.cc", + "fuchsia/process_snapshot_fuchsia.h", + ] + } + if (target_cpu == "x86" || target_cpu == "x64") { sources += [ "x86/cpuid_reader.cc", diff --git a/snapshot/crashpad_info_client_options_test.cc b/snapshot/crashpad_info_client_options_test.cc index 4d6f185f..1fe38acf 100644 --- a/snapshot/crashpad_info_client_options_test.cc +++ b/snapshot/crashpad_info_client_options_test.cc @@ -31,6 +31,9 @@ #elif defined(OS_WIN) #include #include "snapshot/win/process_snapshot_win.h" +#elif defined(OS_FUCHSIA) +#include +#include "snapshot/fuchsia/process_snapshot_fuchsia.h" #endif namespace crashpad { @@ -81,6 +84,9 @@ CrashpadInfoClientOptions SelfProcessSnapshotAndGetCrashpadOptions() { ProcessSnapshotWin process_snapshot; EXPECT_TRUE(process_snapshot.Initialize( GetCurrentProcess(), ProcessSuspensionState::kRunning, 0, 0)); +#elif defined(OS_FUCHSIA) + ProcessSnapshotFuchsia process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(zx_process_self())); #else #error Port. #endif // OS_MACOSX @@ -146,7 +152,7 @@ TEST(CrashpadInfoClientOptions, TwoModules) { TestPaths::BuildArtifact(FILE_PATH_LITERAL("snapshot"), FILE_PATH_LITERAL("module"), TestPaths::FileType::kLoadableModule); -#if defined(OS_MACOSX) +#if defined(OS_POSIX) ScopedModuleHandle module( dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL)); ASSERT_TRUE(module.valid()) << "dlopen " << module_path.value() << ": " @@ -244,7 +250,7 @@ TEST_P(CrashpadInfoSizes_ClientOptions, DifferentlySizedStruct) { TestPaths::BuildArtifact(FILE_PATH_LITERAL("snapshot"), artifact, TestPaths::FileType::kLoadableModule); -#if defined(OS_MACOSX) +#if defined(OS_POSIX) ScopedModuleHandle module( dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL)); ASSERT_TRUE(module.valid()) diff --git a/snapshot/crashpad_info_size_test_module.cc b/snapshot/crashpad_info_size_test_module.cc index dbb22746..1f5a811e 100644 --- a/snapshot/crashpad_info_size_test_module.cc +++ b/snapshot/crashpad_info_size_test_module.cc @@ -70,11 +70,11 @@ struct TestCrashpadInfo { __attribute__(( #if defined(OS_MACOSX) section(SEG_DATA ",crashpad_info"), -#elif defined(OS_LINUX) || defined(OS_ANDROID) +#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) section("crashpad_info"), -#else // !defined(OS_MACOSX) && !defined(OS_LINUX) && !defined(OS_ANDROID) +#else #error Port -#endif // !defined(OS_MACOSX) && !defined(OS_LINUX) && !defined(OS_ANDROID) +#endif #if defined(ADDRESS_SANITIZER) aligned(64), #endif // defined(ADDRESS_SANITIZER) diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc new file mode 100644 index 00000000..acd5449e --- /dev/null +++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -0,0 +1,114 @@ +// 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/fuchsia/process_snapshot_fuchsia.h" + +#include "base/logging.h" + +namespace crashpad { + +ProcessSnapshotFuchsia::ProcessSnapshotFuchsia() {} + +ProcessSnapshotFuchsia::~ProcessSnapshotFuchsia() {} + +bool ProcessSnapshotFuchsia::Initialize(zx_handle_t process) { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return false; +} + +void ProcessSnapshotFuchsia::GetCrashpadOptions( + CrashpadInfoClientOptions* options) { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 +} + +pid_t ProcessSnapshotFuchsia::ProcessID() const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return 0; +} + +pid_t ProcessSnapshotFuchsia::ParentProcessID() const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return 0; +} + +void ProcessSnapshotFuchsia::SnapshotTime(timeval* snapshot_time) const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 +} + +void ProcessSnapshotFuchsia::ProcessStartTime(timeval* start_time) const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 +} + +void ProcessSnapshotFuchsia::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 +} + +void ProcessSnapshotFuchsia::ReportID(UUID* report_id) const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 +} + +void ProcessSnapshotFuchsia::ClientID(UUID* client_id) const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 +} + +const std::map& +ProcessSnapshotFuchsia::AnnotationsSimpleMap() const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return annotations_simple_map_; +} + +const SystemSnapshot* ProcessSnapshotFuchsia::System() const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return nullptr; +} + +std::vector ProcessSnapshotFuchsia::Threads() const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::vector(); +} + +std::vector ProcessSnapshotFuchsia::Modules() const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::vector(); +} + +std::vector ProcessSnapshotFuchsia::UnloadedModules() + const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::vector(); +} + +const ExceptionSnapshot* ProcessSnapshotFuchsia::Exception() const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return nullptr; +} + +std::vector ProcessSnapshotFuchsia::MemoryMap() + const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::vector(); +} + +std::vector ProcessSnapshotFuchsia::Handles() const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::vector(); +} + +std::vector ProcessSnapshotFuchsia::ExtraMemory() const { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::vector(); +} + +} // namespace crashpad diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.h b/snapshot/fuchsia/process_snapshot_fuchsia.h new file mode 100644 index 00000000..5ae6fd31 --- /dev/null +++ b/snapshot/fuchsia/process_snapshot_fuchsia.h @@ -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_FUCHSIA_PROCESS_SNAPSHOT_FUCHSIA_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_SNAPSHOT_FUCHSIA_H_ + +#include + +#include "base/macros.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" + +namespace crashpad { + +//! \brief A ProcessSnapshot of a running (or crashed) process running on a +//! Fuchsia system. This class is not yet implemented. +class ProcessSnapshotFuchsia : public ProcessSnapshot { + public: + ProcessSnapshotFuchsia(); + ~ProcessSnapshotFuchsia() override; + + //! \brief Initializes the object. + //! + //! \param[in] process The process handle to create a snapshot from. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(zx_handle_t process); + + //! \brief Returns options from CrashpadInfo structures found in modules in + //! the process. + //! + //! \param[out] options Options set in CrashpadInfo structures in modules in + //! the process. + void GetCrashpadOptions(CrashpadInfoClientOptions* options); + + // 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; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector Threads() const override; + std::vector Modules() const override; + std::vector UnloadedModules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector MemoryMap() const override; + std::vector Handles() const override; + std::vector ExtraMemory() const override; + + private: + std::map annotations_simple_map_; + + DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotFuchsia); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_SNAPSHOT_FUCHSIA_H_ diff --git a/snapshot/posix/timezone_test.cc b/snapshot/posix/timezone_test.cc index 4ba14f1c..814506fa 100644 --- a/snapshot/posix/timezone_test.cc +++ b/snapshot/posix/timezone_test.cc @@ -15,7 +15,6 @@ #include "snapshot/posix/timezone.h" #include -#include #include #include From 612237a0328a9d44389e9b05878289afe0dc52bd Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Wed, 6 Dec 2017 12:53:54 -0500 Subject: [PATCH 053/326] android: Fix 32-bit test to build at API [21, 24) with unified headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Although API 21 introduced support for 64-bit off_t in many system calls or their wrappers, support for 64-bit off_t is absent until API 24. This is a partial revert of 5969d6b1eb22, because with this more targeted fix applying only to gtest, the rest of Crashpad will work with a 64-bit off_t even at API levels lacking NDK support by going through the mmap() shim in compat. This includes a mini_chromium update to 96e32dd499a4. 85cbec19ffc0 fuchsia: Make EINTR macros no-ops fbf410cd4d40 fuchsia: Use koid instead of getpid() for process field in logging 96e32dd499a4 Revert "android: Don’t use _FILE_OFFSET_BITS=64 until API 21" Bug: crashpad:211 Change-Id: I34c3c8b42eb315605e6775962b44c3c4573b7462 Reviewed-on: https://chromium-review.googlesource.com/811204 Commit-Queue: Mark Mentovai Reviewed-by: Joshua Peraza --- DEPS | 2 +- compat/android/sys/mman.cc | 103 ++++++++++++++++++++++++++++++++++++ compat/android/sys/mman.h | 43 +++++++++++++++ compat/compat.gyp | 2 + third_party/gtest/gtest.gyp | 15 ++++++ 5 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 compat/android/sys/mman.cc create mode 100644 compat/android/sys/mman.h diff --git a/DEPS b/DEPS index 95622b61..a365ea53 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '88e056258a01450b07414642fa5fb98493c1f6ce', + '96e32dd499a49bf6f9cdabf73e8ead6a89f0f634', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/compat/android/sys/mman.cc b/compat/android/sys/mman.cc new file mode 100644 index 00000000..f4d722c9 --- /dev/null +++ b/compat/android/sys/mman.cc @@ -0,0 +1,103 @@ +// 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 + +#include +#include +#include +#include + +#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21 + +// Bionic has provided a wrapper for __mmap2() since the beginning of time. See +// bionic/libc/SYSCALLS.TXT in any Android version. +extern "C" void* __mmap2(void* addr, + size_t size, + int prot, + int flags, + int fd, + size_t pgoff); + +namespace { + +template +T Align(T value, uint8_t alignment) { + return (value + alignment - 1) & ~(alignment - 1); +} + +// Adapted from Android 8.0.0 bionic/libc/bionic/mmap.cpp. +void* LocalMmap64(void* addr, + size_t size, + int prot, + int flags, + int fd, + off64_t offset) { + constexpr int kMmap2Shift = 12; + + if (offset < 0 || (offset & ((1UL << kMmap2Shift) - 1)) != 0) { + errno = EINVAL; + return MAP_FAILED; + } + + const size_t rounded = Align(size, getpagesize()); + if (rounded < size || rounded > PTRDIFF_MAX) { + errno = ENOMEM; + return MAP_FAILED; + } + + const bool is_private_anonymous = + (flags & (MAP_PRIVATE | MAP_ANONYMOUS)) == (MAP_PRIVATE | MAP_ANONYMOUS); + const bool is_stack_or_grows_down = + (flags & (MAP_STACK | MAP_GROWSDOWN)) != 0; + + void* const result = + __mmap2(addr, size, prot, flags, fd, offset >> kMmap2Shift); + + static bool kernel_has_MADV_MERGEABLE = true; + if (result != MAP_FAILED && kernel_has_MADV_MERGEABLE && + is_private_anonymous && !is_stack_or_grows_down) { + const int saved_errno = errno; + const int rc = madvise(result, size, MADV_MERGEABLE); + if (rc == -1 && errno == EINVAL) { + kernel_has_MADV_MERGEABLE = false; + } + errno = saved_errno; + } + + return result; +} + +} // namespace + +extern "C" { + +void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset) { + // Use the system’s mmap64() wrapper if available. It will be available on + // Android 5.0 (“Lollipop”) and later. + using Mmap64Type = void* (*)(void*, size_t, int, int, int, off64_t); + static const Mmap64Type mmap64 = + reinterpret_cast(dlsym(RTLD_DEFAULT, "mmap64")); + if (mmap64) { + return mmap64(addr, size, prot, flags, fd, offset); + } + + // Otherwise, use the local implementation, which should amount to exactly the + // same thing. + return LocalMmap64(addr, size, prot, flags, fd, offset); +} + +} // extern "C" + +#endif // defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21 diff --git a/compat/android/sys/mman.h b/compat/android/sys/mman.h new file mode 100644 index 00000000..5e7cd69f --- /dev/null +++ b/compat/android/sys/mman.h @@ -0,0 +1,43 @@ +// 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_COMPAT_ANDROID_SYS_MMAN_H_ +#define CRASHPAD_COMPAT_ANDROID_SYS_MMAN_H_ + +#include_next + +#include +#include + +// There’s no mmap() wrapper compatible with a 64-bit off_t for 32-bit code +// until API 21 (Android 5.0/“Lollipop”). A custom mmap() wrapper is provided +// here. Note that this scenario is only possible with NDK unified headers. +// +// https://android.googlesource.com/platform/bionic/+/0bfcbaf4d069e005d6e959d97f8d11c77722b70d/docs/32-bit-abi.md#is-32_bit-1 + +#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21 + +#ifdef __cplusplus +extern "C" { +#endif + +void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21 + +#endif // CRASHPAD_COMPAT_ANDROID_SYS_MMAN_H_ diff --git a/compat/compat.gyp b/compat/compat.gyp index 2e6eb038..b0aec0af 100644 --- a/compat/compat.gyp +++ b/compat/compat.gyp @@ -26,6 +26,8 @@ 'android/linux/prctl.h', 'android/linux/ptrace.h', 'android/sched.h', + 'android/sys/mman.cc', + 'android/sys/mman.h', 'android/sys/syscall.h', 'android/sys/user.h', 'linux/signal.h', diff --git a/third_party/gtest/gtest.gyp b/third_party/gtest/gtest.gyp index ad458936..5079e269 100644 --- a/third_party/gtest/gtest.gyp +++ b/third_party/gtest/gtest.gyp @@ -40,6 +40,21 @@ 'cflags!': [ '-Wexit-time-destructors', ], + + 'conditions': [ + ['OS=="android" and android_api_level!="" and android_api_level<24', { + 'defines!': [ + # Although many system interfaces are available to 32-bit code with + # 64-bit off_t at API 21, the routines in are not until API + # 24. gtest doesn’t make use of these functions directly, but can + # reach them indirectly via the C++ standard library. Disable 64-bit + # off_t prior to API 24 so that these uses can work. Since nothing + # dependent on the size of off_t should escape gtest’s own API, this + # should be safe even in a program that otherwise uses a 64-bit off_t. + '_FILE_OFFSET_BITS=64', + ], + }], + ], }, 'targets': [ From e0f396313154723a8e5d433ec9175fc42961dc37 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Wed, 6 Dec 2017 13:07:51 -0500 Subject: [PATCH 054/326] Sever the connection between the test support library and snapshot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This dependency was created in 107fb7631788 for macOS, but it’s not used on other platforms. Since the test support library is broadly used to test all of Crashpad, it’s useful even during early-stage porting. The snapshot library is a higher-level module that builds upon other components, and is not likely to be functional until the later stages of porting. Expressing this dependency artifically makes it difficult to test ports in development. Change-Id: I9dc2e2c473c8519b4c2b0d774acc9c146ee4e121 Reviewed-on: https://chromium-review.googlesource.com/811564 Reviewed-by: Scott Graham Commit-Queue: Mark Mentovai --- test/BUILD.gn | 6 ++++-- test/test.gyp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/BUILD.gn b/test/BUILD.gn index e8c290c7..ae44fc57 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -90,7 +90,6 @@ static_library("test") { deps = [ "../compat", - "../snapshot", "../util", "//base", "//testing/gtest", @@ -98,7 +97,10 @@ static_library("test") { if (is_mac) { libs = [ "bsm" ] - deps += [ "../handler" ] + deps += [ + "../handler", + "../snapshot", + ] } } diff --git a/test/test.gyp b/test/test.gyp index 79e0671a..ce0c817e 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -22,7 +22,6 @@ 'type': 'static_library', 'dependencies': [ '../compat/compat.gyp:crashpad_compat', - '../snapshot/snapshot.gyp:crashpad_snapshot', '../third_party/gtest/gtest.gyp:gtest', '../third_party/mini_chromium/mini_chromium.gyp:base', '../util/util.gyp:crashpad_util', @@ -85,6 +84,7 @@ ['OS=="mac"', { 'dependencies': [ '../handler/handler.gyp:crashpad_handler_lib', + '../snapshot/snapshot.gyp:crashpad_snapshot', ], 'link_settings': { 'libraries': [ From 15c4fff902ea0943787a0dc8e1db31a5d6486059 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 6 Dec 2017 10:08:11 -0800 Subject: [PATCH 055/326] Get crashpad_client_test and crashpad_handler_test building Stubs a variety of classes (CrashReportExceptionHandler, ExceptionHandlerServer, HTTPTransport, CrashReportDatabase). Bug: crashpad:196 Change-Id: I4772f90d0d2ad07cc2f3c2ef119e92fde5c7acef Reviewed-on: https://chromium-review.googlesource.com/809940 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- build/run_tests.py | 10 +-- client/BUILD.gn | 7 ++ client/crash_report_database_fuchsia.cc | 35 ++++++++++ client/crashpad_client_fuchsia.cc | 38 +++++++++++ handler/BUILD.gn | 10 +++ .../fuchsia/crash_report_exception_handler.cc | 27 ++++++++ .../fuchsia/crash_report_exception_handler.h | 65 +++++++++++++++++++ handler/fuchsia/exception_handler_server.cc | 29 +++++++++ handler/fuchsia/exception_handler_server.h | 44 +++++++++++++ handler/handler_main.cc | 15 +++++ handler/main.cc | 4 +- util/BUILD.gn | 5 +- util/net/http_transport_fuchsia.cc | 26 ++++++++ 13 files changed, 304 insertions(+), 11 deletions(-) create mode 100644 client/crash_report_database_fuchsia.cc create mode 100644 client/crashpad_client_fuchsia.cc create mode 100644 handler/fuchsia/crash_report_exception_handler.cc create mode 100644 handler/fuchsia/crash_report_exception_handler.h create mode 100644 handler/fuchsia/exception_handler_server.cc create mode 100644 handler/fuchsia/exception_handler_server.h create mode 100644 util/net/http_transport_fuchsia.cc diff --git a/build/run_tests.py b/build/run_tests.py index 5dbf5d2f..18cc9f53 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -173,20 +173,14 @@ def main(args): is_fuchsia = _BinaryDirLooksLikeFuchsiaBuild(binary_dir) tests = [ + 'crashpad_client_test', + 'crashpad_handler_test', 'crashpad_minidump_test', 'crashpad_snapshot_test', 'crashpad_test_test', 'crashpad_util_test', ] - if not is_fuchsia: - tests.extend([ - # TODO(scottmg): Move the rest of these to the common section once they - # are building and running successfully. - 'crashpad_client_test', - 'crashpad_handler_test', - ]) - if is_fuchsia: zircon_nodename = os.environ.get('ZIRCON_NODENAME') if not zircon_nodename: diff --git a/client/BUILD.gn b/client/BUILD.gn index 5361dda2..bdff4cf8 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -53,6 +53,13 @@ static_library("client") { ] } + if (is_fuchsia) { + sources += [ + "crash_report_database_fuchsia.cc", + "crashpad_client_fuchsia.cc", + ] + } + public_configs = [ "..:crashpad_config" ] deps = [ diff --git a/client/crash_report_database_fuchsia.cc b/client/crash_report_database_fuchsia.cc new file mode 100644 index 00000000..0a7157c8 --- /dev/null +++ b/client/crash_report_database_fuchsia.cc @@ -0,0 +1,35 @@ +// 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 "client/crash_report_database.h" + +#include "base/logging.h" + +namespace crashpad { + +// static +std::unique_ptr CrashReportDatabase::Initialize( + const base::FilePath& path) { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::unique_ptr(); +} + +// static +std::unique_ptr +CrashReportDatabase::InitializeWithoutCreating(const base::FilePath& path) { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::unique_ptr(); +} + +} // namespace crashpad diff --git a/client/crashpad_client_fuchsia.cc b/client/crashpad_client_fuchsia.cc new file mode 100644 index 00000000..ea179b85 --- /dev/null +++ b/client/crashpad_client_fuchsia.cc @@ -0,0 +1,38 @@ +// 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 "client/crashpad_client.h" + +#include "base/logging.h" + +namespace crashpad { + +CrashpadClient::CrashpadClient() {} + +CrashpadClient::~CrashpadClient() {} + +bool CrashpadClient::StartHandler( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + bool restartable, + bool asynchronous_start) { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return false; +} + +} // namespace crashpad diff --git a/handler/BUILD.gn b/handler/BUILD.gn index 05f554b7..496f256e 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -46,6 +46,15 @@ static_library("handler") { ] } + if (is_fuchsia) { + sources += [ + "fuchsia/crash_report_exception_handler.cc", + "fuchsia/crash_report_exception_handler.h", + "fuchsia/exception_handler_server.cc", + "fuchsia/exception_handler_server.h", + ] + } + public_configs = [ "..:crashpad_config" ] deps = [ @@ -75,6 +84,7 @@ source_set("handler_test") { "../client", "../compat", "../snapshot", + "../snapshot:test_support", "../test", "../util", "//base", diff --git a/handler/fuchsia/crash_report_exception_handler.cc b/handler/fuchsia/crash_report_exception_handler.cc new file mode 100644 index 00000000..a7f61994 --- /dev/null +++ b/handler/fuchsia/crash_report_exception_handler.cc @@ -0,0 +1,27 @@ +// 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 "handler/fuchsia/crash_report_exception_handler.h" + +namespace crashpad { + +CrashReportExceptionHandler::CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map* process_annotations, + const UserStreamDataSources* user_stream_data_sources) {} + +CrashReportExceptionHandler::~CrashReportExceptionHandler() {} + +} // namespace crashpad diff --git a/handler/fuchsia/crash_report_exception_handler.h b/handler/fuchsia/crash_report_exception_handler.h new file mode 100644 index 00000000..c987a5dc --- /dev/null +++ b/handler/fuchsia/crash_report_exception_handler.h @@ -0,0 +1,65 @@ +// 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_HANDLER_FUCHSIA_CRASH_REPORT_EXCEPTION_HANDLER_H_ +#define CRASHPAD_HANDLER_FUCHSIA_CRASH_REPORT_EXCEPTION_HANDLER_H_ + +#include +#include + +#include "base/macros.h" +#include "client/crash_report_database.h" +#include "handler/crash_report_upload_thread.h" +#include "handler/user_stream_data_source.h" + +namespace crashpad { + +//! \brief An exception handler that writes crash reports for exception messages +//! to a CrashReportDatabase. This class is not yet implemented. +class CrashReportExceptionHandler { + public: + //! \brief Creates a new object that will store crash reports in \a database. + //! + //! \param[in] database The database to store crash reports in. Weak. + //! \param[in] upload_thread The upload thread to notify when a new crash + //! report is written into \a database. + //! \param[in] process_annotations A map of annotations to insert as + //! process-level annotations into each crash report that is written. Do + //! not confuse this with module-level annotations, which are under the + //! control of the crashing process, and are used to implement Chrome's + //! "crash keys." Process-level annotations are those that are beyond the + //! control of the crashing process, which must reliably be set even if + //! the process crashes before it’s able to establish its own annotations. + //! To interoperate with Breakpad servers, the recommended practice is to + //! specify values for the `"prod"` and `"ver"` keys as process + //! annotations. + //! \param[in] user_stream_data_sources Data sources to be used to extend + //! crash reports. For each crash report that is written, the data sources + //! are called in turn. These data sources may contribute additional + //! minidump streams. `nullptr` if not required. + CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map* process_annotations, + const UserStreamDataSources* user_stream_data_sources); + + ~CrashReportExceptionHandler(); + + private: + DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler); +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_FUCHSIA_CRASH_REPORT_EXCEPTION_HANDLER_H_ diff --git a/handler/fuchsia/exception_handler_server.cc b/handler/fuchsia/exception_handler_server.cc new file mode 100644 index 00000000..d1dfcdf0 --- /dev/null +++ b/handler/fuchsia/exception_handler_server.cc @@ -0,0 +1,29 @@ +// 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 "handler/fuchsia/exception_handler_server.h" + +#include "base/logging.h" + +namespace crashpad { + +ExceptionHandlerServer::ExceptionHandlerServer() {} + +ExceptionHandlerServer::~ExceptionHandlerServer() {} + +void ExceptionHandlerServer::Run(CrashReportExceptionHandler* handler) { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 +} + +} // namespace crashpad diff --git a/handler/fuchsia/exception_handler_server.h b/handler/fuchsia/exception_handler_server.h new file mode 100644 index 00000000..b998ba9c --- /dev/null +++ b/handler/fuchsia/exception_handler_server.h @@ -0,0 +1,44 @@ +// 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_HANDLER_FUCHSIA_EXCEPTION_HANDLER_SERVER_H_ +#define CRASHPAD_HANDLER_FUCHSIA_EXCEPTION_HANDLER_SERVER_H_ + +#include "base/macros.h" + +namespace crashpad { + +class CrashReportExceptionHandler; + +//! \brief Runs the main exception-handling server in Crashpad's handler +//! process. This class is not yet implemented. +class ExceptionHandlerServer { + public: + //! \brief Constructs an ExceptionHandlerServer object. + ExceptionHandlerServer(); + ~ExceptionHandlerServer(); + + //! \brief Runs the exception-handling server. + //! + //! \param[in] handler The handler to which the exceptions are delegated when + //! they are caught in Run(). Ownership is not transferred. + void Run(CrashReportExceptionHandler* handler); + + private: + DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer); +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_FUCHSIA_EXCEPTION_HANDLER_SERVER_H_ diff --git a/handler/handler_main.cc b/handler/handler_main.cc index 2e46f86a..f175fddf 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -74,6 +74,9 @@ #include "util/win/handle.h" #include "util/win/initial_client_data.h" #include "util/win/session_end_watcher.h" +#elif defined(OS_FUCHSIA) +#include "handler/fuchsia/crash_report_exception_handler.h" +#include "handler/fuchsia/exception_handler_server.h" #endif // OS_MACOSX namespace crashpad { @@ -345,6 +348,16 @@ void InstallCrashHandler() { ALLOW_UNUSED_LOCAL(terminate_handler); } +#elif defined(OS_FUCHSIA) + +void InstallCrashHandler() { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 +} + +void ReinstallCrashHandler() { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 +} + #endif // OS_MACOSX void MonitorSelf(const Options& options) { @@ -727,6 +740,8 @@ int HandlerMain(int argc, if (!options.pipe_name.empty()) { exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name)); } +#elif defined(OS_FUCHSIA) + ExceptionHandlerServer exception_handler_server; #endif // OS_MACOSX base::GlobalHistogramAllocator* histogram_allocator = nullptr; diff --git a/handler/main.cc b/handler/main.cc index c0a04f40..3ae73ecd 100644 --- a/handler/main.cc +++ b/handler/main.cc @@ -21,7 +21,7 @@ #include #endif -#if defined(OS_MACOSX) +#if defined(OS_POSIX) int main(int argc, char* argv[]) { return crashpad::HandlerMain(argc, argv, nullptr); @@ -50,4 +50,4 @@ int wmain(int argc, wchar_t* argv[]) { return crashpad::ToolSupport::Wmain(argc, argv, HandlerMainAdaptor); } -#endif // OS_MACOSX +#endif // OS_POSIX diff --git a/util/BUILD.gn b/util/BUILD.gn index 11a6849b..9adc5d63 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -310,7 +310,10 @@ static_library("util") { } if (is_fuchsia) { - sources += [ "misc/paths_fuchsia.cc" ] + sources += [ + "misc/paths_fuchsia.cc", + "net/http_transport_fuchsia.cc", + ] } public_configs = [ "..:crashpad_config" ] diff --git a/util/net/http_transport_fuchsia.cc b/util/net/http_transport_fuchsia.cc new file mode 100644 index 00000000..1c08c1f1 --- /dev/null +++ b/util/net/http_transport_fuchsia.cc @@ -0,0 +1,26 @@ +// 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 "util/net/http_transport.h" + +#include "base/logging.h" + +namespace crashpad { + +std::unique_ptr HTTPTransport::Create() { + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::unique_ptr(); +} + +} // namespace crashpad From 82a80acd3155654c2851ae97658e79d97f5761df Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 6 Dec 2017 15:40:51 -0800 Subject: [PATCH 056/326] Roll mini_chromium 96e32dd4..a12ed4a6 $ git log --oneline 96e32dd4..a12ed4a6 a12ed4a gn, mac: Add sysroot.gni for Mac that finds default SDK 5e8e232 gn, win: Add copy and solink_module tools to win toolchain 009b44a gn: Explicitly filter posix files from mini_chromium/base TBR=mark@chromium.org Bug: crashpad:79 Change-Id: Ic86a2e376d31cbf614e0f44907156f83cbeabd20 Reviewed-on: https://chromium-review.googlesource.com/812544 Reviewed-by: Scott Graham Commit-Queue: Scott Graham --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index a365ea53..79fb6434 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '96e32dd499a49bf6f9cdabf73e8ead6a89f0f634', + 'a12ed4a613a7df900059c8a7885acdad550a5319', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', From a4fc880278e7906acc0573228dc9e95f811c609a Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 6 Dec 2017 15:36:31 -0800 Subject: [PATCH 057/326] gn, mac: Get basic generation working A couple targets compile, but not linking yet. Requires https://chromium-review.googlesource.com/c/chromium/mini_chromium/+/812410 Bug: crashpad:79 Change-Id: Ifc472628ec5233fc88c746e74a1f7b3ce0637f91 Reviewed-on: https://chromium-review.googlesource.com/811884 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- build/BUILDCONFIG.gn | 7 +++++- .../build/config/sysroot.gni | 22 +++++++++++++++++++ util/BUILD.gn | 9 +++----- 3 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 build/chromium_compatibility/build/config/sysroot.gni diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn index 73b7da56..c931c2a6 100644 --- a/build/BUILDCONFIG.gn +++ b/build/BUILDCONFIG.gn @@ -39,7 +39,6 @@ is_win = false is_linux = false is_android = false is_fuchsia = false -is_ios = false # This is necessary for third_party/zlib/zlib/BUILD.gn. if (current_os == "mac") { is_mac = true @@ -91,3 +90,9 @@ set_defaults("loadable_module") { set_defaults("shared_library") { configs = _default_configs } + +# These are set to constant values for Chromium build file compatibility. This +# generally avoids extra explicit checking of whether or not Crashpad is +# building in Chromium mode or not. +is_ios = false +is_component_build = false diff --git a/build/chromium_compatibility/build/config/sysroot.gni b/build/chromium_compatibility/build/config/sysroot.gni new file mode 100644 index 00000000..49390dde --- /dev/null +++ b/build/chromium_compatibility/build/config/sysroot.gni @@ -0,0 +1,22 @@ +# 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. + +# In Crashpad, sysroot.gni is in mini_chromium, forward to that one. +import("//third_party/mini_chromium/mini_chromium/build/sysroot.gni") + +# To avoid Chromium Mac hermetic toolchain paths without explicitly checking +# whether building in Chromium mode. +if (is_mac) { + use_system_xcode = true +} diff --git a/util/BUILD.gn b/util/BUILD.gn index 9adc5d63..38a20128 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -16,9 +16,7 @@ import("//testing/test.gni") if (is_mac) { import("//build/config/sysroot.gni") -} -if (is_mac) { action_foreach("mig") { script = "mach/mig.py" @@ -46,7 +44,7 @@ if (is_mac) { } args += [ "--sdk", - mac_sdk_path, + sysroot, "--include", rebase_path("../compat/mac", root_build_dir), ] @@ -326,10 +324,8 @@ static_library("util") { ] deps = [ - "//base", - - # TODO(GN): Should this point at Chromium's zlib when in Chromium? "../third_party/zlib", + "//base", ] if (is_mac) { @@ -340,6 +336,7 @@ static_library("util") { "IOKit.framework", ] deps += [ ":mig" ] + include_dirs += [ "$root_build_dir/gen" ] } if (is_win) { From 914b0d6755282a42ad1cb114e62ab2ce1e321fd4 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Wed, 6 Dec 2017 20:32:30 -0800 Subject: [PATCH 058/326] Fix enum vs unsigned -Wsign-compare warnings Recent Clang versions started taking into account that enums are signed on Windows when emitting these warnings. Bug: chromium:792519 Change-Id: I08767fa1f5c8211e663769c7e76b13a1b7146f4f Reviewed-on: https://chromium-review.googlesource.com/813497 Reviewed-by: Mark Mentovai Commit-Queue: Mark Mentovai --- minidump/minidump_file_writer.cc | 2 +- minidump/test/minidump_file_writer_test_util.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/minidump/minidump_file_writer.cc b/minidump/minidump_file_writer.cc index b4f0a624..72265453 100644 --- a/minidump/minidump_file_writer.cc +++ b/minidump/minidump_file_writer.cc @@ -59,7 +59,7 @@ void MinidumpFileWriter::InitializeFromSnapshot( DCHECK_EQ(state(), kStateMutable); DCHECK_EQ(header_.Signature, 0u); DCHECK_EQ(header_.TimeDateStamp, 0u); - DCHECK_EQ(header_.Flags, MiniDumpNormal); + DCHECK_EQ(static_cast(header_.Flags), MiniDumpNormal); DCHECK(streams_.empty()); // This time is truncated to an integer number of seconds, not rounded, for diff --git a/minidump/test/minidump_file_writer_test_util.cc b/minidump/test/minidump_file_writer_test_util.cc index f372212b..aae357dc 100644 --- a/minidump/test/minidump_file_writer_test_util.cc +++ b/minidump/test/minidump_file_writer_test_util.cc @@ -52,7 +52,7 @@ void VerifyMinidumpHeader(const MINIDUMP_HEADER* header, ASSERT_EQ(header->StreamDirectoryRva, streams ? sizeof(MINIDUMP_HEADER) : 0u); EXPECT_EQ(header->CheckSum, 0u); EXPECT_EQ(header->TimeDateStamp, timestamp); - EXPECT_EQ(header->Flags, MiniDumpNormal); + EXPECT_EQ(static_cast(header->Flags), MiniDumpNormal); } } // namespace test From 659420bc7d7ea7ba16ca2fa2be55ab2f5872e906 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Thu, 7 Dec 2017 16:57:46 -0500 Subject: [PATCH 059/326] android: Run tests by running run_tests.py on the build host Bug: crashpad:30 Change-Id: Ie432c58c4a2505b6434861276512a5011fd285d4 Reviewed-on: https://chromium-review.googlesource.com/811891 Commit-Queue: Mark Mentovai Reviewed-by: Scott Graham --- build/run_tests.py | 147 ++++++++++++++++++++++++++++++++++++++++++--- doc/developing.md | 39 ++++-------- 2 files changed, 148 insertions(+), 38 deletions(-) diff --git a/build/run_tests.py b/build/run_tests.py index 18cc9f53..67c3a03f 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -19,6 +19,8 @@ from __future__ import print_function import os import pipes +import posixpath +import re import subprocess import sys import uuid @@ -28,18 +30,120 @@ CRASHPAD_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), IS_WINDOWS_HOST = sys.platform.startswith('win') -def _GetFuchsiaSDKRoot(): - arch = 'mac-amd64' if sys.platform == 'darwin' else 'linux-amd64' - return os.path.join(CRASHPAD_DIR, 'third_party', 'fuchsia', 'sdk', arch) +def _BinaryDirTargetOS(binary_dir): + """Returns the apparent target OS of binary_dir, or None if none appear to be + explicitly specified.""" - -def _BinaryDirLooksLikeFuchsiaBuild(binary_dir): - """Checks whether the provided output directory targets Fuchsia.""" + # Look for a GN “target_os”. popen = subprocess.Popen( ['gn', 'args', binary_dir, '--list=target_os', '--short'], shell=IS_WINDOWS_HOST, stdout=subprocess.PIPE, stderr=open(os.devnull)) value = popen.communicate()[0] - return popen.returncode == 0 and 'target_os = "fuchsia"' in value + if popen.returncode == 0: + match = re.match('target_os = "(.*)"$', value.decode('utf-8')) + if match: + return match.group(1) + + # For GYP with Ninja, look for the appearance of “linux-android” in the path + # to ar. This path is configured by gyp_crashpad_android.py. + try: + with open(os.path.join(binary_dir, 'build.ninja')) as build_ninja_file: + build_ninja_content = build_ninja_file.read() + match = re.search('^ar = .+-linux-android(eabi)?-ar$', + build_ninja_content, + re.MULTILINE) + if match: + return 'android' + except FileNotFoundError: + # Ninja may not be in use. Assume the best. + pass + + return None + + +def _RunOnAndroidTarget(binary_dir, test, android_device): + local_test_path = os.path.join(binary_dir, test) + MAYBE_UNSUPPORTED_TESTS = ( + 'crashpad_client_test', + 'crashpad_handler_test', + 'crashpad_minidump_test', + 'crashpad_snapshot_test', + ) + if not os.path.exists(local_test_path) and test in MAYBE_UNSUPPORTED_TESTS: + print(test, 'is not present and may not be supported, skipping') + return + + device_temp_dir = subprocess.check_output( + ['adb', '-s', android_device, 'shell', + 'mktemp', '-d', '/data/local/tmp/%s.XXXXXXXX' % test], + shell=IS_WINDOWS_HOST).decode('utf-8').rstrip() + try: + # Specify test dependencies that must be pushed to the device. This could be + # determined automatically in a GN build, following the example used for + # Fuchsia. Since nothing like that exists for GYP, hard-code it for + # supported tests. + test_build_artifacts = [test] + test_data = ['test/test_paths_test_data_root.txt'] + + if test == 'crashpad_test_test': + test_build_artifacts.append( + 'crashpad_test_test_multiprocess_exec_test_child') + elif test == 'crashpad_util_test': + test_data.append('util/net/testdata/') + + def _adb(*args): + # Flush all of this script’s own buffered stdout output before running + # adb, which will likely produce its own output on stdout. + sys.stdout.flush() + + adb_command = ['adb', '-s', android_device] + adb_command.extend(args) + subprocess.check_call(adb_command, shell=IS_WINDOWS_HOST) + + # Establish the directory structure on the device. + device_out_dir = posixpath.join(device_temp_dir, 'out') + device_mkdirs = [device_out_dir] + for source_path in test_data: + # A trailing slash could reasonably mean to copy an entire directory, but + # will interfere with what’s needed from the path split. All parent + # directories of any source_path need to be be represented in + # device_mkdirs, but it’s important that no source_path itself wind up in + # device_mkdirs, even if source_path names a directory, because that would + # cause the “adb push” of the directory below to behave incorrectly. + if source_path.endswith(posixpath.sep): + source_path = source_path[:-1] + + device_source_path = posixpath.join(device_temp_dir, source_path) + device_mkdir = posixpath.split(device_source_path)[0] + if device_mkdir not in device_mkdirs: + device_mkdirs.append(device_mkdir) + adb_mkdir_command = ['shell', 'mkdir', '-p'] + adb_mkdir_command.extend(device_mkdirs) + _adb(*adb_mkdir_command) + + # Push the test binary and any other build output to the device. + adb_push_command = ['push'] + for artifact in test_build_artifacts: + adb_push_command.append(os.path.join(binary_dir, artifact)) + adb_push_command.append(device_out_dir) + _adb(*adb_push_command) + + # Push test data to the device. + for source_path in test_data: + _adb('push', + os.path.join(CRASHPAD_DIR, source_path), + posixpath.join(device_temp_dir, source_path)) + + # Run the test on the device. + _adb('shell', 'env', 'CRASHPAD_TEST_DATA_ROOT=' + device_temp_dir, + posixpath.join(device_out_dir, test)) + finally: + _adb('shell', 'rm', '-rf', device_temp_dir) + + +def _GetFuchsiaSDKRoot(): + arch = 'mac-amd64' if sys.platform == 'darwin' else 'linux-amd64' + return os.path.join(CRASHPAD_DIR, 'third_party', 'fuchsia', 'sdk', arch) def _GenerateFuchsiaRuntimeDepsFiles(binary_dir, tests): @@ -170,7 +274,9 @@ def main(args): if os.path.isdir(binary_dir_32): os.environ['CRASHPAD_TEST_32_BIT_OUTPUT'] = binary_dir_32 - is_fuchsia = _BinaryDirLooksLikeFuchsiaBuild(binary_dir) + target_os = _BinaryDirTargetOS(binary_dir) + is_android = target_os == 'android' + is_fuchsia = target_os == 'fuchsia' tests = [ 'crashpad_client_test', @@ -181,7 +287,26 @@ def main(args): 'crashpad_util_test', ] - if is_fuchsia: + if is_android: + android_device = os.environ.get('ANDROID_DEVICE') + if not android_device: + adb_devices = subprocess.check_output(['adb', 'devices'], + shell=IS_WINDOWS_HOST) + devices = [] + for line in adb_devices.splitlines(): + line = line.decode('utf-8') + if (line == 'List of devices attached' or + re.match('^\* daemon .+ \*$', line) or + line == ''): + continue + (device, ignore) = line.split('\t') + devices.append(device) + if len(devices) != 1: + print("Please set ANDROID_DEVICE to your device's id", file=sys.stderr) + return 2 + android_device = devices[0] + print('Using autodetected Android device:', android_device) + elif is_fuchsia: zircon_nodename = os.environ.get('ZIRCON_NODENAME') if not zircon_nodename: netls = os.path.join(_GetFuchsiaSDKRoot(), 'tools', 'netls') @@ -212,7 +337,9 @@ def main(args): subprocess.check_call( [sys.executable, os.path.join(CRASHPAD_DIR, test), binary_dir]) else: - if is_fuchsia: + if is_android: + _RunOnAndroidTarget(binary_dir, test, android_device) + elif is_fuchsia: _RunOnFuchsiaTarget(binary_dir, test, zircon_nodename) else: subprocess.check_call(os.path.join(binary_dir, test)) diff --git a/doc/developing.md b/doc/developing.md index d034b4d4..c8bb430d 100644 --- a/doc/developing.md +++ b/doc/developing.md @@ -210,35 +210,18 @@ Software Development Kit. ### Android -To test on Android, use [ADB (Android Debug -Bridge)](https://developer.android.com/studio/command-line/adb.html) to `adb -push` test executables and test data to a device or emulator, then use `adb -shell` to get a shell to run the test executables from. ADB is part of the -[Android SDK](https://developer.android.com/sdk/). Note that it is sufficient to -install just the command-line tools. The entire Android Studio IDE is not -necessary to obtain ADB. +To test on Android, [ADB (Android Debug +Bridge)](https://developer.android.com/studio/command-line/adb.html) from the +[Android SDK](https://developer.android.com/sdk/) must be in the `PATH`. Note +that it is sufficient to install just the command-line tools from the Android +SDK. The entire Android Studio IDE is not necessary to obtain ADB. -This example runs `crashpad_test_test` on a device. This test executable has a -run-time dependency on a second executable and a test data file, which are also -transferred to the device prior to running the test. - -``` -$ cd ~/crashpad/crashpad -$ adb push out/android_arm64_api21/out/Debug/crashpad_test_test /data/local/tmp/ -[100%] /data/local/tmp/crashpad_test_test -$ adb push \ - out/android_arm64_api21/out/Debug/crashpad_test_test_multiprocess_exec_test_child \ - /data/local/tmp/ -[100%] /data/local/tmp/crashpad_test_test_multiprocess_exec_test_child -$ adb shell mkdir -p /data/local/tmp/crashpad_test_data_root/test -$ adb push test/test_paths_test_data_root.txt \ - /data/local/tmp/crashpad_test_data_root/test/ -[100%] /data/local/tmp/crashpad_test_data_root/test/test_paths_test_data_root.txt -$ adb shell -device:/ $ cd /data/local/tmp -device:/data/local/tmp $ CRASHPAD_TEST_DATA_ROOT=crashpad_test_data_root \ - ./crashpad_test_test -``` +When asked to test an Android build directory, `run_tests.py` will detect a +single connected Android device (including an emulator). If multiple devices are +connected, one may be chosen explicitly with the `ANDROID_DEVICE` environment +variable. `run_tests.py` will upload test executables and data to a temporary +location on the detected or selected device, run them, and clean up after itself +when done. ## Contributing From 00e6bd088725bbf526db7a349003bf72ca717bec Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 8 Dec 2017 11:40:29 -0800 Subject: [PATCH 060/326] fuchsia: Get 'all' to build Adds a zlib build file for when building standalone (rather than reusing Chromium's, though the code still Chromium's patched copy). The separate build file avoids including the code for minizip and other support targets (instead, only the main libzlib.a static_library is defined). The other libraries and executables won't build in the Crashpad repo, so having a local build file means that all targets defined in the GN build are buildable. generate_dump is passing an invalid handle to ProcessSnapshotFuchsia as there's not yet any utility to convert a pid to a handle. But that's no great loss, because ProcessSnapshotFuchsia doesn't do anything itself yet. Bug: crashpad:79, crashpad:196 Change-Id: I11c918a30b60cc071465c919315b45caab1de870 Reviewed-on: https://chromium-review.googlesource.com/809354 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- .../crashpad_handler_test_extended_handler.cc | 4 +- third_party/zlib/BUILD.gn | 82 +++++++++++++++++-- tools/generate_dump.cc | 13 ++- 3 files changed, 89 insertions(+), 10 deletions(-) diff --git a/handler/crashpad_handler_test_extended_handler.cc b/handler/crashpad_handler_test_extended_handler.cc index e1c18e83..7a8f4563 100644 --- a/handler/crashpad_handler_test_extended_handler.cc +++ b/handler/crashpad_handler_test_extended_handler.cc @@ -56,7 +56,7 @@ int ExtendedHandlerMain(int argc, char* argv[]) { } // namespace -#if defined(OS_MACOSX) +#if defined(OS_POSIX) int main(int argc, char* argv[]) { return ExtendedHandlerMain(argc, argv); @@ -68,4 +68,4 @@ int wmain(int argc, wchar_t* argv[]) { return crashpad::ToolSupport::Wmain(argc, argv, &ExtendedHandlerMain); } -#endif // OS_MACOSX +#endif // OS_POSIX diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn index 017dfff8..cfea9f3e 100644 --- a/third_party/zlib/BUILD.gn +++ b/third_party/zlib/BUILD.gn @@ -19,19 +19,87 @@ config("zlib_config") { defines = [ "CRASHPAD_ZLIB_SOURCE_CHROMIUM" ] } else { defines = [ "CRASHPAD_ZLIB_SOURCE_EMBEDDED" ] + include_dirs = [ "zlib" ] } } -group("zlib") { - public_configs = [ ":zlib_config" ] - - if (crashpad_in_chromium) { +if (crashpad_in_chromium) { + group("zlib") { + public_configs = [ ":zlib_config" ] public_deps = [ "//third_party/zlib", ] - } else { - public_deps = [ - "//third_party/zlib/zlib", + } +} else { + static_library("zlib") { + sources = [ + "zlib/adler32.c", + "zlib/compress.c", + "zlib/crc32.c", + "zlib/crc32.h", + "zlib/deflate.c", + "zlib/deflate.h", + "zlib/gzclose.c", + "zlib/gzguts.h", + "zlib/gzlib.c", + "zlib/gzread.c", + "zlib/gzwrite.c", + "zlib/infback.c", + "zlib/inffast.c", + "zlib/inffast.h", + "zlib/inffixed.h", + "zlib/inflate.c", + "zlib/inflate.h", + "zlib/inftrees.c", + "zlib/inftrees.h", + "zlib/names.h", + "zlib/trees.c", + "zlib/trees.h", + "zlib/uncompr.c", + "zlib/zconf.h", + "zlib/zlib.h", + "zlib/zlib_crashpad.h", + "zlib/zutil.c", + "zlib/zutil.h", ] + + cflags = [] + defines = [ "HAVE_STDARG_H" ] + public_configs = [ ":zlib_config" ] + + if (is_win) { + cflags += [ + "/wd4131", # uses old-style declarator + "/wd4244", # conversion from 't1' to 't2', possible loss of data + "/wd4245", # conversion from 't1' to 't2', signed/unsigned mismatch + "/wd4267", # conversion from 'size_t' to 't', possible loss of data + "/wd4324", # structure was padded due to alignment specifier + ] + } else { + defines += [ + "HAVE_HIDDEN", + "HAVE_UNISTD_H", + ] + } + + if (current_cpu == "x86" || current_cpu == "x64") { + sources += [ + "zlib/crc_folding.c", + "zlib/fill_window_sse.c", + "zlib/x86.c", + "zlib/x86.h", + ] + if (!is_win || is_clang) { + cflags += [ + "-msse4.2", + "-mpclmul", + ] + } + if (is_clang) { + cflags += [ "-Wno-incompatible-pointer-types" ] + } + } else { + sources += [ "zlib/simd_stub.c" ] + } } } diff --git a/tools/generate_dump.cc b/tools/generate_dump.cc index 956af81b..a470bfd0 100644 --- a/tools/generate_dump.cc +++ b/tools/generate_dump.cc @@ -29,9 +29,12 @@ #include "util/posix/drop_privileges.h" #include "util/stdlib/string_number_conversion.h" +#if defined(OS_POSIX) +#include +#endif + #if defined(OS_MACOSX) #include -#include #include "base/mac/scoped_mach_port.h" #include "snapshot/mac/process_snapshot_mac.h" @@ -42,6 +45,8 @@ #include "snapshot/win/process_snapshot_win.h" #include "util/win/scoped_process_suspend.h" #include "util/win/xp_compat.h" +#elif defined(OS_FUCHSIA) +#include "snapshot/fuchsia/process_snapshot_fuchsia.h" #endif // OS_MACOSX namespace crashpad { @@ -188,6 +193,12 @@ int GenerateDumpMain(int argc, char* argv[]) { 0)) { return EXIT_FAILURE; } +#elif defined(OS_FUCHSIA) + ProcessSnapshotFuchsia process_snapshot; + // TODO(scottmg): https://crashpad.chromium.org/bug/196. + if (!process_snapshot.Initialize(ZX_HANDLE_INVALID)) { + return EXIT_FAILURE; + } #endif // OS_MACOSX FileWriter file_writer; From ac3cc1b884e0f91d189618f69bafee40c64967b0 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Fri, 8 Dec 2017 12:55:08 -0500 Subject: [PATCH 061/326] Provide a non-explicit constructor for StringAnnotation. This allows brace initializing a C array of StringAnnotation objects. Bug: crashpad:192 Change-Id: Id1b187b67b24bb57251957e9d9c18c16579f1dd4 Reviewed-on: https://chromium-review.googlesource.com/807645 Reviewed-by: Mark Mentovai Commit-Queue: Robert Sesek --- client/annotation.h | 24 ++++++++++++++++++++++++ client/annotation_test.cc | 14 ++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/client/annotation.h b/client/annotation.h index 35bdced4..c7d92d80 100644 --- a/client/annotation.h +++ b/client/annotation.h @@ -196,12 +196,36 @@ class Annotation { template class StringAnnotation : public Annotation { public: + //! \brief A constructor tag that enables braced initialization in C arrays. + //! + //! \sa StringAnnotation() + enum class Tag { kArray }; + //! \brief Constructs a new StringAnnotation with the given \a name. //! //! \param[in] name The Annotation name. constexpr explicit StringAnnotation(const char name[]) : Annotation(Type::kString, name, value_), value_() {} + //! \brief Constructs a new StringAnnotation with the given \a name. + //! + //! This constructor takes the ArrayInitializerTag for use when + //! initializing a C array of annotations. The main constructor is + //! explicit and cannot be brace-initialized. As an example: + //! + //! \code + //! static crashpad::StringAnnotation<32> annotations[] = { + //! {"name-1", crashpad::StringAnnotation<32>::Tag::kArray}, + //! {"name-2", crashpad::StringAnnotation<32>::Tag::kArray}, + //! {"name-3", crashpad::StringAnnotation<32>::Tag::kArray}, + //! }; + //! \endcode + //! + //! \param[in] name The Annotation name. + //! \param[in] tag A constructor tag. + constexpr StringAnnotation(const char name[], Tag tag) + : StringAnnotation(name) {} + //! \brief Sets the Annotation's string value. //! //! \param[in] value The `NUL`-terminated C-string value. diff --git a/client/annotation_test.cc b/client/annotation_test.cc index 656ade0c..365c0c22 100644 --- a/client/annotation_test.cc +++ b/client/annotation_test.cc @@ -14,6 +14,7 @@ #include "client/annotation.h" +#include #include #include "client/annotation_list.h" @@ -107,6 +108,19 @@ TEST_F(Annotation, StringType) { EXPECT_EQ("loooo", annotation.value()); } +TEST(StringAnnotation, ArrayOfString) { + static crashpad::StringAnnotation<4> annotations[] = { + {"test-1", crashpad::StringAnnotation<4>::Tag::kArray}, + {"test-2", crashpad::StringAnnotation<4>::Tag::kArray}, + {"test-3", crashpad::StringAnnotation<4>::Tag::kArray}, + {"test-4", crashpad::StringAnnotation<4>::Tag::kArray}, + }; + + for (auto& annotation : annotations) { + EXPECT_FALSE(annotation.is_set()); + } +} + #if DCHECK_IS_ON() TEST(AnnotationDeathTest, EmbeddedNUL) { From c04c05564db1e2eb27b61cd319b42f8056db8a4b Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Fri, 8 Dec 2017 11:12:32 -0500 Subject: [PATCH 062/326] linux: Fix gcc -Wparentheses error from 7a0daa6989a9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ../../snapshot/elf/elf_image_reader.cc:261:29: error: suggest parentheses around ‘-’ in operand of ‘&’ [-Werror=parentheses] #define PAD(x) ((x) + align - 1 & ~(align - 1)) ~~~~~~~~~~~~^~~ ../../snapshot/elf/elf_image_reader.cc:262:26: note: in expansion of macro ‘PAD’ size_t padded_namesz = PAD(note_info.n_namesz); ^~~ Change-Id: I5cabc2741c8541ae9cf66933d35658e1cbc20912 Reviewed-on: https://chromium-review.googlesource.com/817575 Reviewed-by: Joshua Peraza Commit-Queue: Mark Mentovai --- snapshot/elf/elf_image_reader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapshot/elf/elf_image_reader.cc b/snapshot/elf/elf_image_reader.cc index a78f7e45..b7c9a5f4 100644 --- a/snapshot/elf/elf_image_reader.cc +++ b/snapshot/elf/elf_image_reader.cc @@ -258,7 +258,7 @@ ElfImageReader::NoteReader::Result ElfImageReader::NoteReader::ReadNote( current_address_ += sizeof(note_info); constexpr size_t align = sizeof(note_info.n_namesz); -#define PAD(x) ((x) + align - 1 & ~(align - 1)) +#define PAD(x) (((x) + align - 1) & ~(align - 1)) size_t padded_namesz = PAD(note_info.n_namesz); size_t padded_descsz = PAD(note_info.n_descsz); size_t note_size = padded_namesz + padded_descsz; From 92e8bc4713e12e4083ca6d0736cd8a82009c9f86 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 8 Dec 2017 16:01:18 -0800 Subject: [PATCH 063/326] win: Fix ProcessInfo.OtherProcess test flake Removes the /BASE:N and /FIXED arguments to the child, which weren't actually testing correctly (see bug), and were causing problems at least on Win7 when something collided with that address. Additionally, switches to storing modules in load order, rather than a combination of memory order and initialization order, since that was a bit confusing and there was no great rationale for it. While reviewing, handle the case of a corrupted module name, and if it's unreadable continue emitting "???" as a name. Adds a test for this functionality. Bug: chromium:792619 Change-Id: I2e95a81b02fe4d527868f6a5f980d315604255a6 Reviewed-on: https://chromium-review.googlesource.com/815875 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- util/BUILD.gn | 8 ---- util/util_test.gyp | 12 ------ util/win/process_info.cc | 35 ++++------------- util/win/process_info_test.cc | 13 +++++-- util/win/process_info_test_child.cc | 59 ++++++++++++++++++++++++++++- 5 files changed, 74 insertions(+), 53 deletions(-) diff --git a/util/BUILD.gn b/util/BUILD.gn index 38a20128..ba5232f0 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -491,14 +491,6 @@ if (is_win) { sources = [ "win/process_info_test_child.cc", ] - - # Set an unusually high load address to make sure that the main executable - # still appears as the first element in ProcessInfo::Modules(). - ldflags = [ - "/BASE:0x78000000", - "/DYNAMICBASE:NO", - "/FIXED", - ] } executable("crashpad_util_test_safe_terminate_process_test_child") { diff --git a/util/util_test.gyp b/util/util_test.gyp index 30d86001..f9c114db 100644 --- a/util/util_test.gyp +++ b/util/util_test.gyp @@ -169,18 +169,6 @@ 'sources': [ 'win/process_info_test_child.cc', ], - # Set an unusually high load address to make sure that the main - # executable still appears as the first element in - # ProcessInfo::Modules(). - 'msvs_settings': { - 'VCLinkerTool': { - 'AdditionalOptions': [ - '/BASE:0x78000000', - ], - 'RandomizedBaseAddress': '1', # /DYNAMICBASE:NO. - 'FixedBaseAddress': '2', # /FIXED. - }, - }, }, { 'target_name': 'crashpad_util_test_safe_terminate_process_test_child', diff --git a/util/win/process_info.cc b/util/win/process_info.cc index 3f005791..cd6bcd3e 100644 --- a/util/win/process_info.cc +++ b/util/win/process_info.cc @@ -269,36 +269,15 @@ bool ReadProcessData(HANDLE process, return false; process_types::LDR_DATA_TABLE_ENTRY ldr_data_table_entry; - - // Include the first module in the memory order list to get our the main - // executable's name, as it's not included in initialization order below. - if (!ReadStruct(process, - static_cast( - peb_ldr_data.InMemoryOrderModuleList.Flink) - - offsetof(process_types::LDR_DATA_TABLE_ENTRY, - InMemoryOrderLinks), - &ldr_data_table_entry)) { - return false; - } ProcessInfo::Module module; - if (!ReadUnicodeString( - process, ldr_data_table_entry.FullDllName, &module.name)) { - return false; - } - module.dll_base = ldr_data_table_entry.DllBase; - module.size = ldr_data_table_entry.SizeOfImage; - module.timestamp = ldr_data_table_entry.TimeDateStamp; - process_info->modules_.push_back(module); // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded // modules. We use this method rather than EnumProcessModules to get the - // modules in initialization order rather than memory order. - typename Traits::Pointer last = - peb_ldr_data.InInitializationOrderModuleList.Blink; - for (typename Traits::Pointer cur = - peb_ldr_data.InInitializationOrderModuleList.Flink; - ; - cur = ldr_data_table_entry.InInitializationOrderLinks.Flink) { + // modules in load order rather than memory order. Notably, this includes the + // main executable as the first element. + typename Traits::Pointer last = peb_ldr_data.InLoadOrderModuleList.Blink; + for (typename Traits::Pointer cur = peb_ldr_data.InLoadOrderModuleList.Flink;; + cur = ldr_data_table_entry.InLoadOrderLinks.Flink) { // |cur| is the pointer to the LIST_ENTRY embedded in the // LDR_DATA_TABLE_ENTRY, in the target process's address space. So we need // to read from the target, and also offset back to the beginning of the @@ -306,14 +285,14 @@ bool ReadProcessData(HANDLE process, if (!ReadStruct(process, static_cast(cur) - offsetof(process_types::LDR_DATA_TABLE_ENTRY, - InInitializationOrderLinks), + InLoadOrderLinks), &ldr_data_table_entry)) { break; } // TODO(scottmg): Capture Checksum, etc. too? if (!ReadUnicodeString( process, ldr_data_table_entry.FullDllName, &module.name)) { - break; + module.name = L"???"; } module.dll_base = ldr_data_table_entry.DllBase; module.size = ldr_data_table_entry.SizeOfImage; diff --git a/util/win/process_info_test.cc b/util/win/process_info_test.cc index 709536b9..c7abdb6f 100644 --- a/util/win/process_info_test.cc +++ b/util/win/process_info_test.cc @@ -180,11 +180,18 @@ void TestOtherProcess(TestPaths::Architecture architecture) { // lz32.dll is an uncommonly-used-but-always-available module that the test // binary manually loads. static constexpr wchar_t kLz32dllName[] = L"\\lz32.dll"; - ASSERT_GE(modules.back().name.size(), wcslen(kLz32dllName)); - EXPECT_EQ(modules.back().name.substr(modules.back().name.size() - - wcslen(kLz32dllName)), + auto& lz32 = modules[modules.size() - 2]; + ASSERT_GE(lz32.name.size(), wcslen(kLz32dllName)); + EXPECT_EQ(lz32.name.substr(lz32.name.size() - wcslen(kLz32dllName)), kLz32dllName); + // Note that the test code corrupts the PEB MemoryOrder list, whereas + // ProcessInfo::Modules() retrieves the module names via the PEB LoadOrder + // list. These are expected to point to the same strings, but theoretically + // could be separate. + auto& corrupted = modules.back(); + EXPECT_EQ(corrupted.name, L"???"); + VerifyAddressInInCodePage(process_info, code_address); } diff --git a/util/win/process_info_test_child.cc b/util/win/process_info_test_child.cc index c587f5b8..1de1e8b2 100644 --- a/util/win/process_info_test_child.cc +++ b/util/win/process_info_test_child.cc @@ -13,10 +13,26 @@ // limitations under the License. #include -#include #include +#include +#include #include #include +#include + +namespace { + +bool UnicodeStringEndsWithCaseInsensitive(const UNICODE_STRING& us, + const wchar_t* ends_with) { + const size_t len = wcslen(ends_with); + // Recall that UNICODE_STRING.Length is in bytes, not characters. + const size_t us_len_in_chars = us.Length / sizeof(wchar_t); + if (us_len_in_chars < len) + return false; + return _wcsnicmp(&us.Buffer[us_len_in_chars - len], ends_with, len) == 0; +} + +} // namespace // A simple binary to be loaded and inspected by ProcessInfo. int wmain(int argc, wchar_t** argv) { @@ -29,10 +45,49 @@ int wmain(int argc, wchar_t** argv) { abort(); // Load an unusual module (that we don't depend upon) so we can do an - // existence check. + // existence check. It's also important that these DLLs don't depend on + // any other DLLs, otherwise there'll be additional modules in the list, which + // the test expects not to be there. if (!LoadLibrary(L"lz32.dll")) abort(); + // Load another unusual module so we can destroy its FullDllName field in the + // PEB to test corrupted name reads. + static constexpr wchar_t kCorruptableDll[] = L"kbdurdu.dll"; + if (!LoadLibrary(kCorruptableDll)) + abort(); + + // Find and corrupt the buffer pointer to the name in the PEB. + HINSTANCE ntdll = GetModuleHandle(L"ntdll.dll"); + decltype(NtQueryInformationProcess)* nt_query_information_process = + reinterpret_cast( + GetProcAddress(ntdll, "NtQueryInformationProcess")); + if (!nt_query_information_process) + abort(); + + PROCESS_BASIC_INFORMATION pbi; + if (nt_query_information_process(GetCurrentProcess(), + ProcessBasicInformation, + &pbi, + sizeof(pbi), + nullptr) < 0) { + abort(); + } + + PEB_LDR_DATA* ldr = pbi.PebBaseAddress->Ldr; + LIST_ENTRY* head = &ldr->InMemoryOrderModuleList; + LIST_ENTRY* next = head->Flink; + while (next != head) { + LDR_DATA_TABLE_ENTRY* entry = + CONTAINING_RECORD(next, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); + if (UnicodeStringEndsWithCaseInsensitive(entry->FullDllName, + kCorruptableDll)) { + // Corrupt the pointer to the name. + entry->FullDllName.Buffer = 0; + } + next = next->Flink; + } + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); if (out == INVALID_HANDLE_VALUE) abort(); From 0cc63d4a45582e5231cc903e6b2e0999c3c322ff Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Fri, 8 Dec 2017 18:47:12 -0500 Subject: [PATCH 064/326] android: Make run_tests.py work on Android versions before 7.0 (N) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I took yesterday’s work and tried using it to run tests on a Nexus 4 running 5.1.1 (L), and absolutely nothing worked. The highlights: - There’s no /system/bin/mktemp. - There’s no /system/bin/env. - “adb shell” doesn’t know what the command’s exit status was. While I’m in here, I’ll also make colored gtest output work, although it won’t work on the normal Windows console which doesn’t understand ANSI color codes. (It might work in Cygwin?) Plus some bonus bloopers: - I was trying to catch an exception that isn’t even defined in Python 2! - The part of the script that tells you what test it’s about to run had fallen into a conditional block, preventing it from being shown except when running end_to_end_test.py. Bug: crashpad:30 Change-Id: I98fc410f90a2b4e91cb3cacb6a8decf2a8c2252b Reviewed-on: https://chromium-review.googlesource.com/818125 Commit-Queue: Mark Mentovai Reviewed-by: Scott Graham --- build/run_tests.py | 157 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 122 insertions(+), 35 deletions(-) diff --git a/build/run_tests.py b/build/run_tests.py index 67c3a03f..c636c006 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -46,17 +46,15 @@ def _BinaryDirTargetOS(binary_dir): # For GYP with Ninja, look for the appearance of “linux-android” in the path # to ar. This path is configured by gyp_crashpad_android.py. - try: - with open(os.path.join(binary_dir, 'build.ninja')) as build_ninja_file: + build_ninja_path = os.path.join(binary_dir, 'build.ninja') + if os.path.exists(build_ninja_path): + with open(build_ninja_path) as build_ninja_file: build_ninja_content = build_ninja_file.read() match = re.search('^ar = .+-linux-android(eabi)?-ar$', build_ninja_content, re.MULTILINE) if match: return 'android' - except FileNotFoundError: - # Ninja may not be in use. Assume the best. - pass return None @@ -70,13 +68,95 @@ def _RunOnAndroidTarget(binary_dir, test, android_device): 'crashpad_snapshot_test', ) if not os.path.exists(local_test_path) and test in MAYBE_UNSUPPORTED_TESTS: - print(test, 'is not present and may not be supported, skipping') + print('This test is not present and may not be supported, skipping') return - device_temp_dir = subprocess.check_output( - ['adb', '-s', android_device, 'shell', - 'mktemp', '-d', '/data/local/tmp/%s.XXXXXXXX' % test], - shell=IS_WINDOWS_HOST).decode('utf-8').rstrip() + def _adb(*args): + # Flush all of this script’s own buffered stdout output before running adb, + # which will likely produce its own output on stdout. + sys.stdout.flush() + + adb_command = ['adb', '-s', android_device] + adb_command.extend(args) + subprocess.check_call(adb_command, shell=IS_WINDOWS_HOST) + + def _adb_push(sources, destination): + args = list(sources) + args.append(destination) + _adb('push', *args) + + def _adb_shell(command_args, env={}): + # Build a command to execute via “sh -c” instead of invoking it directly. + # Here’s why: + # + # /system/bin/env isn’t normally present prior to Android 6.0 (M), where + # toybox was introduced (Android platform/manifest 9a2c01e8450b). Instead, + # set environment variables by using the shell’s internal “export” command. + # + # adbd prior to Android 7.0 (N), and the adb client prior to SDK + # platform-tools version 24, don’t know how to communicate a shell command’s + # exit status. This was added in Android platform/system/core 606835ae5c4b). + # With older adb servers and clients, adb will “exit 0” indicating success + # even if the command failed on the device. This makes + # subprocess.check_call() semantics difficult to implement directly. As a + # workaround, have the device send the command’s exit status over stdout and + # pick it back up in this function. + # + # Both workarounds are implemented by giving the device a simple script, + # which adbd will run as an “sh -c” argument. + adb_command = ['adb', '-s', android_device, 'shell'] + script_commands = [] + for k, v in env.items(): + script_commands.append('export %s=%s' % (pipes.quote(k), pipes.quote(v))) + script_commands.extend([ + ' '.join(pipes.quote(x) for x in command_args), + 'status=${?}', + 'echo "status=${status}"', + 'exit ${status}']) + adb_command.append('; '.join(script_commands)) + child = subprocess.Popen(adb_command, + shell=IS_WINDOWS_HOST, + stdin=open(os.devnull), + stdout=subprocess.PIPE) + + FINAL_LINE_RE = re.compile('status=(\d+)$') + final_line = None + while True: + # Use readline so that the test output appears “live” when running. + data = child.stdout.readline().decode('utf-8') + if data == '': + break + if final_line is not None: + # It wasn’t really the final line. + print(final_line, end='') + final_line = None + if FINAL_LINE_RE.match(data.rstrip()): + final_line = data + else: + print(data, end='') + + if final_line is None: + # Maybe there was some stderr output after the end of stdout. Old versions + # of adb, prior to when the exit status could be communicated, smush the + # two together. + raise subprocess.CalledProcessError(-1, adb_command) + status = int(FINAL_LINE_RE.match(final_line.rstrip()).group(1)) + if status != 0: + raise subprocess.CalledProcessError(status, adb_command) + + child.wait() + if child.returncode != 0: + raise subprocess.CalledProcessError(subprocess.returncode, adb_command) + + # /system/bin/mktemp isn’t normally present prior to Android 6.0 (M), where + # toybox was introduced (Android platform/manifest 9a2c01e8450b). Fake it with + # a host-generated name. This won’t retry if the name is in use, but with 122 + # bits of randomness, it should be OK. This uses “mkdir” instead of “mkdir -p” + # because the latter will not indicate failure if the directory already + # exists. + device_temp_dir = '/data/local/tmp/%s.%s' % (test, uuid.uuid4().hex) + _adb_shell(['mkdir', device_temp_dir]) + try: # Specify test dependencies that must be pushed to the device. This could be # determined automatically in a GN build, following the example used for @@ -91,15 +171,6 @@ def _RunOnAndroidTarget(binary_dir, test, android_device): elif test == 'crashpad_util_test': test_data.append('util/net/testdata/') - def _adb(*args): - # Flush all of this script’s own buffered stdout output before running - # adb, which will likely produce its own output on stdout. - sys.stdout.flush() - - adb_command = ['adb', '-s', android_device] - adb_command.extend(args) - subprocess.check_call(adb_command, shell=IS_WINDOWS_HOST) - # Establish the directory structure on the device. device_out_dir = posixpath.join(device_temp_dir, 'out') device_mkdirs = [device_out_dir] @@ -117,28 +188,44 @@ def _RunOnAndroidTarget(binary_dir, test, android_device): device_mkdir = posixpath.split(device_source_path)[0] if device_mkdir not in device_mkdirs: device_mkdirs.append(device_mkdir) - adb_mkdir_command = ['shell', 'mkdir', '-p'] + adb_mkdir_command = ['mkdir', '-p'] adb_mkdir_command.extend(device_mkdirs) - _adb(*adb_mkdir_command) + _adb_shell(adb_mkdir_command) # Push the test binary and any other build output to the device. - adb_push_command = ['push'] + local_test_build_artifacts = [] for artifact in test_build_artifacts: - adb_push_command.append(os.path.join(binary_dir, artifact)) - adb_push_command.append(device_out_dir) - _adb(*adb_push_command) + local_test_build_artifacts.append(os.path.join(binary_dir, artifact)) + _adb_push(local_test_build_artifacts, device_out_dir) # Push test data to the device. for source_path in test_data: - _adb('push', - os.path.join(CRASHPAD_DIR, source_path), - posixpath.join(device_temp_dir, source_path)) + _adb_push([os.path.join(CRASHPAD_DIR, source_path)], + posixpath.join(device_temp_dir, source_path)) - # Run the test on the device. - _adb('shell', 'env', 'CRASHPAD_TEST_DATA_ROOT=' + device_temp_dir, - posixpath.join(device_out_dir, test)) + # Run the test on the device. Pass the test data root in the environment. + # + # Because the test will not run with its standard output attached to a + # pseudo-terminal device, gtest will not normally enable colored output, so + # mimic gtest’s own logic for deciding whether to enable color by checking + # this script’s own standard output connection. The whitelist of TERM values + # comes from gtest googletest/src/gtest.cc + # testing::internal::ShouldUseColor(). + env = {'CRASHPAD_TEST_DATA_ROOT': device_temp_dir} + gtest_color = os.environ.get('GTEST_COLOR') + if gtest_color in ('auto', None): + if (sys.stdout.isatty() and + os.environ.get('TERM') in + ('xterm', 'xterm-color', 'xterm-256color', 'screen', + 'screen-256color', 'tmux', 'tmux-256color', 'rxvt-unicode', + 'rxvt-unicode-256color', 'linux', 'cygwin')): + gtest_color = 'yes' + else: + gtest_color = 'no' + env['GTEST_COLOR'] = gtest_color + _adb_shell([posixpath.join(device_out_dir, test)], env) finally: - _adb('shell', 'rm', '-rf', device_temp_dir) + _adb_shell(['rm', '-rf', device_temp_dir]) def _GetFuchsiaSDKRoot(): @@ -330,10 +417,10 @@ def main(args): tests = [single_test] for test in tests: + print('-' * 80) + print(test) + print('-' * 80) if test.endswith('.py'): - print('-' * 80) - print(test) - print('-' * 80) subprocess.check_call( [sys.executable, os.path.join(CRASHPAD_DIR, test), binary_dir]) else: From 13d0defbfb45c579bac8952572a1cc3c0434ae17 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Mon, 11 Dec 2017 12:04:46 -0500 Subject: [PATCH 065/326] android, win: Enable colored test output when available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following the discussion at https://crrev.com/c/818125/3//COMMIT_MSG#17, this sets GTEST_COLOR=yes when running tests on an Android device via “adb” being driven from a Windows host. This is only done when standard output is attached to a console and when ENABLE_VIRTUAL_TERMINAL_PROCESSING is supported (it is on Windows 10). As usual, colored output can be suppressed by setting GTEST_COLOR=no. This is only partially tested. Instead of running on-device tests via adb, I substituted: print('\x1b[0;31mred\x1b[32mgreen\x1b[34mblue\x1b[0m') Change-Id: I3ef67f3890f18f7012111171a5e0eab4addca7b8 Reviewed-on: https://chromium-review.googlesource.com/819597 Reviewed-by: Scott Graham Commit-Queue: Mark Mentovai --- build/run_tests.py | 47 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/build/run_tests.py b/build/run_tests.py index c636c006..e180a6cb 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -59,6 +59,44 @@ def _BinaryDirTargetOS(binary_dir): return None +def _EnableVTProcessingOnWindowsConsole(): + """Enables virtual terminal processing for ANSI/VT100-style escape sequences + on a Windows console attached to standard output. Returns True on success. + Returns False if standard output is not a console or if virtual terminal + processing is not supported. The feature was introduced in Windows 10. + """ + + import pywintypes + import win32console + import winerror + + stdout_console = win32console.GetStdHandle(win32console.STD_OUTPUT_HANDLE) + try: + console_mode = stdout_console.GetConsoleMode() + except pywintypes.error as e: + if e.winerror == winerror.ERROR_INVALID_HANDLE: + # Standard output is not a console. + return False + raise + + try: + # From . This would be + # win32console.ENABLE_VIRTUAL_TERMINAL_PROCESSING, but it’s too new to be + # defined there. + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + + stdout_console.SetConsoleMode(console_mode | + ENABLE_VIRTUAL_TERMINAL_PROCESSING) + except pywintypes.error as e: + if e.winerror == winerror.ERROR_INVALID_PARAMETER: + # ANSI/VT100-style escape sequence processing isn’t supported before + # Windows 10. + return False + raise + + return True + + def _RunOnAndroidTarget(binary_dir, test, android_device): local_test_path = os.path.join(binary_dir, test) MAYBE_UNSUPPORTED_TESTS = ( @@ -215,10 +253,11 @@ def _RunOnAndroidTarget(binary_dir, test, android_device): gtest_color = os.environ.get('GTEST_COLOR') if gtest_color in ('auto', None): if (sys.stdout.isatty() and - os.environ.get('TERM') in - ('xterm', 'xterm-color', 'xterm-256color', 'screen', - 'screen-256color', 'tmux', 'tmux-256color', 'rxvt-unicode', - 'rxvt-unicode-256color', 'linux', 'cygwin')): + (os.environ.get('TERM') in + ('xterm', 'xterm-color', 'xterm-256color', 'screen', + 'screen-256color', 'tmux', 'tmux-256color', 'rxvt-unicode', + 'rxvt-unicode-256color', 'linux', 'cygwin') or + (IS_WINDOWS_HOST and _EnableVTProcessingOnWindowsConsole()))): gtest_color = 'yes' else: gtest_color = 'no' From 0924e567514fc577e725b56cd602808467b317a9 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Mon, 11 Dec 2017 10:23:17 -0800 Subject: [PATCH 066/326] linux: Add PtraceBroker and PtraceClient A PtraceBroker/Client pair implement a PtraceConnection over a socket. The broker runs in a process with `ptrace` capabilities for the target process and serves requests for the client over a socket. Bug: crashpad:30 Change-Id: Ied19bcedf84b46c8f68440fd1c284b2126470e5e Reviewed-on: https://chromium-review.googlesource.com/780397 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- snapshot/linux/process_reader_test.cc | 23 +-- test/linux/get_tls.cc | 45 +++++ test/linux/get_tls.h | 29 ++++ test/test.gyp | 2 + util/linux/direct_ptrace_connection.cc | 2 +- util/linux/ptrace_broker.cc | 187 +++++++++++++++++++++ util/linux/ptrace_broker.h | 146 ++++++++++++++++ util/linux/ptrace_broker_test.cc | 222 +++++++++++++++++++++++++ util/linux/ptrace_client.cc | 178 ++++++++++++++++++++ util/linux/ptrace_client.h | 85 ++++++++++ util/linux/ptracer.cc | 187 +++++++++++++++------ util/linux/ptracer.h | 28 +++- util/linux/ptracer_test.cc | 23 +-- util/util.gyp | 4 + util/util_test.gyp | 1 + 15 files changed, 1063 insertions(+), 99 deletions(-) create mode 100644 test/linux/get_tls.cc create mode 100644 test/linux/get_tls.h create mode 100644 util/linux/ptrace_broker.cc create mode 100644 util/linux/ptrace_broker.h create mode 100644 util/linux/ptrace_broker_test.cc create mode 100644 util/linux/ptrace_client.cc create mode 100644 util/linux/ptrace_client.h diff --git a/snapshot/linux/process_reader_test.cc b/snapshot/linux/process_reader_test.cc index 08edf02a..4bdee743 100644 --- a/snapshot/linux/process_reader_test.cc +++ b/snapshot/linux/process_reader_test.cc @@ -35,6 +35,7 @@ #include "gtest/gtest.h" #include "test/errors.h" #include "test/linux/fake_ptrace_connection.h" +#include "test/linux/get_tls.h" #include "test/multiprocess.h" #include "util/file/file_io.h" #include "util/linux/direct_ptrace_connection.h" @@ -49,28 +50,6 @@ pid_t gettid() { return syscall(SYS_gettid); } -LinuxVMAddress GetTLS() { - LinuxVMAddress tls; -#if defined(ARCH_CPU_ARMEL) - // 0xffff0fe0 is the address of the kernel user helper __kuser_get_tls(). - auto kuser_get_tls = reinterpret_cast(0xffff0fe0); - tls = FromPointerCast(kuser_get_tls()); -#elif defined(ARCH_CPU_ARM64) - // Linux/aarch64 places the tls address in system register tpidr_el0. - asm("mrs %0, tpidr_el0" : "=r"(tls)); -#elif defined(ARCH_CPU_X86) - uint32_t tls_32; - asm("movl %%gs:0x0, %0" : "=r"(tls_32)); - tls = tls_32; -#elif defined(ARCH_CPU_X86_64) - asm("movq %%fs:0x0, %0" : "=r"(tls)); -#else -#error Port. -#endif // ARCH_CPU_ARMEL - - return tls; -} - TEST(ProcessReader, SelfBasic) { FakePtraceConnection connection; connection.Initialize(getpid()); diff --git a/test/linux/get_tls.cc b/test/linux/get_tls.cc new file mode 100644 index 00000000..47d041e4 --- /dev/null +++ b/test/linux/get_tls.cc @@ -0,0 +1,45 @@ +// 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 "test/linux/get_tls.h" + +#include "build/build_config.h" +#include "util/misc/from_pointer_cast.h" + +namespace crashpad { +namespace test { + +LinuxVMAddress GetTLS() { + LinuxVMAddress tls; +#if defined(ARCH_CPU_ARMEL) + // 0xffff0fe0 is the address of the kernel user helper __kuser_get_tls(). + auto kuser_get_tls = reinterpret_cast(0xffff0fe0); + tls = FromPointerCast(kuser_get_tls()); +#elif defined(ARCH_CPU_ARM64) + // Linux/aarch64 places the tls address in system register tpidr_el0. + asm("mrs %0, tpidr_el0" : "=r"(tls)); +#elif defined(ARCH_CPU_X86) + uint32_t tls_32; + asm("movl %%gs:0x0, %0" : "=r"(tls_32)); + tls = tls_32; +#elif defined(ARCH_CPU_X86_64) + asm("movq %%fs:0x0, %0" : "=r"(tls)); +#else +#error Port. +#endif // ARCH_CPU_ARMEL + return tls; +} + +} // namespace test +} // namespace crashpad diff --git a/test/linux/get_tls.h b/test/linux/get_tls.h new file mode 100644 index 00000000..5d59144e --- /dev/null +++ b/test/linux/get_tls.h @@ -0,0 +1,29 @@ +// 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_TEST_LINUX_GET_TLS_H_ +#define CRASHPAD_TEST_LINUX_GET_TLS_H_ + +#include "util/linux/address_types.h" + +namespace crashpad { +namespace test { + +//! \brief Return the thread-local storage address for the current thread. +LinuxVMAddress GetTLS(); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_LINUX_GET_TLS_H_ diff --git a/test/test.gyp b/test/test.gyp index ce0c817e..ce2ba7d5 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -43,6 +43,8 @@ 'hex_string.h', 'linux/fake_ptrace_connection.cc', 'linux/fake_ptrace_connection.h', + 'linux/get_tls.cc', + 'linux/get_tls.h', 'mac/dyld.cc', 'mac/dyld.h', 'mac/exception_swallower.cc', diff --git a/util/linux/direct_ptrace_connection.cc b/util/linux/direct_ptrace_connection.cc index 78092bb9..27ad5b11 100644 --- a/util/linux/direct_ptrace_connection.cc +++ b/util/linux/direct_ptrace_connection.cc @@ -22,7 +22,7 @@ DirectPtraceConnection::DirectPtraceConnection() : PtraceConnection(), attachments_(), pid_(-1), - ptracer_(), + ptracer_(/* can_log= */ true), initialized_() {} DirectPtraceConnection::~DirectPtraceConnection() {} diff --git a/util/linux/ptrace_broker.cc b/util/linux/ptrace_broker.cc new file mode 100644 index 00000000..8810a48f --- /dev/null +++ b/util/linux/ptrace_broker.cc @@ -0,0 +1,187 @@ +// 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 "util/linux/ptrace_broker.h" + +#include + +#include "base/logging.h" +#include "util/file/file_io.h" + +namespace crashpad { + +PtraceBroker::PtraceBroker(int sock, bool is_64_bit) + : ptracer_(is_64_bit, /* can_log= */ false), + attachments_(nullptr), + attach_count_(0), + attach_capacity_(0), + sock_(sock) { + AllocateAttachments(); +} + +PtraceBroker::~PtraceBroker() = default; + +int PtraceBroker::Run() { + int result = RunImpl(); + ReleaseAttachments(); + return result; +} + +int PtraceBroker::RunImpl() { + while (true) { + Request request = {}; + if (!ReadFileExactly(sock_, &request, sizeof(request))) { + return errno; + } + + if (request.version != Request::kVersion) { + return EINVAL; + } + + switch (request.type) { + case Request::kTypeAttach: { + ScopedPtraceAttach* attach; + ScopedPtraceAttach stack_attach; + bool attach_on_stack = false; + + if (attach_capacity_ > attach_count_ || AllocateAttachments()) { + attach = new (&attachments_[attach_count_]) ScopedPtraceAttach; + } else { + attach = &stack_attach; + attach_on_stack = true; + } + + Bool status = kBoolFalse; + if (attach->ResetAttach(request.tid)) { + status = kBoolTrue; + if (!attach_on_stack) { + ++attach_count_; + } + } + + if (!WriteFile(sock_, &status, sizeof(status))) { + return errno; + } + + if (status == kBoolFalse) { + Errno error = errno; + if (!WriteFile(sock_, &error, sizeof(error))) { + return errno; + } + } + + if (attach_on_stack && status == kBoolTrue) { + return RunImpl(); + } + continue; + } + + case Request::kTypeIs64Bit: { + Bool is_64_bit = ptracer_.Is64Bit() ? kBoolTrue : kBoolFalse; + if (!WriteFile(sock_, &is_64_bit, sizeof(is_64_bit))) { + return errno; + } + continue; + } + + case Request::kTypeGetThreadInfo: { + GetThreadInfoResponse response; + response.success = ptracer_.GetThreadInfo(request.tid, &response.info) + ? kBoolTrue + : kBoolFalse; + + if (!WriteFile(sock_, &response, sizeof(response))) { + return errno; + } + + if (response.success == kBoolFalse) { + Errno error = errno; + if (!WriteFile(sock_, &error, sizeof(error))) { + return errno; + } + } + continue; + } + + case Request::kTypeReadMemory: { + int result = + SendMemory(request.tid, request.iov.base, request.iov.size); + if (result != 0) { + return result; + } + continue; + } + + case Request::kTypeExit: + return 0; + } + + DCHECK(false); + return EINVAL; + } +} + +int PtraceBroker::SendMemory(pid_t pid, VMAddress address, VMSize size) { + char buffer[4096]; + while (size > 0) { + VMSize bytes_read = std::min(size, VMSize{sizeof(buffer)}); + + if (!ptracer_.ReadMemory(pid, address, bytes_read, buffer)) { + bytes_read = 0; + Errno error = errno; + if (!WriteFile(sock_, &bytes_read, sizeof(bytes_read)) || + !WriteFile(sock_, &error, sizeof(error))) { + return errno; + } + return 0; + } + + if (!WriteFile(sock_, &bytes_read, sizeof(bytes_read))) { + return errno; + } + + if (!WriteFile(sock_, buffer, bytes_read)) { + return errno; + } + + size -= bytes_read; + address += bytes_read; + } + return 0; +} + +bool PtraceBroker::AllocateAttachments() { + constexpr size_t page_size = 4096; + constexpr size_t alloc_size = + (sizeof(ScopedPtraceAttach) + page_size - 1) & ~(page_size - 1); + void* alloc = sbrk(alloc_size); + if (reinterpret_cast(alloc) == -1) { + return false; + } + + if (attachments_ == nullptr) { + attachments_ = reinterpret_cast(alloc); + } + + attach_capacity_ += alloc_size / sizeof(ScopedPtraceAttach); + return true; +} + +void PtraceBroker::ReleaseAttachments() { + for (size_t index = 0; index < attach_count_; ++index) { + attachments_[index].Reset(); + } +} + +} // namespace crashpad diff --git a/util/linux/ptrace_broker.h b/util/linux/ptrace_broker.h new file mode 100644 index 00000000..d725cb7b --- /dev/null +++ b/util/linux/ptrace_broker.h @@ -0,0 +1,146 @@ +// 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_UTIL_LINUX_PTRACE_BROKER_H_ +#define CRASHPAD_UTIL_LINUX_PTRACE_BROKER_H_ + +#include +#include +#include + +#include "base/macros.h" +#include "util/linux/ptrace_connection.h" +#include "util/linux/ptracer.h" +#include "util/linux/scoped_ptrace_attach.h" +#include "util/linux/thread_info.h" +#include "util/misc/address_types.h" + +namespace crashpad { + +//! \brief Implements a PtraceConnection over a socket. +//! +//! This class is the server half of the connection. The broker should be run +//! in a process with `ptrace` capabilities for the target process and may run +//! in a compromised context. +class PtraceBroker { + public: +#pragma pack(push, 1) + //! \brief A request sent to a PtraceBroker from a PtraceClient. + struct Request { + static constexpr uint16_t kVersion = 1; + + //! \brief The version number for this Request. + uint16_t version = kVersion; + + //! \brief The type of request to serve. + enum Type : uint16_t { + //! \brief `ptrace`-attach the specified thread ID. Responds with + //! kBoolTrue on success, otherwise kBoolFalse, followed by an Errno. + kTypeAttach, + + //! \brief Responds with kBoolTrue if the target process is 64-bit. + //! Otherwise, kBoolFalse. + kTypeIs64Bit, + + //! \brief Responds with a GetThreadInfoResponse containing a ThreadInfo + //! for the specified thread ID. If an error occurs, + //! GetThreadInfoResponse::success is set to kBoolFalse and is + //! followed by an Errno. + kTypeGetThreadInfo, + + //! \brief Reads memory from the attached process. The data is returned in + //! a series of messages. Each message begins with a VMSize indicating + //! the number of bytes being returned in this message, followed by + //! the requested bytes. The broker continues to send messages until + //! either all of the requested memory has been sent or an error + //! occurs, in which case it sends a message containing a VMSize equal + //! to zero, followed by an Errno. + kTypeReadMemory, + + //! \brief Causes the broker to return from Run(), detaching all attached + //! threads. Does not respond. + kTypeExit + } type; + + //! \brief The thread ID associated with this request. Valid for kTypeAttach, + //! kTypeGetThreadInfo, and kTypeReadMemory. + pid_t tid; + + //! \brief Specifies the memory region to read for a kTypeReadMemory request. + struct { + //! \brief The base address of the memory region. + VMAddress base; + + //! \brief The size of the memory region. + VMSize size; + } iov; + }; + + //! \brief The type used for error reporting. + using Errno = int32_t; + static_assert(sizeof(Errno) >= sizeof(errno), "Errno type is too small"); + + //! \brief A boolean status suitable for communication between processes. + enum Bool : char { kBoolFalse, kBoolTrue }; + + //! \brief The response sent for a Request with type kTypeGetThreadInfo. + struct GetThreadInfoResponse { + //! \brief Information about the specified thread. Only valid if #success + //! is kBoolTrue. + ThreadInfo info; + + //! \brief Specifies the success or failure of this call. + Bool success; + }; +#pragma pack(pop) + + //! \brief Constructs this object. + //! + //! \param[in] sock A socket on which to read requests from a connected + //! PtraceClient. Does not take ownership of the socket. + //! \param[in] is_64_bit Whether this broker should be configured to trace a + //! 64-bit process. + PtraceBroker(int sock, bool is_64_bit); + + ~PtraceBroker(); + + //! \brief Begin serving requests on the configured socket. + //! + //! This method returns when a PtraceBrokerRequest with type kTypeExit is + //! received or an error is encountered on the socket. + //! + //! This method calls `sbrk`, which may break other memory management tools, + //! such as `malloc`. + //! + //! \return 0 if Run() exited due to an exit request. Otherwise an error code. + int Run(); + + private: + int RunImpl(); + int SendMemory(pid_t pid, VMAddress address, VMSize size); + bool AllocateAttachments(); + void ReleaseAttachments(); + + Ptracer ptracer_; + ScopedPtraceAttach* attachments_; + size_t attach_count_; + size_t attach_capacity_; + int sock_; + + DISALLOW_COPY_AND_ASSIGN(PtraceBroker); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_LINUX_PTRACE_BROKER_H_ diff --git a/util/linux/ptrace_broker_test.cc b/util/linux/ptrace_broker_test.cc new file mode 100644 index 00000000..749df759 --- /dev/null +++ b/util/linux/ptrace_broker_test.cc @@ -0,0 +1,222 @@ +// 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 "util/linux/ptrace_broker.h" + +#include +#include +#include +#include + +#include + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/linux/get_tls.h" +#include "test/multiprocess.h" +#include "util/file/file_io.h" +#include "util/linux/ptrace_client.h" +#include "util/posix/scoped_mmap.h" +#include "util/synchronization/semaphore.h" +#include "util/thread/thread.h" + +namespace crashpad { +namespace test { +namespace { + +class ScopedTimeoutThread : public Thread { + public: + ScopedTimeoutThread() : join_sem_(0) {} + ~ScopedTimeoutThread() { EXPECT_TRUE(JoinWithTimeout(5.0)); } + + protected: + void ThreadMain() override { join_sem_.Signal(); } + + private: + bool JoinWithTimeout(double timeout) { + if (!join_sem_.TimedWait(timeout)) { + return false; + } + Join(); + return true; + } + + Semaphore join_sem_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTimeoutThread); +}; + +class RunBrokerThread : public ScopedTimeoutThread { + public: + RunBrokerThread(PtraceBroker* broker) + : ScopedTimeoutThread(), broker_(broker) {} + + ~RunBrokerThread() {} + + private: + void ThreadMain() override { + EXPECT_EQ(broker_->Run(), 0); + ScopedTimeoutThread::ThreadMain(); + } + + PtraceBroker* broker_; + + DISALLOW_COPY_AND_ASSIGN(RunBrokerThread); +}; + +class BlockOnReadThread : public ScopedTimeoutThread { + public: + BlockOnReadThread(int readfd, int writefd) + : ScopedTimeoutThread(), readfd_(readfd), writefd_(writefd) {} + + ~BlockOnReadThread() {} + + private: + void ThreadMain() override { + pid_t pid = syscall(SYS_gettid); + LoggingWriteFile(writefd_, &pid, sizeof(pid)); + + LinuxVMAddress tls = GetTLS(); + LoggingWriteFile(writefd_, &tls, sizeof(tls)); + + CheckedReadFileAtEOF(readfd_); + ScopedTimeoutThread::ThreadMain(); + } + + int readfd_; + int writefd_; + + DISALLOW_COPY_AND_ASSIGN(BlockOnReadThread); +}; + +class SameBitnessTest : public Multiprocess { + public: + SameBitnessTest() : Multiprocess(), mapping_() {} + ~SameBitnessTest() {} + + protected: + void PreFork() override { + ASSERT_NO_FATAL_FAILURE(Multiprocess::PreFork()); + + size_t page_size = getpagesize(); + ASSERT_TRUE(mapping_.ResetMmap(nullptr, + page_size * 3, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); + ASSERT_TRUE(mapping_.ResetAddrLen(mapping_.addr(), page_size * 2)); + + auto buffer = mapping_.addr_as(); + for (size_t index = 0; index < mapping_.len(); ++index) { + buffer[index] = index % 256; + } + } + + private: + void MultiprocessParent() override { + LinuxVMAddress child1_tls; + ASSERT_TRUE(LoggingReadFileExactly( + ReadPipeHandle(), &child1_tls, sizeof(child1_tls))); + + pid_t child2_tid; + ASSERT_TRUE(LoggingReadFileExactly( + ReadPipeHandle(), &child2_tid, sizeof(child2_tid))); + + LinuxVMAddress child2_tls; + ASSERT_TRUE(LoggingReadFileExactly( + ReadPipeHandle(), &child2_tls, sizeof(child2_tls))); + + int socks[2]; + ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, socks), 0); + ScopedFileHandle broker_sock(socks[0]); + ScopedFileHandle client_sock(socks[1]); + +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif // ARCH_CPU_64_BITS + PtraceBroker broker(broker_sock.get(), am_64_bit); + RunBrokerThread broker_thread(&broker); + broker_thread.Start(); + + { + PtraceClient client; + ASSERT_TRUE(client.Initialize(client_sock.get(), ChildPID())); + + EXPECT_EQ(client.GetProcessID(), ChildPID()); + EXPECT_TRUE(client.Attach(child2_tid)); + EXPECT_EQ(client.Is64Bit(), am_64_bit); + + ThreadInfo info1; + ASSERT_TRUE(client.GetThreadInfo(ChildPID(), &info1)); + EXPECT_EQ(info1.thread_specific_data_address, child1_tls); + + ThreadInfo info2; + ASSERT_TRUE(client.GetThreadInfo(child2_tid, &info2)); + EXPECT_EQ(info2.thread_specific_data_address, child2_tls); + + auto buffer = std::make_unique(mapping_.len()); + ASSERT_TRUE(client.Read( + mapping_.addr_as(), mapping_.len(), buffer.get())); + auto expected_buffer = mapping_.addr_as(); + for (size_t index = 0; index < mapping_.len(); ++index) { + EXPECT_EQ(buffer[index], expected_buffer[index]); + } + + char first; + ASSERT_TRUE( + client.Read(mapping_.addr_as(), sizeof(first), &first)); + EXPECT_EQ(first, expected_buffer[0]); + + char last; + ASSERT_TRUE( + client.Read(mapping_.addr_as() + mapping_.len() - 1, + sizeof(last), + &last)); + EXPECT_EQ(last, expected_buffer[mapping_.len() - 1]); + + char unmapped; + EXPECT_FALSE(client.Read(mapping_.addr_as() + mapping_.len(), + sizeof(unmapped), + &unmapped)); + } + } + + void MultiprocessChild() override { + LinuxVMAddress tls = GetTLS(); + ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &tls, sizeof(tls))); + + BlockOnReadThread thread(ReadPipeHandle(), WritePipeHandle()); + thread.Start(); + + CheckedReadFileAtEOF(ReadPipeHandle()); + } + + ScopedMmap mapping_; + + DISALLOW_COPY_AND_ASSIGN(SameBitnessTest); +}; + +TEST(PtraceBroker, SameBitness) { + SameBitnessTest test; + test.Run(); +} + +// TODO(jperaza): Test against a process with different bitness. + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/util/linux/ptrace_client.cc b/util/linux/ptrace_client.cc new file mode 100644 index 00000000..dabda12e --- /dev/null +++ b/util/linux/ptrace_client.cc @@ -0,0 +1,178 @@ +// 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 "util/linux/ptrace_client.h" + +#include + +#include + +#include "base/logging.h" +#include "util/file/file_io.h" +#include "util/linux/ptrace_broker.h" + +namespace crashpad { + +namespace { + +bool ReceiveAndLogError(int sock, const std::string& operation) { + PtraceBroker::Errno error; + if (!LoggingReadFileExactly(sock, &error, sizeof(error))) { + return false; + } + errno = error; + PLOG(ERROR) << operation; + return true; +} + +bool AttachImpl(int sock, pid_t tid) { + PtraceBroker::Request request; + request.type = PtraceBroker::Request::kTypeAttach; + request.tid = tid; + if (!LoggingWriteFile(sock, &request, sizeof(request))) { + return false; + } + + PtraceBroker::Bool success; + if (!LoggingReadFileExactly(sock, &success, sizeof(success))) { + return false; + } + + if (success != PtraceBroker::kBoolTrue) { + ReceiveAndLogError(sock, "PtraceBroker Attach"); + } + + return true; +} + +} // namespace + +PtraceClient::PtraceClient() + : PtraceConnection(), + sock_(kInvalidFileHandle), + pid_(-1), + is_64_bit_(false), + initialized_() {} + +PtraceClient::~PtraceClient() { + if (sock_ != kInvalidFileHandle) { + PtraceBroker::Request request; + request.type = PtraceBroker::Request::kTypeExit; + LoggingWriteFile(sock_, &request, sizeof(request)); + } +} + +bool PtraceClient::Initialize(int sock, pid_t pid) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + sock_ = sock; + pid_ = pid; + + if (!AttachImpl(sock_, pid_)) { + return false; + } + + PtraceBroker::Request request; + request.type = PtraceBroker::Request::kTypeIs64Bit; + request.tid = pid_; + + if (!LoggingWriteFile(sock_, &request, sizeof(request))) { + return false; + } + + PtraceBroker::Bool is_64_bit; + if (!LoggingReadFileExactly(sock_, &is_64_bit, sizeof(is_64_bit))) { + return false; + } + is_64_bit_ = is_64_bit == PtraceBroker::kBoolTrue; + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +pid_t PtraceClient::GetProcessID() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return pid_; +} + +bool PtraceClient::Attach(pid_t tid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return AttachImpl(sock_, tid); +} + +bool PtraceClient::Is64Bit() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return is_64_bit_; +} + +bool PtraceClient::GetThreadInfo(pid_t tid, ThreadInfo* info) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + PtraceBroker::Request request; + request.type = PtraceBroker::Request::kTypeGetThreadInfo; + request.tid = tid; + if (!LoggingWriteFile(sock_, &request, sizeof(request))) { + return false; + } + + PtraceBroker::GetThreadInfoResponse response; + if (!LoggingReadFileExactly(sock_, &response, sizeof(response))) { + return false; + } + + if (response.success == PtraceBroker::kBoolTrue) { + *info = response.info; + return true; + } + + ReceiveAndLogError(sock_, "PtraceBroker GetThreadInfo"); + return false; +} + +bool PtraceClient::Read(VMAddress address, size_t size, void* buffer) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + char* buffer_c = reinterpret_cast(buffer); + + PtraceBroker::Request request; + request.type = PtraceBroker::Request::kTypeReadMemory; + request.tid = pid_; + request.iov.base = address; + request.iov.size = size; + + if (!LoggingWriteFile(sock_, &request, sizeof(request))) { + return false; + } + + while (size > 0) { + VMSize bytes_read; + if (!LoggingReadFileExactly(sock_, &bytes_read, sizeof(bytes_read))) { + return false; + } + + if (!bytes_read) { + ReceiveAndLogError(sock_, "PtraceBroker ReadMemory"); + return false; + } + + if (!LoggingReadFileExactly(sock_, buffer_c, bytes_read)) { + return false; + } + + size -= bytes_read; + buffer_c += bytes_read; + } + + return true; +} + +} // namespace crashpad diff --git a/util/linux/ptrace_client.h b/util/linux/ptrace_client.h new file mode 100644 index 00000000..397c891f --- /dev/null +++ b/util/linux/ptrace_client.h @@ -0,0 +1,85 @@ +// 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_UTIL_LINUX_PTRACE_CLIENT_H_ +#define CRASHPAD_UTIL_LINUX_PTRACE_CLIENT_H_ + +#include + +#include "base/macros.h" +#include "util/linux/ptrace_connection.h" +#include "util/misc/address_types.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +//! \brief Implements a PtraceConnection over a socket. +//! +//! This class forms the client half of the connection and is typically used +//! when the current process does not have `ptrace` capabilities on the target +//! process. It should be created with a socket connected to a PtraceBroker. +class PtraceClient : public PtraceConnection { + public: + PtraceClient(); + ~PtraceClient(); + + //! \brief Initializes this object. + //! + //! This method must be successfully called before any other method in this + //! class. + //! + //! \param[in] sock A socket connected to a PtraceBroker. Does not take + //! ownership of the socket. + //! \param[in] pid The process ID of the process to form a PtraceConnection + //! with. + //! \return `true` on success. `false` on failure with a message logged. + bool Initialize(int sock, pid_t pid); + + //! \brief Copies memory from the target process into a caller-provided buffer + //! in the current process. + //! + //! TODO(jperaza): In order for this to be usable, PtraceConnection will need + //! to surface it, possibly by inheriting from ProcessMemory, or providing a + //! method to return a ProcessMemory*. + //! + //! \param[in] address The address, in the target process' address space, of + //! the memory region to copy. + //! \param[in] size The size, in bytes, of the memory region to copy. + //! \a buffer must be at least this size. + //! \param[out] buffer The buffer into which the contents of the other + //! process' memory will be copied. + //! + //! \return `true` on success, with \a buffer filled appropriately. `false` on + //! failure, with a message logged. + bool Read(VMAddress address, size_t size, void* buffer); + + // PtraceConnection: + + pid_t GetProcessID() override; + bool Attach(pid_t tid) override; + bool Is64Bit() override; + bool GetThreadInfo(pid_t tid, ThreadInfo* info) override; + + private: + int sock_; + pid_t pid_; + bool is_64_bit_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(PtraceClient); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_LINUX_PTRACE_CLIENT_H_ diff --git a/util/linux/ptracer.cc b/util/linux/ptracer.cc index fcbaedc0..20115819 100644 --- a/util/linux/ptracer.cc +++ b/util/linux/ptracer.cc @@ -14,6 +14,7 @@ #include "util/linux/ptracer.h" +#include #include #include #include @@ -34,38 +35,43 @@ namespace { #if defined(ARCH_CPU_X86_FAMILY) template -bool GetRegisterSet(pid_t tid, int set, Destination* dest) { +bool GetRegisterSet(pid_t tid, int set, Destination* dest, bool can_log) { iovec iov; iov.iov_base = dest; iov.iov_len = sizeof(*dest); if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast(set), &iov) != 0) { - PLOG(ERROR) << "ptrace"; + PLOG_IF(ERROR, can_log) << "ptrace"; return false; } if (iov.iov_len != sizeof(*dest)) { - LOG(ERROR) << "Unexpected registers size"; + LOG_IF(ERROR, can_log) << "Unexpected registers size"; return false; } return true; } -bool GetFloatingPointRegisters32(pid_t tid, FloatContext* context) { - return GetRegisterSet(tid, NT_PRXFPREG, &context->f32.fxsave); +bool GetFloatingPointRegisters32(pid_t tid, + FloatContext* context, + bool can_log) { + return GetRegisterSet(tid, NT_PRXFPREG, &context->f32.fxsave, can_log); } -bool GetFloatingPointRegisters64(pid_t tid, FloatContext* context) { - return GetRegisterSet(tid, NT_PRFPREG, &context->f64.fxsave); +bool GetFloatingPointRegisters64(pid_t tid, + FloatContext* context, + bool can_log) { + return GetRegisterSet(tid, NT_PRFPREG, &context->f64.fxsave, can_log); } bool GetThreadArea32(pid_t tid, const ThreadContext& context, - LinuxVMAddress* address) { + LinuxVMAddress* address, + bool can_log) { size_t index = (context.t32.xgs & 0xffff) >> 3; user_desc desc; if (ptrace( PTRACE_GET_THREAD_AREA, tid, reinterpret_cast(index), &desc) != 0) { - PLOG(ERROR) << "ptrace"; + PLOG_IF(ERROR, can_log) << "ptrace"; return false; } @@ -75,7 +81,8 @@ bool GetThreadArea32(pid_t tid, bool GetThreadArea64(pid_t tid, const ThreadContext& context, - LinuxVMAddress* address) { + LinuxVMAddress* address, + bool can_log) { *address = context.t64.fs_base; return true; } @@ -98,17 +105,21 @@ bool GetThreadArea64(pid_t tid, // TODO(mark): Once helpers to interpret the kernel version are available, add // a DCHECK to ensure that the kernel is older than 3.5. -bool GetGeneralPurposeRegistersLegacy(pid_t tid, ThreadContext* context) { +bool GetGeneralPurposeRegistersLegacy(pid_t tid, + ThreadContext* context, + bool can_log) { if (ptrace(PTRACE_GETREGS, tid, nullptr, &context->t32) != 0) { - PLOG(ERROR) << "ptrace"; + PLOG_IF(ERROR, can_log) << "ptrace"; return false; } return true; } -bool GetFloatingPointRegistersLegacy(pid_t tid, FloatContext* context) { +bool GetFloatingPointRegistersLegacy(pid_t tid, + FloatContext* context, + bool can_log) { if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) { - PLOG(ERROR) << "ptrace"; + PLOG_IF(ERROR, can_log) << "ptrace"; return false; } context->f32.have_fpregs = true; @@ -119,7 +130,7 @@ bool GetFloatingPointRegistersLegacy(pid_t tid, FloatContext* context) { // These registers are optional on 32-bit ARM cpus break; default: - PLOG(ERROR) << "ptrace"; + PLOG_IF(ERROR, can_log) << "ptrace"; return false; } } else { @@ -138,7 +149,9 @@ bool GetFloatingPointRegistersLegacy(pid_t tid, FloatContext* context) { constexpr size_t kArmVfpSize = 32 * 8 + 4; // Target is 32-bit -bool GetFloatingPointRegisters32(pid_t tid, FloatContext* context) { +bool GetFloatingPointRegisters32(pid_t tid, + FloatContext* context, + bool can_log) { context->f32.have_fpregs = false; context->f32.have_vfp = false; @@ -151,19 +164,19 @@ bool GetFloatingPointRegisters32(pid_t tid, FloatContext* context) { switch (errno) { #if defined(ARCH_CPU_ARMEL) case EIO: - return GetFloatingPointRegistersLegacy(tid, context); + return GetFloatingPointRegistersLegacy(tid, context, can_log); #endif // ARCH_CPU_ARMEL case EINVAL: // A 32-bit process running on a 64-bit CPU doesn't have this register // set. It should have a VFP register set instead. break; default: - PLOG(ERROR) << "ptrace"; + PLOG_IF(ERROR, can_log) << "ptrace"; return false; } } else { if (iov.iov_len != sizeof(context->f32.fpregs)) { - LOG(ERROR) << "Unexpected registers size"; + LOG_IF(ERROR, can_log) << "Unexpected registers size"; return false; } context->f32.have_fpregs = true; @@ -179,36 +192,38 @@ bool GetFloatingPointRegisters32(pid_t tid, FloatContext* context) { // VFP may not be present on 32-bit ARM cpus. break; default: - PLOG(ERROR) << "ptrace"; + PLOG_IF(ERROR, can_log) << "ptrace"; return false; } } else { if (iov.iov_len != kArmVfpSize && iov.iov_len != sizeof(context->f32.vfp)) { - LOG(ERROR) << "Unexpected registers size"; + LOG_IF(ERROR, can_log) << "Unexpected registers size"; return false; } context->f32.have_vfp = true; } if (!(context->f32.have_fpregs || context->f32.have_vfp)) { - LOG(ERROR) << "Unable to collect registers"; + LOG_IF(ERROR, can_log) << "Unable to collect registers"; return false; } return true; } -bool GetFloatingPointRegisters64(pid_t tid, FloatContext* context) { +bool GetFloatingPointRegisters64(pid_t tid, + FloatContext* context, + bool can_log) { iovec iov; iov.iov_base = context; iov.iov_len = sizeof(*context); if (ptrace( PTRACE_GETREGSET, tid, reinterpret_cast(NT_PRFPREG), &iov) != 0) { - PLOG(ERROR) << "ptrace"; + PLOG_IF(ERROR, can_log) << "ptrace"; return false; } if (iov.iov_len != sizeof(context->f64)) { - LOG(ERROR) << "Unexpected registers size"; + LOG_IF(ERROR, can_log) << "Unexpected registers size"; return false; } return true; @@ -216,11 +231,12 @@ bool GetFloatingPointRegisters64(pid_t tid, FloatContext* context) { bool GetThreadArea32(pid_t tid, const ThreadContext& context, - LinuxVMAddress* address) { + LinuxVMAddress* address, + bool can_log) { #if defined(ARCH_CPU_ARMEL) void* result; if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &result) != 0) { - PLOG(ERROR) << "ptrace"; + PLOG_IF(ERROR, can_log) << "ptrace"; return false; } *address = FromPointerCast(result); @@ -228,25 +244,27 @@ bool GetThreadArea32(pid_t tid, #else // TODO(jperaza): it doesn't look like there is a way for a 64-bit ARM process // to get the thread area for a 32-bit ARM process with ptrace. - LOG(WARNING) << "64-bit ARM cannot trace TLS area for a 32-bit process"; + LOG_IF(WARNING, can_log) + << "64-bit ARM cannot trace TLS area for a 32-bit process"; return false; #endif // ARCH_CPU_ARMEL } bool GetThreadArea64(pid_t tid, const ThreadContext& context, - LinuxVMAddress* address) { + LinuxVMAddress* address, + bool can_log) { iovec iov; iov.iov_base = address; iov.iov_len = sizeof(*address); if (ptrace( PTRACE_GETREGSET, tid, reinterpret_cast(NT_ARM_TLS), &iov) != 0) { - PLOG(ERROR) << "ptrace"; + PLOG_IF(ERROR, can_log) << "ptrace"; return false; } if (iov.iov_len != 8) { - LOG(ERROR) << "address size mismatch"; + LOG_IF(ERROR, can_log) << "address size mismatch"; return false; } return true; @@ -255,7 +273,9 @@ bool GetThreadArea64(pid_t tid, #error Port. #endif // ARCH_CPU_X86_FAMILY -size_t GetGeneralPurposeRegistersAndLength(pid_t tid, ThreadContext* context) { +size_t GetGeneralPurposeRegistersAndLength(pid_t tid, + ThreadContext* context, + bool can_log) { iovec iov; iov.iov_base = context; iov.iov_len = sizeof(*context); @@ -265,31 +285,35 @@ size_t GetGeneralPurposeRegistersAndLength(pid_t tid, ThreadContext* context) { switch (errno) { #if defined(ARCH_CPU_ARMEL) case EIO: - if (GetGeneralPurposeRegistersLegacy(tid, context)) { + if (GetGeneralPurposeRegistersLegacy(tid, context, can_log)) { return sizeof(context->t32); } #endif // ARCH_CPU_ARMEL default: - PLOG(ERROR) << "ptrace"; + PLOG_IF(ERROR, can_log) << "ptrace"; return 0; } } return iov.iov_len; } -bool GetGeneralPurposeRegisters32(pid_t tid, ThreadContext* context) { - if (GetGeneralPurposeRegistersAndLength(tid, context) != +bool GetGeneralPurposeRegisters32(pid_t tid, + ThreadContext* context, + bool can_log) { + if (GetGeneralPurposeRegistersAndLength(tid, context, can_log) != sizeof(context->t32)) { - LOG(ERROR) << "Unexpected registers size"; + LOG_IF(ERROR, can_log) << "Unexpected registers size"; return false; } return true; } -bool GetGeneralPurposeRegisters64(pid_t tid, ThreadContext* context) { - if (GetGeneralPurposeRegistersAndLength(tid, context) != +bool GetGeneralPurposeRegisters64(pid_t tid, + ThreadContext* context, + bool can_log) { + if (GetGeneralPurposeRegistersAndLength(tid, context, can_log) != sizeof(context->t64)) { - LOG(ERROR) << "Unexpected registers size"; + LOG_IF(ERROR, can_log) << "Unexpected registers size"; return false; } return true; @@ -297,9 +321,11 @@ bool GetGeneralPurposeRegisters64(pid_t tid, ThreadContext* context) { } // namespace -Ptracer::Ptracer() : is_64_bit_(false), initialized_() {} +Ptracer::Ptracer(bool can_log) + : is_64_bit_(false), can_log_(can_log), initialized_() {} -Ptracer::Ptracer(bool is_64_bit) : is_64_bit_(is_64_bit) { +Ptracer::Ptracer(bool is_64_bit, bool can_log) + : is_64_bit_(is_64_bit), can_log_(can_log), initialized_() { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); INITIALIZATION_STATE_SET_VALID(initialized_); } @@ -310,13 +336,13 @@ bool Ptracer::Initialize(pid_t pid) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); ThreadContext context; - size_t length = GetGeneralPurposeRegistersAndLength(pid, &context); + size_t length = GetGeneralPurposeRegistersAndLength(pid, &context, can_log_); if (length == sizeof(context.t64)) { is_64_bit_ = true; } else if (length == sizeof(context.t32)) { is_64_bit_ = false; } else { - LOG(ERROR) << "Unexpected registers size"; + LOG_IF(ERROR, can_log_) << "Unexpected registers size"; return false; } @@ -333,16 +359,73 @@ bool Ptracer::GetThreadInfo(pid_t tid, ThreadInfo* info) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); if (is_64_bit_) { - return GetGeneralPurposeRegisters64(tid, &info->thread_context) && - GetFloatingPointRegisters64(tid, &info->float_context) && - GetThreadArea64( - tid, info->thread_context, &info->thread_specific_data_address); + return GetGeneralPurposeRegisters64(tid, &info->thread_context, can_log_) && + GetFloatingPointRegisters64(tid, &info->float_context, can_log_) && + GetThreadArea64(tid, + info->thread_context, + &info->thread_specific_data_address, + can_log_); } - return GetGeneralPurposeRegisters32(tid, &info->thread_context) && - GetFloatingPointRegisters32(tid, &info->float_context) && - GetThreadArea32( - tid, info->thread_context, &info->thread_specific_data_address); + return GetGeneralPurposeRegisters32(tid, &info->thread_context, can_log_) && + GetFloatingPointRegisters32(tid, &info->float_context, can_log_) && + GetThreadArea32(tid, + info->thread_context, + &info->thread_specific_data_address, + can_log_); +} + +bool Ptracer::ReadMemory(pid_t pid, + LinuxVMAddress address, + size_t size, + char* buffer) { + while (size > 0) { + errno = 0; + + if (size >= sizeof(long)) { + *reinterpret_cast(buffer) = + ptrace(PTRACE_PEEKDATA, pid, address, nullptr); + + if (errno != 0) { + PLOG_IF(ERROR, can_log_) << "ptrace"; + return false; + } + + size -= sizeof(long); + buffer += sizeof(long); + address += sizeof(long); + } else { + long word = ptrace(PTRACE_PEEKDATA, pid, address, nullptr); + + if (errno == 0) { + memcpy(buffer, reinterpret_cast(&word), size); + return true; + } + + if (errno != EIO) { + PLOG_IF(ERROR, can_log_); + return false; + } + + // A read smaller than a word at the end of a mapping might spill over + // into unmapped memory. Try aligning the read so that the requested + // data is at the end of the word instead. + errno = 0; + word = + ptrace(PTRACE_PEEKDATA, pid, address - sizeof(word) + size, nullptr); + + if (errno == 0) { + memcpy( + buffer, reinterpret_cast(&word) + sizeof(word) - size, size); + return true; + } + + PLOG_IF(ERROR, can_log_); + return false; + } + } + + return true; } } // namespace crashpad diff --git a/util/linux/ptracer.h b/util/linux/ptracer.h index e47f6577..daa7a01f 100644 --- a/util/linux/ptracer.h +++ b/util/linux/ptracer.h @@ -35,13 +35,16 @@ class Ptracer { //! \brief Constructs this object with a pre-determined bitness. //! //! \param[in] is_64_bit `true` if this object is to be configured for 64-bit. - explicit Ptracer(bool is_64_bit); + //! \param[in] can_log Whether methods in this class can log error messages. + Ptracer(bool is_64_bit, bool can_log); //! \brief Constructs this object without a pre-determined bitness. //! //! Initialize() must be successfully called before making any other calls on //! this object. - Ptracer(); + //! + //! \param[in] can_log Whether methods in this class can log error messages. + explicit Ptracer(bool can_log); ~Ptracer(); @@ -49,7 +52,8 @@ class Ptracer { //! ID is \a pid. //! //! \param[in] pid The process ID of the process to initialize with. - //! \return `true` on success. `false` on failure with a message logged. + //! \return `true` on success. `false` on failure with a message logged, if + //! enabled. bool Initialize(pid_t pid); //! \brief Return `true` if this object is configured for 64-bit. @@ -63,11 +67,27 @@ class Ptracer { //! //! \param[in] tid The thread ID of the thread to collect information for. //! \param[out] info A ThreadInfo for the thread. - //! \return `true` on success. `false` on failure with a message logged. + //! \return `true` on success. `false` on failure with a message logged, if + //! enabled. bool GetThreadInfo(pid_t tid, ThreadInfo* info); + //! \brief Uses `ptrace` to read memory from the process with process ID \a + //! pid. + //! + //! The target process should already be attached before calling this method. + //! \see ScopedPtraceAttach + //! + //! \param[in] pid The process ID whose memory to read. + //! \param[in] address The base address of the region to read. + //! \param[in] size The size of the memory region to read. + //! \param[out] buffer The buffer to fill with the data read. + //! \return `true` on success. `false` on failure with a message logged, if + //! enabled. + bool ReadMemory(pid_t pid, LinuxVMAddress address, size_t size, char* buffer); + private: bool is_64_bit_; + bool can_log_; InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(Ptracer); diff --git a/util/linux/ptracer_test.cc b/util/linux/ptracer_test.cc index b32e258a..9a759850 100644 --- a/util/linux/ptracer_test.cc +++ b/util/linux/ptracer_test.cc @@ -16,10 +16,10 @@ #include "build/build_config.h" #include "gtest/gtest.h" +#include "test/linux/get_tls.h" #include "test/multiprocess.h" #include "util/file/file_io.h" #include "util/linux/scoped_ptrace_attach.h" -#include "util/misc/from_pointer_cast.h" namespace crashpad { namespace test { @@ -45,7 +45,7 @@ class SameBitnessTest : public Multiprocess { ScopedPtraceAttach attach; ASSERT_TRUE(attach.ResetAttach(ChildPID())); - Ptracer ptracer(am_64_bit); + Ptracer ptracer(am_64_bit, /* can_log= */ true); EXPECT_EQ(ptracer.Is64Bit(), am_64_bit); @@ -60,24 +60,7 @@ class SameBitnessTest : public Multiprocess { } void MultiprocessChild() override { - LinuxVMAddress expected_tls; -#if defined(ARCH_CPU_ARMEL) - // 0xffff0fe0 is the address of the kernel user helper __kuser_get_tls(). - auto kuser_get_tls = reinterpret_cast(0xffff0fe0); - expected_tls = FromPointerCast(kuser_get_tls()); -#elif defined(ARCH_CPU_ARM64) - // Linux/aarch64 places the tls address in system register tpidr_el0. - asm("mrs %0, tpidr_el0" : "=r"(expected_tls)); -#elif defined(ARCH_CPU_X86) - uint32_t expected_tls_32; - asm("movl %%gs:0x0, %0" : "=r"(expected_tls_32)); - expected_tls = expected_tls_32; -#elif defined(ARCH_CPU_X86_64) - asm("movq %%fs:0x0, %0" : "=r"(expected_tls)); -#else -#error Port. -#endif // ARCH_CPU_ARMEL - + LinuxVMAddress expected_tls = GetTLS(); CheckedWriteFile(WritePipeHandle(), &expected_tls, sizeof(expected_tls)); CheckedReadFileAtEOF(ReadPipeHandle()); diff --git a/util/util.gyp b/util/util.gyp index 5aeb4503..a7bed771 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -62,6 +62,10 @@ 'linux/memory_map.h', 'linux/proc_stat_reader.cc', 'linux/proc_stat_reader.h', + 'linux/ptrace_broker.cc', + 'linux/ptrace_broker.h', + 'linux/ptrace_client.cc', + 'linux/ptrace_client.h' 'linux/ptrace_connection.h', 'linux/ptracer.cc', 'linux/ptracer.h', diff --git a/util/util_test.gyp b/util/util_test.gyp index f9c114db..a2873270 100644 --- a/util/util_test.gyp +++ b/util/util_test.gyp @@ -44,6 +44,7 @@ 'linux/auxiliary_vector_test.cc', 'linux/memory_map_test.cc', 'linux/proc_stat_reader_test.cc', + 'linux/ptrace_broker_test.cc', 'linux/ptracer_test.cc', 'linux/scoped_ptrace_attach_test.cc', 'mac/launchd_test.mm', From 05dee13e844da54266b9fb4ba2bff90befeb997d Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 13 Dec 2017 15:10:42 -0800 Subject: [PATCH 067/326] fuchsia: Add QEMU and helper script to start/stop instance This is mostly intended to be used for waterfall/trybots. Fuchsia-on-metal isn't available as a Swarming dimension, so in order to run tests, use QEMU-with-KVM on the host. It might also be useful for local development for those without a Fuchsia hardware device. Bug: crashpad:196, crashpad:212 Change-Id: I88170bc95bd532676b787b50a94f7fa3c69b1ac7 Reviewed-on: https://chromium-review.googlesource.com/822523 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- .gitignore | 1 + DEPS | 30 ++++++++++ build/run_fuchsia_qemu.py | 119 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+) create mode 100755 build/run_fuchsia_qemu.py diff --git a/.gitignore b/.gitignore index 41806962..e6daaab9 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ /out /third_party/fuchsia/.cipd /third_party/fuchsia/clang +/third_party/fuchsia/qemu /third_party/fuchsia/sdk /third_party/gtest/gtest /third_party/gyp/gyp diff --git a/DEPS b/DEPS index 79fb6434..6aa3306f 100644 --- a/DEPS +++ b/DEPS @@ -145,6 +145,36 @@ hooks = [ '-log-level', 'info', ], }, + { + # Same rationale for using "install" rather than "ensure" as for clang + # packages. https://crbug.com/789364. + 'name': 'fuchsia_qemu_mac', + 'pattern': '.', + 'condition': 'checkout_fuchsia and host_os == "mac"', + 'action': [ + 'cipd', + 'install', + 'fuchsia/qemu/mac-amd64', + 'latest', + '-root', 'crashpad/third_party/fuchsia/qemu/mac-amd64', + '-log-level', 'info', + ], + }, + { + # Same rationale for using "install" rather than "ensure" as for clang + # packages. https://crbug.com/789364. + 'name': 'fuchsia_qemu_linux', + 'pattern': '.', + 'condition': 'checkout_fuchsia and host_os == "linux"', + 'action': [ + 'cipd', + 'install', + 'fuchsia/qemu/linux-amd64', + 'latest', + '-root', 'crashpad/third_party/fuchsia/qemu/linux-amd64', + '-log-level', 'info', + ], + }, { # The SDK is keyed to the host system because it contains build tools. # Currently, linux-amd64 is the only SDK published (see diff --git a/build/run_fuchsia_qemu.py b/build/run_fuchsia_qemu.py new file mode 100755 index 00000000..e3f4efc3 --- /dev/null +++ b/build/run_fuchsia_qemu.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python + +# 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. + +"""Helper script to [re]start or stop a helper Fuchsia QEMU instance to be used +for running tests without a device. +""" + +from __future__ import print_function + +import getpass +import os +import random +import signal +import subprocess +import sys +import tempfile + +try: + from subprocess import DEVNULL +except ImportError: + DEVNULL = open(os.devnull, 'r+b') + +CRASHPAD_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), + os.pardir) + + +def _Stop(pid_file): + if os.path.isfile(pid_file): + with open(pid_file, 'rb') as f: + pid = int(f.read().strip()) + try: + os.kill(pid, signal.SIGTERM) + except: + print('Unable to kill pid %d, continuing' % pid, file=sys.stderr) + os.unlink(pid_file) + + +def _CheckForTun(): + """Check for networking. TODO(scottmg): Currently, this is Linux-specific. + """ + returncode = subprocess.call( + ['tunctl', '-b', '-u', getpass.getuser(), '-t', 'qemu'], + stdout=DEVNULL, stderr=DEVNULL) + if returncode != 0: + print('To use QEMU with networking on Linux, configure TUN/TAP. See:', + file=sys.stderr) + print(' https://fuchsia.googlesource.com/magenta/+/HEAD/docs/qemu.md#enabling-networking-under-qemu-x86_64-only', + file=sys.stderr) + return 2 + return 0 + + +def _Start(pid_file): + tun_result = _CheckForTun() + if tun_result != 0: + return tun_result + + arch = 'mac-amd64' if sys.platform == 'darwin' else 'linux-amd64' + fuchsia_dir = os.path.join(CRASHPAD_ROOT, 'third_party', 'fuchsia') + qemu_path = os.path.join(fuchsia_dir, 'qemu', arch, 'bin', + 'qemu-system-x86_64') + kernel_data_dir = os.path.join(fuchsia_dir, 'sdk', arch, 'target', 'x86_64') + kernel_path = os.path.join(kernel_data_dir, 'zircon.bin') + initrd_path = os.path.join(kernel_data_dir, 'bootdata.bin') + + mac_tail = ':'.join('%02x' % random.randint(0, 255) for x in range(3)) + + # These arguments are from the Fuchsia repo in zircon/scripts/run-zircon. + popen = subprocess.Popen([ + qemu_path, + '-m', '2048', + '-nographic', + '-kernel', kernel_path, + '-initrd', initrd_path, + '-smp', '4', + '-serial', 'stdio', + '-monitor', 'none', + '-machine', 'q35', + '-cpu', 'host,migratable=no', + '-enable-kvm', + '-netdev', 'type=tap,ifname=qemu,script=no,downscript=no,id=net0', + '-device', 'e1000,netdev=net0,mac=52:54:00:' + mac_tail, + '-append', 'TERM=dumb', + ], stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL) + + with open(pid_file, 'wb') as f: + f.write('%d\n' % popen.pid) + + +def main(args): + if len(args) != 1 or args[0] not in ('start', 'stop'): + print('usage: run_fuchsia_qemu.py start|stop', file=sys.stderr) + return 1 + + command = args[0] + + pid_file = os.path.join(tempfile.gettempdir(), 'crashpad_fuchsia_qemu_pid') + _Stop(pid_file) + if command == 'start': + _Start(pid_file) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) From 169731495268b960b0222466862f3a339314b8a2 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 13 Dec 2017 15:13:39 -0800 Subject: [PATCH 068/326] fuchsia: Make Filesystem.RemoveFile, .RemoveDirectory pass - Remove unnecessary flags (O_NOCTTY, O_CLOEXEC) - Don't try to unlink a directory when it's expected to fail - Disable rmdir() in location where it's expected to fail, as it currently (incorrectly) does not fail on Fuchsia. Bug: crashpad:196, US-400 Change-Id: I80cf833ba90f31943b9043727ea07893b4eb3494 Reviewed-on: https://chromium-review.googlesource.com/823286 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- util/file/file_io_posix.cc | 5 +++++ util/file/filesystem_test.cc | 10 ++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/util/file/file_io_posix.cc b/util/file/file_io_posix.cc index b2afdc0d..ec5e5abe 100644 --- a/util/file/file_io_posix.cc +++ b/util/file/file_io_posix.cc @@ -66,7 +66,12 @@ FileHandle OpenFileForOutput(int rdwr_or_wronly, const base::FilePath& path, FileWriteMode mode, FilePermissions permissions) { +#if defined(OS_FUCHSIA) + // O_NOCTTY is invalid on Fuchsia, and O_CLOEXEC isn't necessary. + int flags = 0; +#else int flags = O_NOCTTY | O_CLOEXEC; +#endif DCHECK(rdwr_or_wronly & (O_RDWR | O_WRONLY)); DCHECK_EQ(rdwr_or_wronly & ~(O_RDWR | O_WRONLY), 0); diff --git a/util/file/filesystem_test.cc b/util/file/filesystem_test.cc index 28d2d88a..3430c3f9 100644 --- a/util/file/filesystem_test.cc +++ b/util/file/filesystem_test.cc @@ -373,7 +373,6 @@ TEST(Filesystem, RemoveFile) { EXPECT_FALSE(LoggingRemoveFile(base::FilePath())); ScopedTempDir temp_dir; - EXPECT_FALSE(LoggingRemoveFile(temp_dir.path())); base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file"))); EXPECT_FALSE(LoggingRemoveFile(file)); @@ -384,7 +383,6 @@ TEST(Filesystem, RemoveFile) { base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir"))); ASSERT_TRUE( LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false)); - EXPECT_FALSE(LoggingRemoveFile(dir)); EXPECT_TRUE(LoggingRemoveFile(file)); EXPECT_FALSE(PathExists(file)); @@ -433,8 +431,16 @@ TEST(Filesystem, RemoveDirectory) { EXPECT_FALSE(LoggingRemoveDirectory(file)); ASSERT_TRUE(CreateFile(file)); +#if !defined(OS_FUCHSIA) + // The current implementation of Fuchsia's rmdir() simply calls unlink(), and + // unlink() works on all FS objects. This is incorrect as + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html says + // "The directory shall be removed only if it is an empty directory." and "If + // the directory is not an empty directory, rmdir() shall fail and set errno + // to [EEXIST] or [ENOTEMPTY]." Upstream bug: US-400. EXPECT_FALSE(LoggingRemoveDirectory(file)); EXPECT_FALSE(LoggingRemoveDirectory(dir)); +#endif ASSERT_TRUE(LoggingRemoveFile(file)); EXPECT_TRUE(LoggingRemoveDirectory(dir)); From 7662fb087f6c9d172a31e277a2dff465311f2d74 Mon Sep 17 00:00:00 2001 From: Ilya Sherman Date: Thu, 14 Dec 2017 12:39:48 -0800 Subject: [PATCH 069/326] Roll mini_chromium a12ed4a6..20182dd2 $ git log --oneline a12ed4a6..20182dd2 20182dd Add missing newline at EOF after d1943e187f47 d1943e1 Add a stub for Chromium's base::UmaHistogramSparse(). 9920849 gn, mac: Various GN build fixes 0b16698 gn, mac: Tell libtool not to warn about empty .o files R=mark@chromium.org Bug: chromium:773850 Change-Id: I2fb4b7ed8a8efa8b3d37f1b8f131396e9a2bbfdc Reviewed-on: https://chromium-review.googlesource.com/827648 Reviewed-by: Mark Mentovai --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 6aa3306f..c59ff430 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'a12ed4a613a7df900059c8a7885acdad550a5319', + '20182dd263312db9fad52042fc92c33331ec6904', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', From 344acadfdd3670a499f118005a5f3a8a056f431a Mon Sep 17 00:00:00 2001 From: Ilya Sherman Date: Fri, 15 Dec 2017 00:49:39 -0800 Subject: [PATCH 070/326] [Cleanup] Rename UMA_HISTOGRAM_SPARSE_SLOWLY to base::UmaHistogramSparse(). R=mark@chromium.org Bug: chromium:773850 Change-Id: Idef7b4c821dcac03e095d1400534ddf503a22423 Reviewed-on: https://chromium-review.googlesource.com/828530 Reviewed-by: Mark Mentovai --- util/misc/metrics.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util/misc/metrics.cc b/util/misc/metrics.cc index 30de6b90..f4fb5882 100644 --- a/util/misc/metrics.cc +++ b/util/misc/metrics.cc @@ -14,8 +14,8 @@ #include "util/misc/metrics.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" -#include "base/metrics/sparse_histogram.h" #include "build/build_config.h" #if defined(OS_MACOSX) @@ -91,8 +91,8 @@ void Metrics::ExceptionCaptureResult(CaptureResult result) { // static void Metrics::ExceptionCode(uint32_t exception_code) { - UMA_HISTOGRAM_SPARSE_SLOWLY("Crashpad.ExceptionCode." METRICS_OS_NAME, - static_cast(exception_code)); + base::UmaHistogramSparse("Crashpad.ExceptionCode." METRICS_OS_NAME, + static_cast(exception_code)); } // static @@ -109,7 +109,7 @@ void Metrics::HandlerLifetimeMilestone(LifetimeMilestone milestone) { // static void Metrics::HandlerCrashed(uint32_t exception_code) { - UMA_HISTOGRAM_SPARSE_SLOWLY( + base::UmaHistogramSparse( "Crashpad.HandlerCrash.ExceptionCode." METRICS_OS_NAME, static_cast(exception_code)); } From 10ff56eee5da29d14b86818b88a93d3e96b4bacd Mon Sep 17 00:00:00 2001 From: Ilya Sherman Date: Fri, 15 Dec 2017 11:42:00 -0800 Subject: [PATCH 071/326] Include the appropriate header for BUILD_FUSCHIA Without this, attempting to roll crashpad in Chromium gives this presubmit warning: third_party/crashpad/crashpad/util/file/file_io_posix.cc:69 OS_FUCHSIA macro is used without including build/build_config.h. R=mark@chromium.org Bug: none Change-Id: Ie2d1df574773b66687948a481b9b31012427a3c3 Reviewed-on: https://chromium-review.googlesource.com/830258 Commit-Queue: Ilya Sherman Reviewed-by: Mark Mentovai --- util/file/file_io_posix.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/util/file/file_io_posix.cc b/util/file/file_io_posix.cc index ec5e5abe..2993279d 100644 --- a/util/file/file_io_posix.cc +++ b/util/file/file_io_posix.cc @@ -25,6 +25,7 @@ #include "base/files/file_path.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" namespace crashpad { From 8742051c609907620384377e88c369cb914be3d0 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Fri, 15 Dec 2017 00:57:37 -0500 Subject: [PATCH 072/326] mac: Drop apple_cctools and getsectiondata()/getsegmentdata() wrappers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are present on 10.7 and later, and were only provided for the benefit of older systems that probably aren’t relevant to Crashpad any longer. Change-Id: If9d7222f7af05020d0ff57d5d9ed06355fa14a48 Reviewed-on: https://chromium-review.googlesource.com/827686 Commit-Queue: Mark Mentovai Reviewed-by: Scott Graham --- compat/BUILD.gn | 21 +- compat/compat.gyp | 5 - compat/mac/mach-o/getsect.cc | 107 ----- compat/mac/mach-o/getsect.h | 69 ---- third_party/apple_cctools/BUILD.gn | 25 -- third_party/apple_cctools/README.crashpad | 44 --- third_party/apple_cctools/apple_cctools.gyp | 34 -- .../apple_cctools/cctools/APPLE_LICENSE | 367 ------------------ .../cctools/include/mach-o/getsect.h | 76 ---- .../cctools/libmacho/getsecbyname.c | 133 ------- third_party/getopt/BUILD.gn | 2 +- 11 files changed, 16 insertions(+), 867 deletions(-) delete mode 100644 compat/mac/mach-o/getsect.cc delete mode 100644 compat/mac/mach-o/getsect.h delete mode 100644 third_party/apple_cctools/BUILD.gn delete mode 100644 third_party/apple_cctools/README.crashpad delete mode 100644 third_party/apple_cctools/apple_cctools.gyp delete mode 100644 third_party/apple_cctools/cctools/APPLE_LICENSE delete mode 100644 third_party/apple_cctools/cctools/include/mach-o/getsect.h delete mode 100644 third_party/apple_cctools/cctools/libmacho/getsecbyname.c diff --git a/compat/BUILD.gn b/compat/BUILD.gn index a03653cf..af845130 100644 --- a/compat/BUILD.gn +++ b/compat/BUILD.gn @@ -26,15 +26,27 @@ config("compat_config") { } } -static_library("compat") { +template("compat_target") { + if (is_mac) { + # There are no sources to compile, which doesn’t mix will with a + # static_library. + group(target_name) { + forward_variables_from(invoker, "*") + } + } else { + static_library(target_name) { + forward_variables_from(invoker, "*") + } + } +} + +compat_target("compat") { sources = [] if (is_mac) { sources += [ "mac/AvailabilityMacros.h", "mac/kern/exc_resource.h", - "mac/mach-o/getsect.cc", - "mac/mach-o/getsect.h", "mac/mach-o/loader.h", "mac/mach/mach.h", "mac/sys/resource.h", @@ -73,9 +85,6 @@ static_library("compat") { deps = [] - if (is_mac) { - deps += [ "../third_party/apple_cctools" ] - } if (is_win) { deps += [ "../third_party/getopt" ] } diff --git a/compat/compat.gyp b/compat/compat.gyp index b0aec0af..6bc54848 100644 --- a/compat/compat.gyp +++ b/compat/compat.gyp @@ -36,8 +36,6 @@ 'mac/kern/exc_resource.h', 'mac/mach/i386/thread_state.h', 'mac/mach/mach.h', - 'mac/mach-o/getsect.cc', - 'mac/mach-o/getsect.h', 'mac/mach-o/loader.h', 'mac/sys/resource.h', 'non_mac/mach/mach.h', @@ -60,9 +58,6 @@ ], 'conditions': [ ['OS=="mac"', { - 'dependencies': [ - '../third_party/apple_cctools/apple_cctools.gyp:apple_cctools', - ], 'include_dirs': [ 'mac', ], diff --git a/compat/mac/mach-o/getsect.cc b/compat/mac/mach-o/getsect.cc deleted file mode 100644 index 6cda9ffe..00000000 --- a/compat/mac/mach-o/getsect.cc +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2014 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 - -// This is only necessary when building code that might run on systems earlier -// than 10.7. When building for 10.7 or later, getsectiondata() and -// getsegmentdata() are always present in libmacho and made available through -// libSystem. When building for earlier systems, custom definitions of -// these functions are needed. -// -// This file checks the deployment target instead of the SDK. The deployment -// target is correct because it identifies the earliest possible system that -// the code being compiled is expected to run on. - -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 - -#include -#include - -#include "third_party/apple_cctools/cctools/include/mach-o/getsect.h" - -namespace { - -// Returns a dlopen() handle to the same library that provides the -// getsectbyname() function. getsectbyname() is always present in libmacho. -// getsectiondata() and getsegmentdata() are not always present, but when they -// are, they’re in the same library as getsectbyname(). If the library cannot -// be found or a handle to it cannot be returned, returns nullptr. -void* SystemLibMachOHandle() { - Dl_info info; - if (!dladdr(reinterpret_cast(getsectbyname), &info)) { - return nullptr; - } - return dlopen(info.dli_fname, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); -} - -// Returns a function pointer to a function in libmacho based on a lookup of -// that function by symbol name. Returns nullptr if libmacho cannot be found or -// opened, or if the named symbol cannot be found in libmacho. -void* LookUpSystemLibMachOSymbol(const char* symbol) { - static void* dl_handle = SystemLibMachOHandle(); - if (!dl_handle) { - return nullptr; - } - return dlsym(dl_handle, symbol); -} - -#ifndef __LP64__ -using MachHeader = mach_header; -#else -using MachHeader = mach_header_64; -#endif - -using GetSectionDataType = - uint8_t*(*)(const MachHeader*, const char*, const char*, unsigned long*); -using GetSegmentDataType = - uint8_t*(*)(const MachHeader*, const char*, unsigned long*); - -} // namespace - -extern "C" { - -// These implementations look up their functions in libmacho at run time. If the -// system libmacho provides these functions as it normally does on OS X 10.7 and -// later, the system’s versions are used directly. Otherwise, the versions in -// third_party/apple_cctools are used, which are actually just copies of the -// system’s functions. - -uint8_t* getsectiondata(const MachHeader* mhp, - const char* segname, - const char* sectname, - unsigned long* size) { - static GetSectionDataType system_getsectiondata = - reinterpret_cast( - LookUpSystemLibMachOSymbol("getsectiondata")); - if (system_getsectiondata) { - return system_getsectiondata(mhp, segname, sectname, size); - } - return crashpad_getsectiondata(mhp, segname, sectname, size); -} - -uint8_t* getsegmentdata( - const MachHeader* mhp, const char* segname, unsigned long* size) { - static GetSegmentDataType system_getsegmentdata = - reinterpret_cast( - LookUpSystemLibMachOSymbol("getsegmentdata")); - if (system_getsegmentdata) { - return system_getsegmentdata(mhp, segname, size); - } - return crashpad_getsegmentdata(mhp, segname, size); -} - -} // extern "C" - -#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 diff --git a/compat/mac/mach-o/getsect.h b/compat/mac/mach-o/getsect.h deleted file mode 100644 index c9169e16..00000000 --- a/compat/mac/mach-o/getsect.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2014 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_COMPAT_MAC_MACH_O_GETSECT_H_ -#define CRASHPAD_COMPAT_MAC_MACH_O_GETSECT_H_ - -#include_next - -#include - -// This file checks the SDK instead of the deployment target. The SDK is correct -// because this file is concerned with providing compile-time declarations, -// which are either present in a specific SDK version or not. - -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// Don’t use a type alias to account for the mach_header/mach_header_64 -// difference between the 32-bit and 64-bit versions of getsectiondata() and -// getsegmentdata(). This file should be faithfully equivalent to the native -// SDK, and adding type aliases here would pollute the namespace in a way that -// the native SDK does not. - -#if !defined(__LP64__) - -uint8_t* getsectiondata(const struct mach_header* mhp, - const char* segname, - const char* sectname, - unsigned long* size); - -uint8_t* getsegmentdata( - const struct mach_header* mhp, const char* segname, unsigned long* size); - -#else - -uint8_t* getsectiondata(const struct mach_header_64* mhp, - const char* segname, - const char* sectname, - unsigned long* size); - -uint8_t* getsegmentdata( - const struct mach_header_64* mhp, const char* segname, unsigned long* size); - -#endif - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 - -#endif // CRASHPAD_COMPAT_MAC_MACH_O_GETSECT_H_ diff --git a/third_party/apple_cctools/BUILD.gn b/third_party/apple_cctools/BUILD.gn deleted file mode 100644 index c6277281..00000000 --- a/third_party/apple_cctools/BUILD.gn +++ /dev/null @@ -1,25 +0,0 @@ -# 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. - -config("apple_cctools_config") { - include_dirs = [ "../.." ] -} - -source_set("apple_cctools") { - sources = [ - "cctools/include/mach-o/getsect.h", - "cctools/libmacho/getsecbyname.c", - ] - public_configs = [ ":apple_cctools_config" ] -} diff --git a/third_party/apple_cctools/README.crashpad b/third_party/apple_cctools/README.crashpad deleted file mode 100644 index 43b08618..00000000 --- a/third_party/apple_cctools/README.crashpad +++ /dev/null @@ -1,44 +0,0 @@ -Name: Apple cctools -Short Name: cctools -URL: https://opensource.apple.com/source/cctools/ -URL: https://opensource.apple.com/tarballs/cctools/ -Version: 855 (from Xcode 5.1) -License: APSL 2.0 -License File: cctools/APPLE_LICENSE -Security Critical: no - -Description: -cctools contains portions of Apple’s compiler toolchain, including common tools -like ar, as, nm, strings, and strip, and platform-specific tools like lipo and -otool. It also contains support libraries such as libmacho, which contains -interfaces for dealing with Mach-O images. - -libmacho is available on macOS as a runtime library that is part of libSystem, -but versions of libmacho included in operating system versions prior to Mac OS X -10.7 did not include the getsectiondata() and getsegmentdata() functions. This -library is present here to provide implementations of these functions for -systems that do not have them. - -Crashpad code is not expected to use this library directly. It should use the -getsectiondata() and getsegmentdata() wrappers in compat, which will use -system-provided implementations if present at runtime, and will otherwise fall -back to the implementations in this library. - -Local Modifications: - - Only cctools/APPLE_LICENSE, cctools/libmacho/getsecbyname.c, and - cctools/include/mach-o/getsect.h are included. - - getsecbyname.c and getsect.h have been trimmed to remove everything other - than the getsectiondata() and getsegmentdata() functions. The #include guards - in getsect.h have been made unique. - - getsectiondata() is renamed to crashpad_getsectiondata(), and - getsegmentdata() is renamed to crashpad_getsegmentdata(). - - These functions are only declared and defined if the deployment target is - older than 10.7. This library is not needed otherwise, because in that case, - the system always provides implementations in runtime libraries. - - Originally, each of these two functions were implemented twice: once for - 32-bit code and once for 64-bit code. Aside from the types and constants - used, the two implementations were completely identical. This has been - simplified to have a shared implementation that relies on local typedefs and - constants being defined properly. This change was only made in - getsecbyname.c. getsect.h was not changed to avoid leaking new definitions - beyond this header. diff --git a/third_party/apple_cctools/apple_cctools.gyp b/third_party/apple_cctools/apple_cctools.gyp deleted file mode 100644 index 7e0dfd43..00000000 --- a/third_party/apple_cctools/apple_cctools.gyp +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2014 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. - -{ - 'targets': [ - { - 'target_name': 'apple_cctools', - 'type': 'static_library', - 'include_dirs': [ - '../..', - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../..', - ], - }, - 'sources': [ - 'cctools/include/mach-o/getsect.h', - 'cctools/libmacho/getsecbyname.c', - ], - }, - ], -} diff --git a/third_party/apple_cctools/cctools/APPLE_LICENSE b/third_party/apple_cctools/cctools/APPLE_LICENSE deleted file mode 100644 index fe81a60c..00000000 --- a/third_party/apple_cctools/cctools/APPLE_LICENSE +++ /dev/null @@ -1,367 +0,0 @@ -APPLE PUBLIC SOURCE LICENSE -Version 2.0 - August 6, 2003 - -Please read this License carefully before downloading this software. -By downloading or using this software, you are agreeing to be bound by -the terms of this License. If you do not or cannot agree to the terms -of this License, please do not download or use the software. - -1. General; Definitions. This License applies to any program or other -work which Apple Computer, Inc. ("Apple") makes publicly available and -which contains a notice placed by Apple identifying such program or -work as "Original Code" and stating that it is subject to the terms of -this Apple Public Source License version 2.0 ("License"). As used in -this License: - -1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is -the grantor of rights, (i) claims of patents that are now or hereafter -acquired, owned by or assigned to Apple and (ii) that cover subject -matter contained in the Original Code, but only to the extent -necessary to use, reproduce and/or distribute the Original Code -without infringement; and (b) in the case where You are the grantor of -rights, (i) claims of patents that are now or hereafter acquired, -owned by or assigned to You and (ii) that cover subject matter in Your -Modifications, taken alone or in combination with Original Code. - -1.2 "Contributor" means any person or entity that creates or -contributes to the creation of Modifications. - -1.3 "Covered Code" means the Original Code, Modifications, the -combination of Original Code and any Modifications, and/or any -respective portions thereof. - -1.4 "Externally Deploy" means: (a) to sublicense, distribute or -otherwise make Covered Code available, directly or indirectly, to -anyone other than You; and/or (b) to use Covered Code, alone or as -part of a Larger Work, in any way to provide a service, including but -not limited to delivery of content, through electronic communication -with a client other than You. - -1.5 "Larger Work" means a work which combines Covered Code or portions -thereof with code not governed by the terms of this License. - -1.6 "Modifications" mean any addition to, deletion from, and/or change -to, the substance and/or structure of the Original Code, any previous -Modifications, the combination of Original Code and any previous -Modifications, and/or any respective portions thereof. When code is -released as a series of files, a Modification is: (a) any addition to -or deletion from the contents of a file containing Covered Code; -and/or (b) any new file or other representation of computer program -statements that contains any part of Covered Code. - -1.7 "Original Code" means (a) the Source Code of a program or other -work as originally made available by Apple under this License, -including the Source Code of any updates or upgrades to such programs -or works made available by Apple under this License, and that has been -expressly identified by Apple as such in the header file(s) of such -work; and (b) the object code compiled from such Source Code and -originally made available by Apple under this License. - -1.8 "Source Code" means the human readable form of a program or other -work that is suitable for making modifications to it, including all -modules it contains, plus any associated interface definition files, -scripts used to control compilation and installation of an executable -(object code). - -1.9 "You" or "Your" means an individual or a legal entity exercising -rights under this License. For legal entities, "You" or "Your" -includes any entity which controls, is controlled by, or is under -common control with, You, where "control" means (a) the power, direct -or indirect, to cause the direction or management of such entity, -whether by contract or otherwise, or (b) ownership of fifty percent -(50%) or more of the outstanding shares or beneficial ownership of -such entity. - -2. Permitted Uses; Conditions & Restrictions. Subject to the terms -and conditions of this License, Apple hereby grants You, effective on -the date You accept this License and download the Original Code, a -world-wide, royalty-free, non-exclusive license, to the extent of -Apple's Applicable Patent Rights and copyrights covering the Original -Code, to do the following: - -2.1 Unmodified Code. You may use, reproduce, display, perform, -internally distribute within Your organization, and Externally Deploy -verbatim, unmodified copies of the Original Code, for commercial or -non-commercial purposes, provided that in each instance: - -(a) You must retain and reproduce in all copies of Original Code the -copyright and other proprietary notices and disclaimers of Apple as -they appear in the Original Code, and keep intact all notices in the -Original Code that refer to this License; and - -(b) You must include a copy of this License with every copy of Source -Code of Covered Code and documentation You distribute or Externally -Deploy, and You may not offer or impose any terms on such Source Code -that alter or restrict this License or the recipients' rights -hereunder, except as permitted under Section 6. - -2.2 Modified Code. You may modify Covered Code and use, reproduce, -display, perform, internally distribute within Your organization, and -Externally Deploy Your Modifications and Covered Code, for commercial -or non-commercial purposes, provided that in each instance You also -meet all of these conditions: - -(a) You must satisfy all the conditions of Section 2.1 with respect to -the Source Code of the Covered Code; - -(b) You must duplicate, to the extent it does not already exist, the -notice in Exhibit A in each file of the Source Code of all Your -Modifications, and cause the modified files to carry prominent notices -stating that You changed the files and the date of any change; and - -(c) If You Externally Deploy Your Modifications, You must make -Source Code of all Your Externally Deployed Modifications either -available to those to whom You have Externally Deployed Your -Modifications, or publicly available. Source Code of Your Externally -Deployed Modifications must be released under the terms set forth in -this License, including the license grants set forth in Section 3 -below, for as long as you Externally Deploy the Covered Code or twelve -(12) months from the date of initial External Deployment, whichever is -longer. You should preferably distribute the Source Code of Your -Externally Deployed Modifications electronically (e.g. download from a -web site). - -2.3 Distribution of Executable Versions. In addition, if You -Externally Deploy Covered Code (Original Code and/or Modifications) in -object code, executable form only, You must include a prominent -notice, in the code itself as well as in related documentation, -stating that Source Code of the Covered Code is available under the -terms of this License with information on how and where to obtain such -Source Code. - -2.4 Third Party Rights. You expressly acknowledge and agree that -although Apple and each Contributor grants the licenses to their -respective portions of the Covered Code set forth herein, no -assurances are provided by Apple or any Contributor that the Covered -Code does not infringe the patent or other intellectual property -rights of any other entity. Apple and each Contributor disclaim any -liability to You for claims brought by any other entity based on -infringement of intellectual property rights or otherwise. As a -condition to exercising the rights and licenses granted hereunder, You -hereby assume sole responsibility to secure any other intellectual -property rights needed, if any. For example, if a third party patent -license is required to allow You to distribute the Covered Code, it is -Your responsibility to acquire that license before distributing the -Covered Code. - -3. Your Grants. In consideration of, and as a condition to, the -licenses granted to You under this License, You hereby grant to any -person or entity receiving or distributing Covered Code under this -License a non-exclusive, royalty-free, perpetual, irrevocable license, -under Your Applicable Patent Rights and other intellectual property -rights (other than patent) owned or controlled by You, to use, -reproduce, display, perform, modify, sublicense, distribute and -Externally Deploy Your Modifications of the same scope and extent as -Apple's licenses under Sections 2.1 and 2.2 above. - -4. Larger Works. You may create a Larger Work by combining Covered -Code with other code not governed by the terms of this License and -distribute the Larger Work as a single product. In each such instance, -You must make sure the requirements of this License are fulfilled for -the Covered Code or any portion thereof. - -5. Limitations on Patent License. Except as expressly stated in -Section 2, no other patent rights, express or implied, are granted by -Apple herein. Modifications and/or Larger Works may require additional -patent licenses from Apple which Apple may grant in its sole -discretion. - -6. Additional Terms. You may choose to offer, and to charge a fee for, -warranty, support, indemnity or liability obligations and/or other -rights consistent with the scope of the license granted herein -("Additional Terms") to one or more recipients of Covered Code. -However, You may do so only on Your own behalf and as Your sole -responsibility, and not on behalf of Apple or any Contributor. You -must obtain the recipient's agreement that any such Additional Terms -are offered by You alone, and You hereby agree to indemnify, defend -and hold Apple and every Contributor harmless for any liability -incurred by or claims asserted against Apple or such Contributor by -reason of any such Additional Terms. - -7. Versions of the License. Apple may publish revised and/or new -versions of this License from time to time. Each version will be given -a distinguishing version number. Once Original Code has been published -under a particular version of this License, You may continue to use it -under the terms of that version. You may also choose to use such -Original Code under the terms of any subsequent version of this -License published by Apple. No one other than Apple has the right to -modify the terms applicable to Covered Code created under this -License. - -8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in -part pre-release, untested, or not fully tested works. The Covered -Code may contain errors that could cause failures or loss of data, and -may be incomplete or contain inaccuracies. You expressly acknowledge -and agree that use of the Covered Code, or any portion thereof, is at -Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND -WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND -APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE -PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM -ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT -NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF -MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR -PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD -PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST -INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE -FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, -THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR -ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO -ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE -AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. -You acknowledge that the Covered Code is not intended for use in the -operation of nuclear facilities, aircraft navigation, communication -systems, or air traffic control machines in which case the failure of -the Covered Code could lead to death, personal injury, or severe -physical or environmental damage. - -9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO -EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, -SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING -TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR -ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, -TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF -APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY -REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF -INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY -TO YOU. In no event shall Apple's total liability to You for all -damages (other than as may be required by applicable law) under this -License exceed the amount of fifty dollars ($50.00). - -10. Trademarks. This License does not grant any rights to use the -trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS", -"QuickTime", "QuickTime Streaming Server" or any other trademarks, -service marks, logos or trade names belonging to Apple (collectively -"Apple Marks") or to any trademark, service mark, logo or trade name -belonging to any Contributor. You agree not to use any Apple Marks in -or as part of the name of products derived from the Original Code or -to endorse or promote products derived from the Original Code other -than as expressly permitted by and in strict compliance at all times -with Apple's third party trademark usage guidelines which are posted -at http://www.apple.com/legal/guidelinesfor3rdparties.html. - -11. Ownership. Subject to the licenses granted under this License, -each Contributor retains all rights, title and interest in and to any -Modifications made by such Contributor. Apple retains all rights, -title and interest in and to the Original Code and any Modifications -made by or on behalf of Apple ("Apple Modifications"), and such Apple -Modifications will not be automatically subject to this License. Apple -may, at its sole discretion, choose to license such Apple -Modifications under this License, or on different terms from those -contained in this License or may choose not to license them at all. - -12. Termination. - -12.1 Termination. This License and the rights granted hereunder will -terminate: - -(a) automatically without notice from Apple if You fail to comply with -any term(s) of this License and fail to cure such breach within 30 -days of becoming aware of such breach; - -(b) immediately in the event of the circumstances described in Section -13.5(b); or - -(c) automatically without notice from Apple if You, at any time during -the term of this License, commence an action for patent infringement -against Apple; provided that Apple did not first commence -an action for patent infringement against You in that instance. - -12.2 Effect of Termination. Upon termination, You agree to immediately -stop any further use, reproduction, modification, sublicensing and -distribution of the Covered Code. All sublicenses to the Covered Code -which have been properly granted prior to termination shall survive -any termination of this License. Provisions which, by their nature, -should remain in effect beyond the termination of this License shall -survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, -12.2 and 13. No party will be liable to any other for compensation, -indemnity or damages of any sort solely as a result of terminating -this License in accordance with its terms, and termination of this -License will be without prejudice to any other right or remedy of -any party. - -13. Miscellaneous. - -13.1 Government End Users. The Covered Code is a "commercial item" as -defined in FAR 2.101. Government software and technical data rights in -the Covered Code include only those rights customarily provided to the -public as defined in this License. This customary commercial license -in technical data and software is provided in accordance with FAR -12.211 (Technical Data) and 12.212 (Computer Software) and, for -Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- -Commercial Items) and 227.7202-3 (Rights in Commercial Computer -Software or Computer Software Documentation). Accordingly, all U.S. -Government End Users acquire Covered Code with only those rights set -forth herein. - -13.2 Relationship of Parties. This License will not be construed as -creating an agency, partnership, joint venture or any other form of -legal association between or among You, Apple or any Contributor, and -You will not represent to the contrary, whether expressly, by -implication, appearance or otherwise. - -13.3 Independent Development. Nothing in this License will impair -Apple's right to acquire, license, develop, have others develop for -it, market and/or distribute technology or products that perform the -same or similar functions as, or otherwise compete with, -Modifications, Larger Works, technology or products that You may -develop, produce, market or distribute. - -13.4 Waiver; Construction. Failure by Apple or any Contributor to -enforce any provision of this License will not be deemed a waiver of -future enforcement of that or any other provision. Any law or -regulation which provides that the language of a contract shall be -construed against the drafter will not apply to this License. - -13.5 Severability. (a) If for any reason a court of competent -jurisdiction finds any provision of this License, or portion thereof, -to be unenforceable, that provision of the License will be enforced to -the maximum extent permissible so as to effect the economic benefits -and intent of the parties, and the remainder of this License will -continue in full force and effect. (b) Notwithstanding the foregoing, -if applicable law prohibits or restricts You from fully and/or -specifically complying with Sections 2 and/or 3 or prevents the -enforceability of either of those Sections, this License will -immediately terminate and You must immediately discontinue any use of -the Covered Code and destroy all copies of it that are in your -possession or control. - -13.6 Dispute Resolution. Any litigation or other dispute resolution -between You and Apple relating to this License shall take place in the -Northern District of California, and You and Apple hereby consent to -the personal jurisdiction of, and venue in, the state and federal -courts within that District with respect to this License. The -application of the United Nations Convention on Contracts for the -International Sale of Goods is expressly excluded. - -13.7 Entire Agreement; Governing Law. This License constitutes the -entire agreement between the parties with respect to the subject -matter hereof. This License shall be governed by the laws of the -United States and the State of California, except that body of -California law concerning conflicts of law. - -Where You are located in the province of Quebec, Canada, the following -clause applies: The parties hereby confirm that they have requested -that this License and all related documents be drafted in English. Les -parties ont exige que le present contrat et tous les documents -connexes soient rediges en anglais. - -EXHIBIT A. - -"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights -Reserved. - -This file contains Original Code and/or Modifications of Original Code -as defined in and that are subject to the Apple Public Source License -Version 2.0 (the 'License'). You may not use this file except in -compliance with the License. Please obtain a copy of the License at -http://www.opensource.apple.com/apsl/ and read it before using this -file. - -The Original Code and all software distributed under the License are -distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER -EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, -INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. -Please see the License for the specific language governing rights and -limitations under the License." diff --git a/third_party/apple_cctools/cctools/include/mach-o/getsect.h b/third_party/apple_cctools/cctools/include/mach-o/getsect.h deleted file mode 100644 index 639b8061..00000000 --- a/third_party/apple_cctools/cctools/include/mach-o/getsect.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -#ifndef CRASHPAD_THIRD_PARTY_APPLE_CCTOOLS_CCTOOLS_INCLUDE_MACH_O_GETSECT_H_ -#define CRASHPAD_THIRD_PARTY_APPLE_CCTOOLS_CCTOOLS_INCLUDE_MACH_O_GETSECT_H_ - -#include - -#if !defined(MAC_OS_X_VERSION_10_7) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifndef __LP64__ -/* - * Runtime interfaces for 32-bit Mach-O programs. - */ -extern uint8_t *crashpad_getsectiondata( - const struct mach_header *mhp, - const char *segname, - const char *sectname, - unsigned long *size); - -extern uint8_t *crashpad_getsegmentdata( - const struct mach_header *mhp, - const char *segname, - unsigned long *size); - -#else /* defined(__LP64__) */ -/* - * Runtime interfaces for 64-bit Mach-O programs. - */ -extern uint8_t *crashpad_getsectiondata( - const struct mach_header_64 *mhp, - const char *segname, - const char *sectname, - unsigned long *size); - -extern uint8_t *crashpad_getsegmentdata( - const struct mach_header_64 *mhp, - const char *segname, - unsigned long *size); - -#endif /* defined(__LP64__) */ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 */ - -#endif /* CRASHPAD_THIRD_PARTY_APPLE_CCTOOLS_CCTOOLS_INCLUDE_MACH_O_GETSECT_H_ */ diff --git a/third_party/apple_cctools/cctools/libmacho/getsecbyname.c b/third_party/apple_cctools/cctools/libmacho/getsecbyname.c deleted file mode 100644 index 1db1cbec..00000000 --- a/third_party/apple_cctools/cctools/libmacho/getsecbyname.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include "third_party/apple_cctools/cctools/include/mach-o/getsect.h" - -#if !defined(MAC_OS_X_VERSION_10_7) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 - -#include - -#ifndef __LP64__ -typedef struct mach_header mach_header_32_64; -typedef struct segment_command segment_command_32_64; -typedef struct section section_32_64; -#define LC_SEGMENT_32_64 LC_SEGMENT -#else /* defined(__LP64__) */ -typedef struct mach_header_64 mach_header_32_64; -typedef struct segment_command_64 segment_command_32_64; -typedef struct section_64 section_32_64; -#define LC_SEGMENT_32_64 LC_SEGMENT_64 -#endif /* defined(__LP64__) */ - -/* - * This routine returns the a pointer to the section contents of the named - * section in the named segment if it exists in the image pointed to by the - * mach header. Otherwise it returns zero. - */ - -uint8_t * -crashpad_getsectiondata( -const mach_header_32_64 *mhp, -const char *segname, -const char *sectname, -unsigned long *size) -{ - segment_command_32_64 *sgp, *zero; - section_32_64 *sp, *find; - uint32_t i, j; - - zero = 0; - find = 0; - sp = 0; - sgp = (segment_command_32_64 *) - ((char *)mhp + sizeof(mach_header_32_64)); - for(i = 0; i < mhp->ncmds; i++){ - if(sgp->cmd == LC_SEGMENT_32_64){ - if(zero == 0 && sgp->fileoff == 0 && sgp->nsects != 0){ - zero = sgp; - if(find != 0) - goto done; - } - if(find == 0 && - strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0){ - sp = (section_32_64 *)((char *)sgp + - sizeof(segment_command_32_64)); - for(j = 0; j < sgp->nsects; j++){ - if(strncmp(sp->sectname, sectname, - sizeof(sp->sectname)) == 0 && - strncmp(sp->segname, segname, - sizeof(sp->segname)) == 0){ - find = sp; - if(zero != 0) - goto done; - } - sp = (section_32_64 *)((char *)sp + - sizeof(section_32_64)); - } - } - } - sgp = (segment_command_32_64 *)((char *)sgp + sgp->cmdsize); - } - return(0); -done: - *size = sp->size; - return((uint8_t *)((uintptr_t)mhp - zero->vmaddr + sp->addr)); -} - -uint8_t * -crashpad_getsegmentdata( -const mach_header_32_64 *mhp, -const char *segname, -unsigned long *size) -{ - segment_command_32_64 *sgp, *zero, *find; - uint32_t i; - - zero = 0; - find = 0; - sgp = (segment_command_32_64 *) - ((char *)mhp + sizeof(mach_header_32_64)); - for(i = 0; i < mhp->ncmds; i++){ - if(sgp->cmd == LC_SEGMENT_32_64){ - if(zero == 0 && sgp->fileoff == 0 && sgp->nsects != 0){ - zero = sgp; - if(find != 0) - goto done; - } - if(find == 0 && - strncmp(sgp->segname, segname, sizeof(sgp->segname)) == 0){ - find = sgp; - if(zero != 0) - goto done; - } - } - sgp = (segment_command_32_64 *)((char *)sgp + sgp->cmdsize); - } - return(0); -done: - *size = sgp->vmsize; - return((uint8_t *)((uintptr_t)mhp - zero->vmaddr + sgp->vmaddr)); -} - -#endif /* MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 */ diff --git a/third_party/getopt/BUILD.gn b/third_party/getopt/BUILD.gn index 573d844b..da27624f 100644 --- a/third_party/getopt/BUILD.gn +++ b/third_party/getopt/BUILD.gn @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -source_set("getopt") { +static_library("getopt") { sources = [ "getopt.cc", "getopt.h", From 3650d565709f1cc390d1ea9e5a3e9a436b8c81a4 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Fri, 15 Dec 2017 18:05:12 -0500 Subject: [PATCH 073/326] gn: Build gtest and gmock tests Bug: crashpad:79 Change-Id: Iea78fcb6a758f57d2b550b214b947ca5aabad036 Reviewed-on: https://chromium-review.googlesource.com/827732 Commit-Queue: Mark Mentovai Reviewed-by: Scott Graham --- build/BUILDCONFIG.gn | 12 +- .../testing/gmock/BUILD.gn | 1 + .../testing/gtest/BUILD.gn | 1 + build/chromium_compatibility/testing/test.gni | 8 +- third_party/gtest/BUILD.gn | 230 +++++++++++++++++- third_party/gtest/gmock.gyp | 7 +- 6 files changed, 236 insertions(+), 23 deletions(-) diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn index c931c2a6..5ddabfd9 100644 --- a/build/BUILDCONFIG.gn +++ b/build/BUILDCONFIG.gn @@ -62,7 +62,7 @@ if (is_win) { "//third_party/mini_chromium/mini_chromium/build:gcc_like_toolchain") } -_default_configs = [ +default_configs = [ "//third_party/mini_chromium/mini_chromium/build:default", "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", @@ -72,23 +72,23 @@ _default_configs = [ ] set_defaults("source_set") { - configs = _default_configs + configs = default_configs } set_defaults("static_library") { - configs = _default_configs + configs = default_configs } set_defaults("executable") { - configs = _default_configs + configs = default_configs } set_defaults("loadable_module") { - configs = _default_configs + configs = default_configs } set_defaults("shared_library") { - configs = _default_configs + configs = default_configs } # These are set to constant values for Chromium build file compatibility. This diff --git a/build/chromium_compatibility/testing/gmock/BUILD.gn b/build/chromium_compatibility/testing/gmock/BUILD.gn index 06c404c8..77c7f589 100644 --- a/build/chromium_compatibility/testing/gmock/BUILD.gn +++ b/build/chromium_compatibility/testing/gmock/BUILD.gn @@ -15,6 +15,7 @@ # This is a forwarding target to match the location that Chromium uses. group("gmock") { + testonly = true public_configs = [ "//third_party/gtest:gmock_public_config" ] public_deps = [ "//third_party/gtest:gmock", diff --git a/build/chromium_compatibility/testing/gtest/BUILD.gn b/build/chromium_compatibility/testing/gtest/BUILD.gn index 236359fd..3dbb6729 100644 --- a/build/chromium_compatibility/testing/gtest/BUILD.gn +++ b/build/chromium_compatibility/testing/gtest/BUILD.gn @@ -15,6 +15,7 @@ # This is a forwarding target to match the location that Chromium uses. group("gtest") { + testonly = true public_configs = [ "//third_party/gtest:gtest_public_config" ] public_deps = [ "//third_party/gtest:gtest", diff --git a/build/chromium_compatibility/testing/test.gni b/build/chromium_compatibility/testing/test.gni index b94470dc..4642c9d5 100644 --- a/build/chromium_compatibility/testing/test.gni +++ b/build/chromium_compatibility/testing/test.gni @@ -14,9 +14,11 @@ template("test") { executable(target_name) { - deps = [] - forward_variables_from(invoker, "*") - testonly = true + forward_variables_from(invoker, "*") } } + +set_defaults("test") { + configs = default_configs +} diff --git a/third_party/gtest/BUILD.gn b/third_party/gtest/BUILD.gn index 97fe1839..80a65e4d 100644 --- a/third_party/gtest/BUILD.gn +++ b/third_party/gtest/BUILD.gn @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import("//testing/test.gni") + config("gtest_private_config") { visibility = [ ":*" ] include_dirs = [ "gtest/googletest" ] @@ -23,6 +25,7 @@ config("gtest_public_config") { } static_library("gtest") { + testonly = true sources = [ "gtest/googletest/include/gtest/gtest-death-test.h", "gtest/googletest/include/gtest/gtest-message.h", @@ -59,9 +62,155 @@ static_library("gtest") { "gtest/googletest/src/gtest.cc", ] sources -= [ "gtest/googletest/src/gtest-all.cc" ] + public_configs = [ ":gtest_public_config" ] configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] configs += [ ":gtest_private_config" ] - public_configs = [ ":gtest_public_config" ] +} + +static_library("gtest_main") { + # Tests outside of this file should use ../../test:gtest_main instead. + visibility = [ ":*" ] + + testonly = true + sources = [ + "gtest/googletest/src/gtest_main.cc", + ] + deps = [ + ":gtest", + ] +} + +test("gtest_all_test") { + sources = [ + "gtest/googletest/test/gtest-death-test_test.cc", + "gtest/googletest/test/gtest-filepath_test.cc", + "gtest/googletest/test/gtest-linked_ptr_test.cc", + "gtest/googletest/test/gtest-message_test.cc", + "gtest/googletest/test/gtest-options_test.cc", + "gtest/googletest/test/gtest-port_test.cc", + "gtest/googletest/test/gtest-printers_test.cc", + "gtest/googletest/test/gtest-test-part_test.cc", + "gtest/googletest/test/gtest-typed-test2_test.cc", + "gtest/googletest/test/gtest-typed-test_test.cc", + "gtest/googletest/test/gtest-typed-test_test.h", + "gtest/googletest/test/gtest_main_unittest.cc", + "gtest/googletest/test/gtest_pred_impl_unittest.cc", + "gtest/googletest/test/gtest_prod_test.cc", + "gtest/googletest/test/gtest_unittest.cc", + "gtest/googletest/test/production.cc", + "gtest/googletest/test/production.h", + ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs += [ ":gtest_private_config" ] + deps = [ + ":gtest", + ":gtest_main", + ] +} + +test("gtest_environment_test") { + sources = [ + "gtest/googletest/test/gtest_environment_test.cc", + ] + configs += [ ":gtest_private_config" ] + deps = [ + ":gtest", + ] +} + +test("gtest_listener_test") { + sources = [ + "gtest/googletest/test/gtest-listener_test.cc", + ] + deps = [ + ":gtest", + ] +} + +test("gtest_no_test") { + sources = [ + "gtest/googletest/test/gtest_no_test_unittest.cc", + ] + deps = [ + ":gtest", + ] +} + +test("gtest_param_test") { + sources = [ + "gtest/googletest/test/gtest-param-test2_test.cc", + "gtest/googletest/test/gtest-param-test_test.cc", + "gtest/googletest/test/gtest-param-test_test.h", + ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs += [ ":gtest_private_config" ] + deps = [ + ":gtest", + ] +} + +test("gtest_premature_exit_test") { + sources = [ + "gtest/googletest/test/gtest_premature_exit_test.cc", + ] + deps = [ + ":gtest", + ] +} + +test("gtest_repeat_test") { + sources = [ + "gtest/googletest/test/gtest_repeat_test.cc", + ] + configs += [ ":gtest_private_config" ] + deps = [ + ":gtest", + ] +} + +test("gtest_sole_header_test") { + sources = [ + "gtest/googletest/test/gtest_sole_header_test.cc", + ] + deps = [ + ":gtest", + ":gtest_main", + ] +} + +test("gtest_stress_test") { + sources = [ + "gtest/googletest/test/gtest_stress_test.cc", + ] + configs += [ ":gtest_private_config" ] + deps = [ + ":gtest", + ] +} + +test("gtest_unittest_api_test") { + sources = [ + "gtest/googletest/test/gtest-unittest-api_test.cc", + ] + deps = [ + ":gtest", + ] +} + +group("gtest_all_tests") { + testonly = true + deps = [ + ":gtest_all_test", + ":gtest_environment_test", + ":gtest_listener_test", + ":gtest_no_test", + ":gtest_param_test", + ":gtest_premature_exit_test", + ":gtest_repeat_test", + ":gtest_sole_header_test", + ":gtest_stress_test", + ":gtest_unittest_api_test", + ] } config("gmock_private_config") { @@ -74,19 +223,17 @@ config("gmock_public_config") { # The MOCK_METHODn() macros do not specify “override”, which triggers this # warning in users: “error: 'Method' overrides a member function but is not - # marked 'override' [-Werror,-Winconsistent-missing-override]”. Suppress - # these warnings, and add -Wno-unknown-warning-option because only recent - # versions of clang (trunk r220703 and later, version - # 3.6 and later) recognize it. + # marked 'override' [-Werror,-Winconsistent-missing-override]”. Suppress these + # warnings until https://github.com/google/googletest/issues/533 is fixed. if (is_clang) { cflags_cc = [ "-Wno-inconsistent-missing-override", - "-Wno-unknown-warning-option", ] } } static_library("gmock") { + testonly = true sources = [ "gtest/googlemock/include/gmock/gmock-actions.h", "gtest/googlemock/include/gmock/gmock-cardinalities.h", @@ -113,15 +260,18 @@ static_library("gmock") { "gtest/googlemock/src/gmock.cc", ] sources -= [ "gtest/googlemock/src/gmock-all.cc" ] + public_configs = [ ":gmock_public_config" ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs += [ ":gmock_private_config" ] deps = [ ":gtest", ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] - configs += [ ":gmock_private_config" ] - public_configs = [ ":gmock_public_config" ] } static_library("gmock_main") { + # Tests outside of this file should use ../../test:gmock_main instead. + visibility = [ ":*" ] + testonly = true sources = [ "gtest/googlemock/src/gmock_main.cc", ] @@ -130,3 +280,65 @@ static_library("gmock_main") { ":gtest", ] } + +test("gmock_all_test") { + sources = [ + "gtest/googlemock/test/gmock-actions_test.cc", + "gtest/googlemock/test/gmock-cardinalities_test.cc", + "gtest/googlemock/test/gmock-generated-actions_test.cc", + "gtest/googlemock/test/gmock-generated-function-mockers_test.cc", + "gtest/googlemock/test/gmock-generated-internal-utils_test.cc", + "gtest/googlemock/test/gmock-generated-matchers_test.cc", + "gtest/googlemock/test/gmock-internal-utils_test.cc", + "gtest/googlemock/test/gmock-matchers_test.cc", + "gtest/googlemock/test/gmock-more-actions_test.cc", + "gtest/googlemock/test/gmock-nice-strict_test.cc", + "gtest/googlemock/test/gmock-port_test.cc", + "gtest/googlemock/test/gmock-spec-builders_test.cc", + "gtest/googlemock/test/gmock_test.cc", + ] + configs += [ + ":gmock_private_config", + ":gtest_private_config", + ] + deps = [ + ":gmock", + ":gmock_main", + ":gtest", + ] +} + +test("gmock_link_test") { + sources = [ + "gtest/googlemock/test/gmock_link2_test.cc", + "gtest/googlemock/test/gmock_link_test.cc", + "gtest/googlemock/test/gmock_link_test.h", + ] + configs += [ ":gmock_private_config" ] + deps = [ + ":gmock", + ":gmock_main", + ":gtest", + ] +} + +test("gmock_stress_test") { + sources = [ + "gtest/googlemock/test/gmock_stress_test.cc", + ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs += [ ":gmock_private_config" ] + deps = [ + ":gmock", + ":gtest", + ] +} + +group("gmock_all_tests") { + testonly = true + deps = [ + ":gmock_all_test", + ":gmock_link_test", + ":gmock_stress_test", + ] +} diff --git a/third_party/gtest/gmock.gyp b/third_party/gtest/gmock.gyp index a6600708..db8e8b87 100644 --- a/third_party/gtest/gmock.gyp +++ b/third_party/gtest/gmock.gyp @@ -92,22 +92,19 @@ # triggers this warning in users: “error: 'Method' overrides a # member function but is not marked 'override' # [-Werror,-Winconsistent-missing-override]”. Suppress these - # warnings, and add -Wno-unknown-warning-option because only - # recent versions of clang (trunk r220703 and later, version - # 3.6 and later) recognize it. + # warnings until https://github.com/google/googletest/issues/533 is + # fixed. 'conditions': [ ['OS=="mac"', { 'xcode_settings': { 'WARNING_CFLAGS': [ '-Wno-inconsistent-missing-override', - '-Wno-unknown-warning-option', ], }, }], ['OS=="linux" or OS=="android"', { 'cflags': [ '-Wno-inconsistent-missing-override', - '-Wno-unknown-warning-option', ], }], ], From 6e0f15b8f5a7fb6a1f8a54bdd43d4350049abf54 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Thu, 14 Dec 2017 15:50:09 -0500 Subject: [PATCH 074/326] =?UTF-8?q?gn:=20Add=20=E2=80=9Csystem=E2=80=9D?= =?UTF-8?q?=C2=A0as=20a=20zlib=20source,=20used=20for=20standalone=20non-W?= =?UTF-8?q?in/Fuchsia?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: crashpad:79 Change-Id: I07e346000ce6df07ac7021056a4cb00d28443e15 Reviewed-on: https://chromium-review.googlesource.com/827745 Reviewed-by: Scott Graham Commit-Queue: Mark Mentovai --- third_party/zlib/BUILD.gn | 23 +++++++++++++++++++---- third_party/zlib/zlib_crashpad.h | 6 +++--- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn index cfea9f3e..706f52cb 100644 --- a/third_party/zlib/BUILD.gn +++ b/third_party/zlib/BUILD.gn @@ -14,23 +14,38 @@ import("../../build/crashpad_in_chromium.gni") +if (crashpad_in_chromium) { + zlib_source = "chromium" +} else if (!is_win && !is_fuchsia) { + zlib_source = "system" +} else { + zlib_source = "embedded" +} + config("zlib_config") { - if (crashpad_in_chromium) { + if (zlib_source == "chromium") { defines = [ "CRASHPAD_ZLIB_SOURCE_CHROMIUM" ] - } else { + } else if (zlib_source == "system") { + defines = [ "CRASHPAD_ZLIB_SOURCE_SYSTEM" ] + } else if (zlib_source == "embedded") { defines = [ "CRASHPAD_ZLIB_SOURCE_EMBEDDED" ] include_dirs = [ "zlib" ] } } -if (crashpad_in_chromium) { +if (zlib_source == "chromium") { group("zlib") { public_configs = [ ":zlib_config" ] public_deps = [ "//third_party/zlib", ] } -} else { +} else if (zlib_source == "system") { + source_set("zlib") { + public_configs = [ ":zlib_config" ] + libs = [ "z" ] + } +} else if (zlib_source == "embedded") { static_library("zlib") { sources = [ "zlib/adler32.c", diff --git a/third_party/zlib/zlib_crashpad.h b/third_party/zlib/zlib_crashpad.h index 2ab542e0..5df7f659 100644 --- a/third_party/zlib/zlib_crashpad.h +++ b/third_party/zlib/zlib_crashpad.h @@ -19,12 +19,12 @@ // available at any other location in the source tree. It will #include the // proper depending on how the build has been configured. -#if defined(CRASHPAD_ZLIB_SOURCE_SYSTEM) +#if defined(CRASHPAD_ZLIB_SOURCE_CHROMIUM) +#include "third_party/zlib/zlib.h" +#elif defined(CRASHPAD_ZLIB_SOURCE_SYSTEM) #include #elif defined(CRASHPAD_ZLIB_SOURCE_EMBEDDED) #include "third_party/zlib/zlib/zlib.h" -#elif defined(CRASHPAD_ZLIB_SOURCE_CHROMIUM) -#include "third_party/zlib/zlib.h" #else #error Unknown zlib source #endif From 1764594bff5fcb57c79e6ecf8e05320400c0e99e Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Fri, 15 Dec 2017 18:49:34 -0500 Subject: [PATCH 075/326] gn, mac: Work without a sysroot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is needed to make the “sysroot = "/"” configuration, which translates to “sysroot = ""”, work properly Bug: crashpad:79 Change-Id: I25ab49b7d57abfcf0ce9a62925013bb58dadf5dd Reviewed-on: https://chromium-review.googlesource.com/831007 Reviewed-by: Scott Graham --- util/BUILD.gn | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/util/BUILD.gn b/util/BUILD.gn index ba5232f0..e296b29e 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -42,9 +42,13 @@ if (is_mac) { hermetic_xcode_path, ] } + if (sysroot != "") { + args += [ + "--sdk", + sysroot, + ] + } args += [ - "--sdk", - sysroot, "--include", rebase_path("../compat/mac", root_build_dir), ] From b51adda8d29c82253e104fc9588fe38a4ed45f94 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Mon, 18 Dec 2017 15:26:15 -0500 Subject: [PATCH 076/326] Build certain gtest/gmock tests with -Wno-unused-private-field Fuchsia is using a newer Clang than I was using on macOS when I wrote these GN targets, and -Wunused-private-field can now detect these violations. Change-Id: If71eb74f6453957aa92852cbe53356e325c7b635 Reviewed-on: https://chromium-review.googlesource.com/833195 Reviewed-by: Scott Graham Commit-Queue: Mark Mentovai --- third_party/gtest/BUILD.gn | 24 ++++++++++++++++++++---- third_party/gtest/gmock.gyp | 20 ++++++++++++++++++++ third_party/gtest/gtest.gyp | 20 ++++++++++++++++++++ 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/third_party/gtest/BUILD.gn b/third_party/gtest/BUILD.gn index 80a65e4d..86b0d0fb 100644 --- a/third_party/gtest/BUILD.gn +++ b/third_party/gtest/BUILD.gn @@ -147,6 +147,13 @@ test("gtest_param_test") { deps = [ ":gtest", ] + + if (is_clang) { + cflags_cc = [ + # For gtest/googlemock/test/gmock-matchers_test.cc’s Unstreamable::value_. + "-Wno-unused-private-field", + ] + } } test("gtest_premature_exit_test") { @@ -221,12 +228,13 @@ config("gmock_private_config") { config("gmock_public_config") { include_dirs = [ "gtest/googlemock/include" ] - # The MOCK_METHODn() macros do not specify “override”, which triggers this - # warning in users: “error: 'Method' overrides a member function but is not - # marked 'override' [-Werror,-Winconsistent-missing-override]”. Suppress these - # warnings until https://github.com/google/googletest/issues/533 is fixed. if (is_clang) { cflags_cc = [ + # The MOCK_METHODn() macros do not specify “override”, which triggers this + # warning in users: “error: 'Method' overrides a member function but is + # not marked 'override' [-Werror,-Winconsistent-missing-override]”. + # Suppress these warnings until + # https://github.com/google/googletest/issues/533 is fixed. "-Wno-inconsistent-missing-override", ] } @@ -306,6 +314,14 @@ test("gmock_all_test") { ":gmock_main", ":gtest", ] + + if (is_clang) { + cflags_cc = [ + # For gtest/googlemock/test/gmock-matchers_test.cc’s + # testing::gmock_matchers_test::Unprintable::c_. + "-Wno-unused-private-field", + ] + } } test("gmock_link_test") { diff --git a/third_party/gtest/gmock.gyp b/third_party/gtest/gmock.gyp index db8e8b87..d53c925f 100644 --- a/third_party/gtest/gmock.gyp +++ b/third_party/gtest/gmock.gyp @@ -168,6 +168,26 @@ '<(gmock_dir)/test/gmock-spec-builders_test.cc', '<(gmock_dir)/test/gmock_test.cc', ], + 'conditions': [ + ['clang!=0', { + # For gtest/googlemock/test/gmock-matchers_test.cc’s + # Unstreamable::value_. + 'conditions': [ + ['OS=="mac"', { + 'xcode_settings': { + 'WARNING_CFLAGS': [ + '-Wno-unused-private-field', + ], + }, + }], + ['OS=="linux" or OS=="android"', { + 'cflags': [ + '-Wno-unused-private-field', + ], + }], + ], + }], + ], }, { 'target_name': 'gmock_link_test', diff --git a/third_party/gtest/gtest.gyp b/third_party/gtest/gtest.gyp index 5079e269..5d93feb5 100644 --- a/third_party/gtest/gtest.gyp +++ b/third_party/gtest/gtest.gyp @@ -216,6 +216,26 @@ '<(gtest_dir)/test/gtest-param-test_test.cc', '<(gtest_dir)/test/gtest-param-test_test.h', ], + 'conditions': [ + ['clang!=0', { + # For gtest/googlemock/test/gmock-matchers_test.cc’s + # Unstreamable::value_. + 'conditions': [ + ['OS=="mac"', { + 'xcode_settings': { + 'WARNING_CFLAGS': [ + '-Wno-unused-private-field', + ], + }, + }], + ['OS=="linux" or OS=="android"', { + 'cflags': [ + '-Wno-unused-private-field', + ], + }], + ], + }], + ], }, { 'target_name': 'gtest_premature_exit_test', From 457cc6a34fde6ed6ef87edb92da5a7ae139fdf4a Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 18 Dec 2017 14:35:55 -0800 Subject: [PATCH 077/326] gn: Refactor build files to avoid build/secondary In doing standalone bringup of Crashpad targeting Fuchsia, it seemed tidy to keep the same literal paths to the dependencies that Chromium needed and add stubs/forwarding to build/secondary in the Crashpad tree as required to make those work. However, when trying to build Crashpad in the Fuchsia tree itself, that would require adding forwarding files to the Fuchsia tree to match the Chromium directory structure, which would be awkward. Instead, have explicit dependencies in the Crashpad tree that select the locations for various dependencies. Bug: crashpad:79, crashpad:196 Change-Id: Ib506839f9c97d8ef823663cdc733cbdcfa126139 Reviewed-on: https://chromium-review.googlesource.com/826025 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- .gn | 4 - BUILD.gn | 5 +- build/BUILD.gn | 16 +- build/BUILDCONFIG.gn | 4 - build/chromium_compatibility/README.crashpad | 5 - .../chromium_compatibility/base/test/BUILD.gn | 19 - .../build/config/compiler/BUILD.gn | 28 - .../build/config/compiler/compiler.gni | 16 - .../build/config/sysroot.gni | 22 - .../chromium_compatibility/build/win/BUILD.gn | 25 - .../testing/gmock/BUILD.gn | 23 - .../testing/gtest/BUILD.gn | 23 - build/chromium_compatibility/testing/test.gni | 24 - build/crashpad_dependencies.gni | 43 ++ build/crashpad_in_chromium.gni | 17 - client/BUILD.gn | 10 +- handler/BUILD.gn | 20 +- minidump/BUILD.gn | 12 +- snapshot/BUILD.gn | 35 +- test/BUILD.gn | 32 +- test/gtest_main.cc | 8 +- test/test_paths.cc | 10 +- third_party/gtest/BUILD.gn | 686 +++++++++--------- .../mini_chromium}/BUILD.gn | 31 +- third_party/zlib/BUILD.gn | 4 +- tools/BUILD.gn | 22 +- util/BUILD.gn | 30 +- 27 files changed, 523 insertions(+), 651 deletions(-) delete mode 100644 build/chromium_compatibility/README.crashpad delete mode 100644 build/chromium_compatibility/base/test/BUILD.gn delete mode 100644 build/chromium_compatibility/build/config/compiler/BUILD.gn delete mode 100644 build/chromium_compatibility/build/config/compiler/compiler.gni delete mode 100644 build/chromium_compatibility/build/config/sysroot.gni delete mode 100644 build/chromium_compatibility/build/win/BUILD.gn delete mode 100644 build/chromium_compatibility/testing/gmock/BUILD.gn delete mode 100644 build/chromium_compatibility/testing/gtest/BUILD.gn delete mode 100644 build/chromium_compatibility/testing/test.gni create mode 100644 build/crashpad_dependencies.gni delete mode 100644 build/crashpad_in_chromium.gni rename {build/chromium_compatibility/base => third_party/mini_chromium}/BUILD.gn (56%) diff --git a/.gn b/.gn index 304422da..d447a553 100644 --- a/.gn +++ b/.gn @@ -13,7 +13,3 @@ # limitations under the License. buildconfig = "//build/BUILDCONFIG.gn" - -# This secondary source root is used to put various forwarding/stub files that -# serve to make the core build files compatible with Chromium. -secondary_source = "//build/chromium_compatibility/" diff --git a/BUILD.gn b/BUILD.gn index 6ca07512..e3d23173 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -12,14 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("//testing/test.gni") -import("build/crashpad_in_chromium.gni") +import("build/crashpad_dependencies.gni") config("crashpad_config") { include_dirs = [ "." ] } -if (crashpad_in_chromium) { +if (crashpad_is_in_chromium) { test("crashpad_tests") { deps = [ "client:client_test", diff --git a/build/BUILD.gn b/build/BUILD.gn index e60cd0c6..c55c7b78 100644 --- a/build/BUILD.gn +++ b/build/BUILD.gn @@ -16,10 +16,18 @@ # whether code is being built standalone, or in Chromium, or potentially in some # other configutation. -import("crashpad_in_chromium.gni") +import("crashpad_dependencies.gni") -config("crashpad_in_chromium") { - if (crashpad_in_chromium) { - defines = [ "CRASHPAD_IN_CHROMIUM" ] +config("crashpad_is_in_chromium") { + if (crashpad_is_in_chromium) { + defines = [ "CRASHPAD_IS_IN_CHROMIUM" ] + } +} + +group("default_exe_manifest_win") { + if (crashpad_is_in_chromium) { + deps = [ + "//build/win:default_exe_manifest", + ] } } diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn index 5ddabfd9..a033e823 100644 --- a/build/BUILDCONFIG.gn +++ b/build/BUILDCONFIG.gn @@ -65,10 +65,6 @@ if (is_win) { default_configs = [ "//third_party/mini_chromium/mini_chromium/build:default", "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", - - # This (no-op) is added here so that build files that expect to be able to - # remove it can do so without causing an error. - "//build/config/compiler:chromium_code", ] set_defaults("source_set") { diff --git a/build/chromium_compatibility/README.crashpad b/build/chromium_compatibility/README.crashpad deleted file mode 100644 index 9a3c1296..00000000 --- a/build/chromium_compatibility/README.crashpad +++ /dev/null @@ -1,5 +0,0 @@ -This directory is used as a secondary GN source root for compatibility with -Chromium. Files in this subtree should match file paths that the Crashpad build -files need to refer to when building in Chromium. In the Crashpad tree, they -should either be empty/no-ops, or forward to the real Crashpad implementation -in the real tree. No actual configuration should be done in this secondary tree. diff --git a/build/chromium_compatibility/base/test/BUILD.gn b/build/chromium_compatibility/base/test/BUILD.gn deleted file mode 100644 index 5bc810a0..00000000 --- a/build/chromium_compatibility/base/test/BUILD.gn +++ /dev/null @@ -1,19 +0,0 @@ -# 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. - -# This is a stub to match Chromium. This target is unused and has no effect when -# building standalone in Crashpad. - -group("test_support") { -} diff --git a/build/chromium_compatibility/build/config/compiler/BUILD.gn b/build/chromium_compatibility/build/config/compiler/BUILD.gn deleted file mode 100644 index 9d4d2e14..00000000 --- a/build/chromium_compatibility/build/config/compiler/BUILD.gn +++ /dev/null @@ -1,28 +0,0 @@ -# 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. - -# This is a stub to match Chromium. The configs in this file do not have any -# effect on the build when building standalone in Crashpad. - -config("default_symbols") { -} - -config("minimal_symbols") { -} - -config("chromium_code") { -} - -config("no_chromium_code") { -} diff --git a/build/chromium_compatibility/build/config/compiler/compiler.gni b/build/chromium_compatibility/build/config/compiler/compiler.gni deleted file mode 100644 index f615259a..00000000 --- a/build/chromium_compatibility/build/config/compiler/compiler.gni +++ /dev/null @@ -1,16 +0,0 @@ -# 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. - -# This is a stub to match Chromium, but is unused when building standalone in -# Crashpad. diff --git a/build/chromium_compatibility/build/config/sysroot.gni b/build/chromium_compatibility/build/config/sysroot.gni deleted file mode 100644 index 49390dde..00000000 --- a/build/chromium_compatibility/build/config/sysroot.gni +++ /dev/null @@ -1,22 +0,0 @@ -# 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. - -# In Crashpad, sysroot.gni is in mini_chromium, forward to that one. -import("//third_party/mini_chromium/mini_chromium/build/sysroot.gni") - -# To avoid Chromium Mac hermetic toolchain paths without explicitly checking -# whether building in Chromium mode. -if (is_mac) { - use_system_xcode = true -} diff --git a/build/chromium_compatibility/build/win/BUILD.gn b/build/chromium_compatibility/build/win/BUILD.gn deleted file mode 100644 index 3bf730e4..00000000 --- a/build/chromium_compatibility/build/win/BUILD.gn +++ /dev/null @@ -1,25 +0,0 @@ -# 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. - -# This is a stub to match Chromium. The configs in this file do not have any -# effect on the build when building standalone in Crashpad. - -group("default_exe_manifest") { -} - -config("console") { -} - -config("windowed") { -} diff --git a/build/chromium_compatibility/testing/gmock/BUILD.gn b/build/chromium_compatibility/testing/gmock/BUILD.gn deleted file mode 100644 index 77c7f589..00000000 --- a/build/chromium_compatibility/testing/gmock/BUILD.gn +++ /dev/null @@ -1,23 +0,0 @@ -# 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. - -# This is a forwarding target to match the location that Chromium uses. - -group("gmock") { - testonly = true - public_configs = [ "//third_party/gtest:gmock_public_config" ] - public_deps = [ - "//third_party/gtest:gmock", - ] -} diff --git a/build/chromium_compatibility/testing/gtest/BUILD.gn b/build/chromium_compatibility/testing/gtest/BUILD.gn deleted file mode 100644 index 3dbb6729..00000000 --- a/build/chromium_compatibility/testing/gtest/BUILD.gn +++ /dev/null @@ -1,23 +0,0 @@ -# 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. - -# This is a forwarding target to match the location that Chromium uses. - -group("gtest") { - testonly = true - public_configs = [ "//third_party/gtest:gtest_public_config" ] - public_deps = [ - "//third_party/gtest:gtest", - ] -} diff --git a/build/chromium_compatibility/testing/test.gni b/build/chromium_compatibility/testing/test.gni deleted file mode 100644 index 4642c9d5..00000000 --- a/build/chromium_compatibility/testing/test.gni +++ /dev/null @@ -1,24 +0,0 @@ -# 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. - -template("test") { - executable(target_name) { - testonly = true - forward_variables_from(invoker, "*") - } -} - -set_defaults("test") { - configs = default_configs -} diff --git a/build/crashpad_dependencies.gni b/build/crashpad_dependencies.gni new file mode 100644 index 00000000..ae6e1196 --- /dev/null +++ b/build/crashpad_dependencies.gni @@ -0,0 +1,43 @@ +# 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. + +declare_args() { + # Determines various flavors of build configuration, and which concrete + # targets to use for dependencies. Valid values are "standalone", "chromium", + # and "fuchsia". + crashpad_dependencies = "standalone" +} + +assert( + crashpad_dependencies == "chromium" || crashpad_dependencies == "fuchsia" || + crashpad_dependencies == "standalone") + +crashpad_is_in_chromium = crashpad_dependencies == "chromium" +crashpad_is_in_fuchsia = crashpad_dependencies == "fuchsia" +crashpad_is_standalone = crashpad_dependencies == "standalone" + +if (crashpad_is_in_chromium) { + import("//testing/test.gni") +} else { + template("test") { + executable(target_name) { + testonly = true + forward_variables_from(invoker, "*") + } + } + + set_defaults("test") { + configs = default_configs + } +} diff --git a/build/crashpad_in_chromium.gni b/build/crashpad_in_chromium.gni deleted file mode 100644 index 568c52e7..00000000 --- a/build/crashpad_in_chromium.gni +++ /dev/null @@ -1,17 +0,0 @@ -# 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. - -declare_args() { - crashpad_in_chromium = false -} diff --git a/client/BUILD.gn b/client/BUILD.gn index bdff4cf8..8d8c79f4 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("//testing/test.gni") - static_library("client") { sources = [ "annotation.cc", @@ -64,8 +62,8 @@ static_library("client") { deps = [ "../compat", + "../third_party/mini_chromium:base", "../util", - "//base", ] if (is_win) { @@ -102,10 +100,10 @@ source_set("client_test") { ":client", "../compat", "../test", + "../third_party/gtest:gmock", + "../third_party/gtest:gtest", + "../third_party/mini_chromium:base", "../util", - "//base", - "//testing/gmock", - "//testing/gtest", ] data_deps = [ diff --git a/handler/BUILD.gn b/handler/BUILD.gn index 496f256e..1223110a 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("//testing/test.gni") - static_library("handler") { sources = [ "crash_report_upload_thread.cc", @@ -62,9 +60,9 @@ static_library("handler") { "../compat", "../minidump", "../snapshot", + "../third_party/mini_chromium:base", "../tools:tool_support", "../util", - "//base", ] if (is_win) { @@ -86,9 +84,9 @@ source_set("handler_test") { "../snapshot", "../snapshot:test_support", "../test", + "../third_party/gtest:gtest", + "../third_party/mini_chromium:base", "../util", - "//base", - "//testing/gtest", ] if (is_win) { @@ -107,9 +105,9 @@ executable("crashpad_handler") { deps = [ ":handler", + "../build:default_exe_manifest_win", "../compat", - "//base", - "//build/win:default_exe_manifest", + "../third_party/mini_chromium:base", ] if (is_mac && is_component_build) { @@ -143,11 +141,11 @@ executable("crashpad_handler_test_extended_handler") { deps = [ ":handler", + "../build:default_exe_manifest_win", "../compat", "../minidump:test_support", + "../third_party/mini_chromium:base", "../tools:tool_support", - "//base", - "//build/win:default_exe_manifest", ] } @@ -164,9 +162,9 @@ if (is_win) { deps = [ ":handler", + "../build:default_exe_manifest_win", "../compat", - "//base", - "//build/win:default_exe_manifest", + "../third_party/mini_chromium:base", ] } diff --git a/minidump/BUILD.gn b/minidump/BUILD.gn index a3c6a2c9..7a51a49a 100644 --- a/minidump/BUILD.gn +++ b/minidump/BUILD.gn @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("//testing/test.gni") - static_library("minidump") { sources = [ "minidump_annotation_writer.cc", @@ -77,8 +75,8 @@ static_library("minidump") { deps = [ "../snapshot", + "../third_party/mini_chromium:base", "../util", - "//base", ] if (is_win) { @@ -118,8 +116,8 @@ static_library("test_support") { ] deps = [ - "//base", - "//testing/gtest", + "../third_party/gtest:gtest", + "../third_party/mini_chromium:base", ] if (is_win) { @@ -158,9 +156,9 @@ source_set("minidump_test") { ":test_support", "../snapshot:test_support", "../test", + "../third_party/gtest:gtest", + "../third_party/mini_chromium:base", "../util", - "//base", - "//testing/gtest", ] if (is_win) { diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index c5d38f16..5e9a9314 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -12,8 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("//build/config/compiler/compiler.gni") -import("//testing/test.gni") +import("../build/crashpad_dependencies.gni") static_library("snapshot") { sources = [ @@ -147,8 +146,8 @@ static_library("snapshot") { deps = [ "../client", "../compat", + "../third_party/mini_chromium:base", "../util", - "//base", ] if (is_win) { @@ -171,8 +170,8 @@ if (is_win) { deps = [ ":snapshot", "../compat", + "../third_party/mini_chromium:base", "../util", - "//base", ] } } else { @@ -210,8 +209,8 @@ static_library("test_support") { deps = [ "../compat", + "../third_party/mini_chromium:base", "../util", - "//base", ] if (is_win) { @@ -262,9 +261,9 @@ source_set("snapshot_test") { "../client", "../compat", "../test", + "../third_party/gtest:gtest", + "../third_party/mini_chromium:base", "../util", - "//base", - "//testing/gtest", ] data_deps = [ @@ -303,7 +302,7 @@ loadable_module("crashpad_snapshot_test_module") { ] deps = [ "../client", - "//base", + "../third_party/mini_chromium:base", ] } @@ -314,7 +313,7 @@ loadable_module("crashpad_snapshot_test_module_large") { ] defines = [ "CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE" ] deps = [ - "//base", + "../third_party/mini_chromium:base", ] } @@ -325,7 +324,7 @@ loadable_module("crashpad_snapshot_test_module_small") { ] defines = [ "CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL" ] deps = [ - "//base", + "../third_party/mini_chromium:base", ] } @@ -354,7 +353,7 @@ if (is_win) { deps = [ "../client", "../compat", - "//base", + "../third_party/mini_chromium:base", ] } @@ -366,8 +365,8 @@ if (is_win) { deps = [ "../client", "../compat", + "../third_party/mini_chromium:base", "../util", - "//base", ] } @@ -379,8 +378,8 @@ if (is_win) { deps = [ "../client", "../compat", + "../third_party/mini_chromium:base", "../util", - "//base", ] } @@ -392,7 +391,7 @@ if (is_win) { deps = [ "../client", "../compat", - "//base", + "../third_party/mini_chromium:base", ] } @@ -404,10 +403,10 @@ if (is_win) { deps = [ "../client", "../compat", + "../third_party/mini_chromium:base", "../util", - "//base", ] - if (symbol_level == 0) { + if (crashpad_is_in_chromium && symbol_level == 0) { # The tests that use this executable rely on at least minimal debug info. configs -= [ "//build/config/compiler:default_symbols" ] configs += [ "//build/config/compiler:minimal_symbols" ] @@ -421,9 +420,9 @@ if (is_win) { ] deps = [ "../client", - "//base", + "../third_party/mini_chromium:base", ] - if (symbol_level == 0) { + if (crashpad_is_in_chromium && symbol_level == 0) { # The tests that use this module rely on at least minimal debug info. configs -= [ "//build/config/compiler:default_symbols" ] configs += [ "//build/config/compiler:minimal_symbols" ] diff --git a/test/BUILD.gn b/test/BUILD.gn index ae44fc57..21948a72 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("//testing/test.gni") - static_library("test") { testonly = true @@ -82,7 +80,7 @@ static_library("test") { public_configs = [ "..:crashpad_config" ] - configs += [ "../build:crashpad_in_chromium" ] + configs += [ "../build:crashpad_is_in_chromium" ] data = [ "test_paths_test_data_root.txt", @@ -90,9 +88,9 @@ static_library("test") { deps = [ "../compat", + "../third_party/gtest:gtest", + "../third_party/mini_chromium:base", "../util", - "//base", - "//testing/gtest", ] if (is_mac) { @@ -140,10 +138,10 @@ source_set("test_test") { deps = [ ":test", "../compat", + "../third_party/gtest:gmock", + "../third_party/gtest:gtest", + "../third_party/mini_chromium:base", "../util", - "//base", - "//testing/gmock", - "//testing/gtest", ] data_deps = [ @@ -162,14 +160,14 @@ static_library("gmock_main") { sources = [ "gtest_main.cc", ] - configs += [ "../build:crashpad_in_chromium" ] + configs += [ "../build:crashpad_is_in_chromium" ] defines = [ "CRASHPAD_TEST_LAUNCHER_GMOCK" ] deps = [ ":test", - "//base", - "//base/test:test_support", - "//testing/gmock", - "//testing/gtest", + "../third_party/gtest:gmock", + "../third_party/gtest:gtest", + "../third_party/mini_chromium:base", + "../third_party/mini_chromium:base_test_support", ] } @@ -178,12 +176,12 @@ static_library("gtest_main") { sources = [ "gtest_main.cc", ] - configs += [ "../build:crashpad_in_chromium" ] + configs += [ "../build:crashpad_is_in_chromium" ] defines = [ "CRASHPAD_TEST_LAUNCHER_GTEST" ] deps = [ ":test", - "//base", - "//base/test:test_support", - "//testing/gtest", + "../third_party/gtest:gtest", + "../third_party/mini_chromium:base", + "../third_party/mini_chromium:base_test_support", ] } diff --git a/test/gtest_main.cc b/test/gtest_main.cc index d8c2a904..5a3d7996 100644 --- a/test/gtest_main.cc +++ b/test/gtest_main.cc @@ -25,18 +25,18 @@ #include "test/win/win_child_process.h" #endif // OS_WIN -#if defined(CRASHPAD_IN_CHROMIUM) +#if defined(CRASHPAD_IS_IN_CHROMIUM) #include "base/bind.h" #include "base/test/launcher/unit_test_launcher.h" #include "base/test/test_suite.h" -#endif // CRASHPAD_IN_CHROMIUM +#endif // CRASHPAD_IS_IN_CHROMIUM int main(int argc, char* argv[]) { crashpad::test::InitializeMainArguments(argc, argv); testing::AddGlobalTestEnvironment( crashpad::test::DisabledTestGtestEnvironment::Get()); -#if defined(CRASHPAD_IN_CHROMIUM) +#if defined(CRASHPAD_IS_IN_CHROMIUM) #if defined(OS_WIN) // Chromium’s test launcher interferes with WinMultiprocess-based tests. Allow @@ -58,7 +58,7 @@ int main(int argc, char* argv[]) { base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite))); } -#endif // CRASHPAD_IN_CHROMIUM +#endif // CRASHPAD_IS_IN_CHROMIUM #if defined(CRASHPAD_TEST_LAUNCHER_GMOCK) testing::InitGoogleMock(&argc, argv); diff --git a/test/test_paths.cc b/test/test_paths.cc index 923d22f6..b3eee3c1 100644 --- a/test/test_paths.cc +++ b/test/test_paths.cc @@ -127,12 +127,12 @@ base::FilePath TestPaths::Executable() { // static base::FilePath TestPaths::ExpectedExecutableBasename( const base::FilePath::StringType& name) { -#if defined(CRASHPAD_IN_CHROMIUM) +#if defined(CRASHPAD_IS_IN_CHROMIUM) base::FilePath::StringType executable_name( FILE_PATH_LITERAL("crashpad_tests")); -#else // CRASHPAD_IN_CHROMIUM +#else // CRASHPAD_IS_IN_CHROMIUM base::FilePath::StringType executable_name(name); -#endif // CRASHPAD_IN_CHROMIUM +#endif // CRASHPAD_IS_IN_CHROMIUM #if defined(OS_WIN) executable_name += FILE_PATH_LITERAL(".exe"); @@ -170,9 +170,9 @@ base::FilePath TestPaths::BuildArtifact( base::FilePath::StringType test_name = FILE_PATH_LITERAL("crashpad_") + module + FILE_PATH_LITERAL("_test"); -#if !defined(CRASHPAD_IN_CHROMIUM) +#if !defined(CRASHPAD_IS_IN_CHROMIUM) CHECK(Executable().BaseName().RemoveFinalExtension().value() == test_name); -#endif // !CRASHPAD_IN_CHROMIUM +#endif // !CRASHPAD_IS_IN_CHROMIUM base::FilePath::StringType extension; switch (file_type) { diff --git a/third_party/gtest/BUILD.gn b/third_party/gtest/BUILD.gn index 86b0d0fb..bbb6e4c4 100644 --- a/third_party/gtest/BUILD.gn +++ b/third_party/gtest/BUILD.gn @@ -12,349 +12,371 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("//testing/test.gni") +import("../../build/crashpad_dependencies.gni") -config("gtest_private_config") { - visibility = [ ":*" ] - include_dirs = [ "gtest/googletest" ] - defines = [ "GUNIT_NO_GOOGLE3=1" ] -} +if (crashpad_is_in_chromium) { + group("gtest") { + public_deps = [ + "//testing/gtest", + ] + } + group("gmock") { + public_deps = [ + "//testing/gmock", + ] + } +} else if (crashpad_is_in_fuchsia) { + # TODO(scottmg): Fuchsia doesn't have a third_party/gmock, and has a + # pre-gmock-integration gtest. + group("gtest") { + public_deps = [ + "//third_party/gtest", + ] + } +} else if (crashpad_is_standalone) { + config("gtest_private_config") { + visibility = [ ":*" ] + include_dirs = [ "gtest/googletest" ] + defines = [ "GUNIT_NO_GOOGLE3=1" ] + } -config("gtest_public_config") { - include_dirs = [ "gtest/googletest/include" ] -} + config("gtest_public_config") { + include_dirs = [ "gtest/googletest/include" ] + } -static_library("gtest") { - testonly = true - sources = [ - "gtest/googletest/include/gtest/gtest-death-test.h", - "gtest/googletest/include/gtest/gtest-message.h", - "gtest/googletest/include/gtest/gtest-param-test.h", - "gtest/googletest/include/gtest/gtest-printers.h", - "gtest/googletest/include/gtest/gtest-spi.h", - "gtest/googletest/include/gtest/gtest-test-part.h", - "gtest/googletest/include/gtest/gtest-typed-test.h", - "gtest/googletest/include/gtest/gtest.h", - "gtest/googletest/include/gtest/gtest_pred_impl.h", - "gtest/googletest/include/gtest/gtest_prod.h", - "gtest/googletest/include/gtest/internal/custom/gtest-port.h", - "gtest/googletest/include/gtest/internal/custom/gtest-printers.h", - "gtest/googletest/include/gtest/internal/custom/gtest.h", - "gtest/googletest/include/gtest/internal/gtest-death-test-internal.h", - "gtest/googletest/include/gtest/internal/gtest-filepath.h", - "gtest/googletest/include/gtest/internal/gtest-internal.h", - "gtest/googletest/include/gtest/internal/gtest-linked_ptr.h", - "gtest/googletest/include/gtest/internal/gtest-param-util-generated.h", - "gtest/googletest/include/gtest/internal/gtest-param-util.h", - "gtest/googletest/include/gtest/internal/gtest-port-arch.h", - "gtest/googletest/include/gtest/internal/gtest-port.h", - "gtest/googletest/include/gtest/internal/gtest-string.h", - "gtest/googletest/include/gtest/internal/gtest-tuple.h", - "gtest/googletest/include/gtest/internal/gtest-type-util.h", - "gtest/googletest/src/gtest-all.cc", - "gtest/googletest/src/gtest-death-test.cc", - "gtest/googletest/src/gtest-filepath.cc", - "gtest/googletest/src/gtest-internal-inl.h", - "gtest/googletest/src/gtest-port.cc", - "gtest/googletest/src/gtest-printers.cc", - "gtest/googletest/src/gtest-test-part.cc", - "gtest/googletest/src/gtest-typed-test.cc", - "gtest/googletest/src/gtest.cc", - ] - sources -= [ "gtest/googletest/src/gtest-all.cc" ] - public_configs = [ ":gtest_public_config" ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] - configs += [ ":gtest_private_config" ] -} + static_library("gtest") { + testonly = true + sources = [ + "gtest/googletest/include/gtest/gtest-death-test.h", + "gtest/googletest/include/gtest/gtest-message.h", + "gtest/googletest/include/gtest/gtest-param-test.h", + "gtest/googletest/include/gtest/gtest-printers.h", + "gtest/googletest/include/gtest/gtest-spi.h", + "gtest/googletest/include/gtest/gtest-test-part.h", + "gtest/googletest/include/gtest/gtest-typed-test.h", + "gtest/googletest/include/gtest/gtest.h", + "gtest/googletest/include/gtest/gtest_pred_impl.h", + "gtest/googletest/include/gtest/gtest_prod.h", + "gtest/googletest/include/gtest/internal/custom/gtest-port.h", + "gtest/googletest/include/gtest/internal/custom/gtest-printers.h", + "gtest/googletest/include/gtest/internal/custom/gtest.h", + "gtest/googletest/include/gtest/internal/gtest-death-test-internal.h", + "gtest/googletest/include/gtest/internal/gtest-filepath.h", + "gtest/googletest/include/gtest/internal/gtest-internal.h", + "gtest/googletest/include/gtest/internal/gtest-linked_ptr.h", + "gtest/googletest/include/gtest/internal/gtest-param-util-generated.h", + "gtest/googletest/include/gtest/internal/gtest-param-util.h", + "gtest/googletest/include/gtest/internal/gtest-port-arch.h", + "gtest/googletest/include/gtest/internal/gtest-port.h", + "gtest/googletest/include/gtest/internal/gtest-string.h", + "gtest/googletest/include/gtest/internal/gtest-tuple.h", + "gtest/googletest/include/gtest/internal/gtest-type-util.h", + "gtest/googletest/src/gtest-all.cc", + "gtest/googletest/src/gtest-death-test.cc", + "gtest/googletest/src/gtest-filepath.cc", + "gtest/googletest/src/gtest-internal-inl.h", + "gtest/googletest/src/gtest-port.cc", + "gtest/googletest/src/gtest-printers.cc", + "gtest/googletest/src/gtest-test-part.cc", + "gtest/googletest/src/gtest-typed-test.cc", + "gtest/googletest/src/gtest.cc", + ] + sources -= [ "gtest/googletest/src/gtest-all.cc" ] + public_configs = [ ":gtest_public_config" ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs += [ ":gtest_private_config" ] + } -static_library("gtest_main") { - # Tests outside of this file should use ../../test:gtest_main instead. - visibility = [ ":*" ] + static_library("gtest_main") { + # Tests outside of this file should use ../../test:gtest_main instead. + visibility = [ ":*" ] - testonly = true - sources = [ - "gtest/googletest/src/gtest_main.cc", - ] - deps = [ - ":gtest", - ] -} + testonly = true + sources = [ + "gtest/googletest/src/gtest_main.cc", + ] + deps = [ + ":gtest", + ] + } -test("gtest_all_test") { - sources = [ - "gtest/googletest/test/gtest-death-test_test.cc", - "gtest/googletest/test/gtest-filepath_test.cc", - "gtest/googletest/test/gtest-linked_ptr_test.cc", - "gtest/googletest/test/gtest-message_test.cc", - "gtest/googletest/test/gtest-options_test.cc", - "gtest/googletest/test/gtest-port_test.cc", - "gtest/googletest/test/gtest-printers_test.cc", - "gtest/googletest/test/gtest-test-part_test.cc", - "gtest/googletest/test/gtest-typed-test2_test.cc", - "gtest/googletest/test/gtest-typed-test_test.cc", - "gtest/googletest/test/gtest-typed-test_test.h", - "gtest/googletest/test/gtest_main_unittest.cc", - "gtest/googletest/test/gtest_pred_impl_unittest.cc", - "gtest/googletest/test/gtest_prod_test.cc", - "gtest/googletest/test/gtest_unittest.cc", - "gtest/googletest/test/production.cc", - "gtest/googletest/test/production.h", - ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] - configs += [ ":gtest_private_config" ] - deps = [ - ":gtest", - ":gtest_main", - ] -} + test("gtest_all_test") { + sources = [ + "gtest/googletest/test/gtest-death-test_test.cc", + "gtest/googletest/test/gtest-filepath_test.cc", + "gtest/googletest/test/gtest-linked_ptr_test.cc", + "gtest/googletest/test/gtest-message_test.cc", + "gtest/googletest/test/gtest-options_test.cc", + "gtest/googletest/test/gtest-port_test.cc", + "gtest/googletest/test/gtest-printers_test.cc", + "gtest/googletest/test/gtest-test-part_test.cc", + "gtest/googletest/test/gtest-typed-test2_test.cc", + "gtest/googletest/test/gtest-typed-test_test.cc", + "gtest/googletest/test/gtest-typed-test_test.h", + "gtest/googletest/test/gtest_main_unittest.cc", + "gtest/googletest/test/gtest_pred_impl_unittest.cc", + "gtest/googletest/test/gtest_prod_test.cc", + "gtest/googletest/test/gtest_unittest.cc", + "gtest/googletest/test/production.cc", + "gtest/googletest/test/production.h", + ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs += [ ":gtest_private_config" ] + deps = [ + ":gtest", + ":gtest_main", + ] + } -test("gtest_environment_test") { - sources = [ - "gtest/googletest/test/gtest_environment_test.cc", - ] - configs += [ ":gtest_private_config" ] - deps = [ - ":gtest", - ] -} + test("gtest_environment_test") { + sources = [ + "gtest/googletest/test/gtest_environment_test.cc", + ] + configs += [ ":gtest_private_config" ] + deps = [ + ":gtest", + ] + } -test("gtest_listener_test") { - sources = [ - "gtest/googletest/test/gtest-listener_test.cc", - ] - deps = [ - ":gtest", - ] -} + test("gtest_listener_test") { + sources = [ + "gtest/googletest/test/gtest-listener_test.cc", + ] + deps = [ + ":gtest", + ] + } -test("gtest_no_test") { - sources = [ - "gtest/googletest/test/gtest_no_test_unittest.cc", - ] - deps = [ - ":gtest", - ] -} + test("gtest_no_test") { + sources = [ + "gtest/googletest/test/gtest_no_test_unittest.cc", + ] + deps = [ + ":gtest", + ] + } -test("gtest_param_test") { - sources = [ - "gtest/googletest/test/gtest-param-test2_test.cc", - "gtest/googletest/test/gtest-param-test_test.cc", - "gtest/googletest/test/gtest-param-test_test.h", - ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] - configs += [ ":gtest_private_config" ] - deps = [ - ":gtest", - ] + test("gtest_param_test") { + sources = [ + "gtest/googletest/test/gtest-param-test2_test.cc", + "gtest/googletest/test/gtest-param-test_test.cc", + "gtest/googletest/test/gtest-param-test_test.h", + ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs += [ ":gtest_private_config" ] + deps = [ + ":gtest", + ] - if (is_clang) { - cflags_cc = [ - # For gtest/googlemock/test/gmock-matchers_test.cc’s Unstreamable::value_. - "-Wno-unused-private-field", + if (is_clang) { + cflags_cc = [ + # For gtest/googlemock/test/gmock-matchers_test.cc’s + # Unstreamable::value_. + "-Wno-unused-private-field", + ] + } + } + + test("gtest_premature_exit_test") { + sources = [ + "gtest/googletest/test/gtest_premature_exit_test.cc", + ] + deps = [ + ":gtest", + ] + } + + test("gtest_repeat_test") { + sources = [ + "gtest/googletest/test/gtest_repeat_test.cc", + ] + configs += [ ":gtest_private_config" ] + deps = [ + ":gtest", + ] + } + + test("gtest_sole_header_test") { + sources = [ + "gtest/googletest/test/gtest_sole_header_test.cc", + ] + deps = [ + ":gtest", + ":gtest_main", + ] + } + + test("gtest_stress_test") { + sources = [ + "gtest/googletest/test/gtest_stress_test.cc", + ] + configs += [ ":gtest_private_config" ] + deps = [ + ":gtest", + ] + } + + test("gtest_unittest_api_test") { + sources = [ + "gtest/googletest/test/gtest-unittest-api_test.cc", + ] + deps = [ + ":gtest", + ] + } + + group("gtest_all_tests") { + testonly = true + deps = [ + ":gtest_all_test", + ":gtest_environment_test", + ":gtest_listener_test", + ":gtest_no_test", + ":gtest_param_test", + ":gtest_premature_exit_test", + ":gtest_repeat_test", + ":gtest_sole_header_test", + ":gtest_stress_test", + ":gtest_unittest_api_test", + ] + } + + config("gmock_private_config") { + visibility = [ ":*" ] + include_dirs = [ "gtest/googlemock" ] + } + + config("gmock_public_config") { + include_dirs = [ "gtest/googlemock/include" ] + + if (is_clang) { + cflags_cc = [ + # The MOCK_METHODn() macros do not specify “override”, which triggers + # this warning in users: “error: 'Method' overrides a member function + # but is not marked 'override' + # [-Werror,-Winconsistent-missing-override]”. Suppress these warnings + # until https://github.com/google/googletest/issues/533 is fixed. + "-Wno-inconsistent-missing-override", + ] + } + } + + static_library("gmock") { + testonly = true + sources = [ + "gtest/googlemock/include/gmock/gmock-actions.h", + "gtest/googlemock/include/gmock/gmock-cardinalities.h", + "gtest/googlemock/include/gmock/gmock-generated-actions.h", + "gtest/googlemock/include/gmock/gmock-generated-function-mockers.h", + "gtest/googlemock/include/gmock/gmock-generated-matchers.h", + "gtest/googlemock/include/gmock/gmock-generated-nice-strict.h", + "gtest/googlemock/include/gmock/gmock-matchers.h", + "gtest/googlemock/include/gmock/gmock-more-actions.h", + "gtest/googlemock/include/gmock/gmock-more-matchers.h", + "gtest/googlemock/include/gmock/gmock-spec-builders.h", + "gtest/googlemock/include/gmock/gmock.h", + "gtest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h", + "gtest/googlemock/include/gmock/internal/custom/gmock-matchers.h", + "gtest/googlemock/include/gmock/internal/custom/gmock-port.h", + "gtest/googlemock/include/gmock/internal/gmock-generated-internal-utils.h", + "gtest/googlemock/include/gmock/internal/gmock-internal-utils.h", + "gtest/googlemock/include/gmock/internal/gmock-port.h", + "gtest/googlemock/src/gmock-all.cc", + "gtest/googlemock/src/gmock-cardinalities.cc", + "gtest/googlemock/src/gmock-internal-utils.cc", + "gtest/googlemock/src/gmock-matchers.cc", + "gtest/googlemock/src/gmock-spec-builders.cc", + "gtest/googlemock/src/gmock.cc", + ] + sources -= [ "gtest/googlemock/src/gmock-all.cc" ] + public_configs = [ ":gmock_public_config" ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs += [ ":gmock_private_config" ] + deps = [ + ":gtest", + ] + } + + static_library("gmock_main") { + # Tests outside of this file should use ../../test:gmock_main instead. + visibility = [ ":*" ] + testonly = true + sources = [ + "gtest/googlemock/src/gmock_main.cc", + ] + deps = [ + ":gmock", + ":gtest", + ] + } + + test("gmock_all_test") { + sources = [ + "gtest/googlemock/test/gmock-actions_test.cc", + "gtest/googlemock/test/gmock-cardinalities_test.cc", + "gtest/googlemock/test/gmock-generated-actions_test.cc", + "gtest/googlemock/test/gmock-generated-function-mockers_test.cc", + "gtest/googlemock/test/gmock-generated-internal-utils_test.cc", + "gtest/googlemock/test/gmock-generated-matchers_test.cc", + "gtest/googlemock/test/gmock-internal-utils_test.cc", + "gtest/googlemock/test/gmock-matchers_test.cc", + "gtest/googlemock/test/gmock-more-actions_test.cc", + "gtest/googlemock/test/gmock-nice-strict_test.cc", + "gtest/googlemock/test/gmock-port_test.cc", + "gtest/googlemock/test/gmock-spec-builders_test.cc", + "gtest/googlemock/test/gmock_test.cc", + ] + configs += [ + ":gmock_private_config", + ":gtest_private_config", + ] + deps = [ + ":gmock", + ":gmock_main", + ":gtest", + ] + + if (is_clang) { + cflags_cc = [ + # For gtest/googlemock/test/gmock-matchers_test.cc’s + # testing::gmock_matchers_test::Unprintable::c_. + "-Wno-unused-private-field", + ] + } + } + + test("gmock_link_test") { + sources = [ + "gtest/googlemock/test/gmock_link2_test.cc", + "gtest/googlemock/test/gmock_link_test.cc", + "gtest/googlemock/test/gmock_link_test.h", + ] + configs += [ ":gmock_private_config" ] + deps = [ + ":gmock", + ":gmock_main", + ":gtest", + ] + } + + test("gmock_stress_test") { + sources = [ + "gtest/googlemock/test/gmock_stress_test.cc", + ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs += [ ":gmock_private_config" ] + deps = [ + ":gmock", + ":gtest", + ] + } + + group("gmock_all_tests") { + testonly = true + deps = [ + ":gmock_all_test", + ":gmock_link_test", + ":gmock_stress_test", ] } } - -test("gtest_premature_exit_test") { - sources = [ - "gtest/googletest/test/gtest_premature_exit_test.cc", - ] - deps = [ - ":gtest", - ] -} - -test("gtest_repeat_test") { - sources = [ - "gtest/googletest/test/gtest_repeat_test.cc", - ] - configs += [ ":gtest_private_config" ] - deps = [ - ":gtest", - ] -} - -test("gtest_sole_header_test") { - sources = [ - "gtest/googletest/test/gtest_sole_header_test.cc", - ] - deps = [ - ":gtest", - ":gtest_main", - ] -} - -test("gtest_stress_test") { - sources = [ - "gtest/googletest/test/gtest_stress_test.cc", - ] - configs += [ ":gtest_private_config" ] - deps = [ - ":gtest", - ] -} - -test("gtest_unittest_api_test") { - sources = [ - "gtest/googletest/test/gtest-unittest-api_test.cc", - ] - deps = [ - ":gtest", - ] -} - -group("gtest_all_tests") { - testonly = true - deps = [ - ":gtest_all_test", - ":gtest_environment_test", - ":gtest_listener_test", - ":gtest_no_test", - ":gtest_param_test", - ":gtest_premature_exit_test", - ":gtest_repeat_test", - ":gtest_sole_header_test", - ":gtest_stress_test", - ":gtest_unittest_api_test", - ] -} - -config("gmock_private_config") { - visibility = [ ":*" ] - include_dirs = [ "gtest/googlemock" ] -} - -config("gmock_public_config") { - include_dirs = [ "gtest/googlemock/include" ] - - if (is_clang) { - cflags_cc = [ - # The MOCK_METHODn() macros do not specify “override”, which triggers this - # warning in users: “error: 'Method' overrides a member function but is - # not marked 'override' [-Werror,-Winconsistent-missing-override]”. - # Suppress these warnings until - # https://github.com/google/googletest/issues/533 is fixed. - "-Wno-inconsistent-missing-override", - ] - } -} - -static_library("gmock") { - testonly = true - sources = [ - "gtest/googlemock/include/gmock/gmock-actions.h", - "gtest/googlemock/include/gmock/gmock-cardinalities.h", - "gtest/googlemock/include/gmock/gmock-generated-actions.h", - "gtest/googlemock/include/gmock/gmock-generated-function-mockers.h", - "gtest/googlemock/include/gmock/gmock-generated-matchers.h", - "gtest/googlemock/include/gmock/gmock-generated-nice-strict.h", - "gtest/googlemock/include/gmock/gmock-matchers.h", - "gtest/googlemock/include/gmock/gmock-more-actions.h", - "gtest/googlemock/include/gmock/gmock-more-matchers.h", - "gtest/googlemock/include/gmock/gmock-spec-builders.h", - "gtest/googlemock/include/gmock/gmock.h", - "gtest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h", - "gtest/googlemock/include/gmock/internal/custom/gmock-matchers.h", - "gtest/googlemock/include/gmock/internal/custom/gmock-port.h", - "gtest/googlemock/include/gmock/internal/gmock-generated-internal-utils.h", - "gtest/googlemock/include/gmock/internal/gmock-internal-utils.h", - "gtest/googlemock/include/gmock/internal/gmock-port.h", - "gtest/googlemock/src/gmock-all.cc", - "gtest/googlemock/src/gmock-cardinalities.cc", - "gtest/googlemock/src/gmock-internal-utils.cc", - "gtest/googlemock/src/gmock-matchers.cc", - "gtest/googlemock/src/gmock-spec-builders.cc", - "gtest/googlemock/src/gmock.cc", - ] - sources -= [ "gtest/googlemock/src/gmock-all.cc" ] - public_configs = [ ":gmock_public_config" ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] - configs += [ ":gmock_private_config" ] - deps = [ - ":gtest", - ] -} - -static_library("gmock_main") { - # Tests outside of this file should use ../../test:gmock_main instead. - visibility = [ ":*" ] - testonly = true - sources = [ - "gtest/googlemock/src/gmock_main.cc", - ] - deps = [ - ":gmock", - ":gtest", - ] -} - -test("gmock_all_test") { - sources = [ - "gtest/googlemock/test/gmock-actions_test.cc", - "gtest/googlemock/test/gmock-cardinalities_test.cc", - "gtest/googlemock/test/gmock-generated-actions_test.cc", - "gtest/googlemock/test/gmock-generated-function-mockers_test.cc", - "gtest/googlemock/test/gmock-generated-internal-utils_test.cc", - "gtest/googlemock/test/gmock-generated-matchers_test.cc", - "gtest/googlemock/test/gmock-internal-utils_test.cc", - "gtest/googlemock/test/gmock-matchers_test.cc", - "gtest/googlemock/test/gmock-more-actions_test.cc", - "gtest/googlemock/test/gmock-nice-strict_test.cc", - "gtest/googlemock/test/gmock-port_test.cc", - "gtest/googlemock/test/gmock-spec-builders_test.cc", - "gtest/googlemock/test/gmock_test.cc", - ] - configs += [ - ":gmock_private_config", - ":gtest_private_config", - ] - deps = [ - ":gmock", - ":gmock_main", - ":gtest", - ] - - if (is_clang) { - cflags_cc = [ - # For gtest/googlemock/test/gmock-matchers_test.cc’s - # testing::gmock_matchers_test::Unprintable::c_. - "-Wno-unused-private-field", - ] - } -} - -test("gmock_link_test") { - sources = [ - "gtest/googlemock/test/gmock_link2_test.cc", - "gtest/googlemock/test/gmock_link_test.cc", - "gtest/googlemock/test/gmock_link_test.h", - ] - configs += [ ":gmock_private_config" ] - deps = [ - ":gmock", - ":gmock_main", - ":gtest", - ] -} - -test("gmock_stress_test") { - sources = [ - "gtest/googlemock/test/gmock_stress_test.cc", - ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] - configs += [ ":gmock_private_config" ] - deps = [ - ":gmock", - ":gtest", - ] -} - -group("gmock_all_tests") { - testonly = true - deps = [ - ":gmock_all_test", - ":gmock_link_test", - ":gmock_stress_test", - ] -} diff --git a/build/chromium_compatibility/base/BUILD.gn b/third_party/mini_chromium/BUILD.gn similarity index 56% rename from build/chromium_compatibility/base/BUILD.gn rename to third_party/mini_chromium/BUILD.gn index f00a51c2..acd4a588 100644 --- a/build/chromium_compatibility/base/BUILD.gn +++ b/third_party/mini_chromium/BUILD.gn @@ -12,15 +12,28 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This target is a stub so that both Crashpad and Chromium can refer to "//base" -# in their build files. When building in the Chromium tree, "//base" will refer -# the "real" base, but when building standalone in Crashpad, we forward those -# references on to mini_chromium. +import("../../build/crashpad_dependencies.gni") group("base") { - public_configs = - [ "//third_party/mini_chromium/mini_chromium/base:base_public_config" ] - public_deps = [ - "//third_party/mini_chromium/mini_chromium/base", - ] + if (crashpad_is_in_chromium) { + public_deps = [ + "//base", + ] + } else if (crashpad_is_in_fuchsia) { + public_deps = [ + "//third_party/mini_chromium/base", + ] + } else if (crashpad_is_standalone) { + public_deps = [ + "//third_party/mini_chromium/mini_chromium/base", + ] + } +} + +group("base_test_support") { + if (crashpad_is_in_chromium) { + public_deps = [ + "//base:test_support", + ] + } } diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn index 706f52cb..d93ac5e6 100644 --- a/third_party/zlib/BUILD.gn +++ b/third_party/zlib/BUILD.gn @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("../../build/crashpad_in_chromium.gni") +import("../../build/crashpad_dependencies.gni") -if (crashpad_in_chromium) { +if (crashpad_is_in_chromium) { zlib_source = "chromium" } else if (!is_win && !is_fuchsia) { zlib_source = "system" diff --git a/tools/BUILD.gn b/tools/BUILD.gn index af02e66c..e81b4493 100644 --- a/tools/BUILD.gn +++ b/tools/BUILD.gn @@ -21,7 +21,7 @@ source_set("tool_support") { public_configs = [ "..:crashpad_config" ] deps = [ - "//base", + "../third_party/mini_chromium:base", ] } @@ -32,11 +32,11 @@ executable("crashpad_database_util") { deps = [ ":tool_support", + "../build:default_exe_manifest_win", "../client", "../compat", + "../third_party/mini_chromium:base", "../util", - "//base", - "//build/win:default_exe_manifest", ] } @@ -47,10 +47,10 @@ executable("crashpad_http_upload") { deps = [ ":tool_support", + "../build:default_exe_manifest_win", "../compat", + "../third_party/mini_chromium:base", "../util", - "//base", - "//build/win:default_exe_manifest", ] } @@ -61,12 +61,12 @@ executable("generate_dump") { deps = [ ":tool_support", + "../build:default_exe_manifest_win", "../compat", "../minidump", "../snapshot", + "../third_party/mini_chromium:base", "../util", - "//base", - "//build/win:default_exe_manifest", ] if (is_mac) { @@ -97,8 +97,8 @@ if (is_mac) { deps = [ ":tool_support", "../compat", + "../third_party/mini_chromium:base", "../util", - "//base", ] } @@ -122,8 +122,8 @@ if (is_mac) { deps = [ ":tool_support", "../compat", + "../third_party/mini_chromium:base", "../util", - "//base", ] } @@ -140,8 +140,8 @@ if (is_mac) { deps = [ ":tool_support", "../compat", + "../third_party/mini_chromium:base", "../util", - "//base", ] } @@ -154,8 +154,8 @@ if (is_mac) { ":tool_support", "../client", "../compat", + "../third_party/mini_chromium:base", "../util", - "//base", ] } } diff --git a/util/BUILD.gn b/util/BUILD.gn index e296b29e..43d64b38 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -12,10 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("//testing/test.gni") - if (is_mac) { - import("//build/config/sysroot.gni") + import("../build/crashpad_dependencies.gni") + + if (crashpad_is_in_chromium) { + import("//build/config/sysroot.gni") + } else { + import("//third_party/mini_chromium/mini_chromium/build/sysroot.gni") + } action_foreach("mig") { script = "mach/mig.py" @@ -36,11 +40,13 @@ if (is_mac) { args = [ "{{source}}" ] args += rebase_path(outputs, root_build_dir) - if (!use_system_xcode) { - args += [ - "--developer-dir", - hermetic_xcode_path, - ] + if (crashpad_is_in_chromium) { + if (!use_system_xcode) { + args += [ + "--developer-dir", + hermetic_xcode_path, + ] + } } if (sysroot != "") { args += [ @@ -328,8 +334,8 @@ static_library("util") { ] deps = [ + "../third_party/mini_chromium:base", "../third_party/zlib", - "//base", ] if (is_mac) { @@ -470,10 +476,10 @@ source_set("util_test") { "../client", "../compat", "../test", + "../third_party/gtest:gmock", + "../third_party/gtest:gtest", + "../third_party/mini_chromium:base", "../third_party/zlib", - "//base", - "//testing/gmock", - "//testing/gtest", ] if (is_mac) { From 1bc07b76ed2c66e892b300e51c9512e005c7844b Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 18 Dec 2017 16:01:51 -0800 Subject: [PATCH 078/326] gn: Use mini_chromium_is_posix in preference to global is_posix Goes with https://chromium-review.googlesource.com/c/chromium/mini_chromium/+/833328. Also roll mini_chromium: scottmg@around:/work/crashpad/crashpad/third_party/mini_chromium/mini_chromium$ git log 20182dd263312db9fad52042fc92c33331ec6904..e182031 --oneline e182031 gn: Add is_posix.gni to define local is_posix variable 4cb1344 gn: Enable proper release-mode optimizations for POSIX-non-Mac 9c0eb0c Remove reference to ptr_util.h c5ae5aa gn: Configure the sysroot in target_sysroot, not sysroot f7e5654 gn, mac: Honor mac_sdk_min, sysroot, and mac_deployment_target 7701901 Remove the deprecated sparse_histogram.h header. e2f0160 Use Chromium copyright notice and BSD license in mini_chromium Bug: crashpad:79, crashpad:196 Change-Id: Ie41d971e0e769db2ed18861da07021c071f6c650 Reviewed-on: https://chromium-review.googlesource.com/833329 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- DEPS | 2 +- build/BUILDCONFIG.gn | 2 -- build/crashpad_dependencies.gni | 12 +++++++++++- test/BUILD.gn | 6 ++++-- util/BUILD.gn | 8 ++++---- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/DEPS b/DEPS index c59ff430..e221cad1 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '20182dd263312db9fad52042fc92c33331ec6904', + 'e182031c61072f629494a1f52729a8d29a6acace', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn index a033e823..58178818 100644 --- a/build/BUILDCONFIG.gn +++ b/build/BUILDCONFIG.gn @@ -52,8 +52,6 @@ if (current_os == "mac") { is_fuchsia = true } -is_posix = is_mac || is_linux || is_android || is_fuchsia - if (is_win) { set_default_toolchain( "//third_party/mini_chromium/mini_chromium/build:msvc_toolchain") diff --git a/build/crashpad_dependencies.gni b/build/crashpad_dependencies.gni index ae6e1196..e786420f 100644 --- a/build/crashpad_dependencies.gni +++ b/build/crashpad_dependencies.gni @@ -27,6 +27,16 @@ crashpad_is_in_chromium = crashpad_dependencies == "chromium" crashpad_is_in_fuchsia = crashpad_dependencies == "fuchsia" crashpad_is_standalone = crashpad_dependencies == "standalone" +if (crashpad_is_in_chromium) { + crashpad_is_posix = is_posix +} else if (crashpad_is_in_fuchsia) { + import("//third_party/mini_chromium/build/is_posix.gni") + crashpad_is_posix = mini_chromium_is_posix +} else if (crashpad_is_standalone) { + import("../third_party/mini_chromium/mini_chromium/build/is_posix.gni") + crashpad_is_posix = mini_chromium_is_posix +} + if (crashpad_is_in_chromium) { import("//testing/test.gni") } else { @@ -38,6 +48,6 @@ if (crashpad_is_in_chromium) { } set_defaults("test") { - configs = default_configs + configs = _default_configs } } diff --git a/test/BUILD.gn b/test/BUILD.gn index 21948a72..e6a08a12 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import("../build/crashpad_dependencies.gni") + static_library("test") { testonly = true @@ -39,7 +41,7 @@ static_library("test") { "test_paths.h", ] - if (is_posix) { + if (crashpad_is_posix) { sources += [ "multiprocess_posix.cc", "scoped_temp_dir_posix.cc", @@ -112,7 +114,7 @@ source_set("test_test") { "test_paths_test.cc", ] - if (is_posix && !is_fuchsia) { + if (crashpad_is_posix && !is_fuchsia) { sources += [ "multiprocess_posix_test.cc" ] } diff --git a/util/BUILD.gn b/util/BUILD.gn index 43d64b38..e097f2fa 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -if (is_mac) { - import("../build/crashpad_dependencies.gni") +import("../build/crashpad_dependencies.gni") +if (is_mac) { if (crashpad_is_in_chromium) { import("//build/config/sysroot.gni") } else { @@ -150,7 +150,7 @@ static_library("util") { "thread/worker_thread.h", ] - if (is_posix) { + if (crashpad_is_posix) { sources += [ "file/directory_reader_posix.cc", "file/file_io_posix.cc", @@ -414,7 +414,7 @@ source_set("util_test") { sources += [ "net/http_transport_test.cc" ] } - if (is_posix) { + if (crashpad_is_posix) { if (!is_fuchsia) { sources += [ "posix/process_info_test.cc", From a0bd3fee55ea0538ac66bd9d20b06df766941f53 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 18 Dec 2017 16:41:20 -0800 Subject: [PATCH 079/326] fuchsia: Support referencing the Fuchsia tree's third_party/zlib This renames the "chromium" configuration of zlib to "external". What it really means is that zlib lives in //third_party/zlib, which happens to be where both Chromium and Fuchsia put it, with moderately similar build files. Bug: crashpad:79, crashpad:196 Change-Id: I380c106ec1f97471b2354166f5cf92885196e1b8 Reviewed-on: https://chromium-review.googlesource.com/833095 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- third_party/zlib/BUILD.gn | 10 +++++----- third_party/zlib/zlib_crashpad.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn index d93ac5e6..6edddfb9 100644 --- a/third_party/zlib/BUILD.gn +++ b/third_party/zlib/BUILD.gn @@ -14,8 +14,8 @@ import("../../build/crashpad_dependencies.gni") -if (crashpad_is_in_chromium) { - zlib_source = "chromium" +if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { + zlib_source = "external" } else if (!is_win && !is_fuchsia) { zlib_source = "system" } else { @@ -23,8 +23,8 @@ if (crashpad_is_in_chromium) { } config("zlib_config") { - if (zlib_source == "chromium") { - defines = [ "CRASHPAD_ZLIB_SOURCE_CHROMIUM" ] + if (zlib_source == "external") { + defines = [ "CRASHPAD_ZLIB_SOURCE_EXTERNAL" ] } else if (zlib_source == "system") { defines = [ "CRASHPAD_ZLIB_SOURCE_SYSTEM" ] } else if (zlib_source == "embedded") { @@ -33,7 +33,7 @@ config("zlib_config") { } } -if (zlib_source == "chromium") { +if (zlib_source == "external") { group("zlib") { public_configs = [ ":zlib_config" ] public_deps = [ diff --git a/third_party/zlib/zlib_crashpad.h b/third_party/zlib/zlib_crashpad.h index 5df7f659..d3a23861 100644 --- a/third_party/zlib/zlib_crashpad.h +++ b/third_party/zlib/zlib_crashpad.h @@ -19,7 +19,7 @@ // available at any other location in the source tree. It will #include the // proper depending on how the build has been configured. -#if defined(CRASHPAD_ZLIB_SOURCE_CHROMIUM) +#if defined(CRASHPAD_ZLIB_SOURCE_EXTERNAL) #include "third_party/zlib/zlib.h" #elif defined(CRASHPAD_ZLIB_SOURCE_SYSTEM) #include From 99b00306162df0984c6040f44b6f317ce9a67322 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Tue, 19 Dec 2017 14:21:14 -0500 Subject: [PATCH 080/326] gn, linux: Build for Linux with GN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is sufficient for a native Linux build using GN. Android is not yet supported. mini_chromium side: https://crrev.com/c/833407 This also updates mini_chromium to 404f6dbf9928. c913ef97a236 gn, linux: Build for Linux with GN 404f6dbf9928 gn: Don’t use .rsp files; rationalize descriptions and output dirs Bug: crashpad:79 Change-Id: I4f3b72fd02884d77812e520fb95231b35815677d Reviewed-on: https://chromium-review.googlesource.com/833408 Commit-Queue: Mark Mentovai Reviewed-by: Scott Graham --- DEPS | 2 +- build/BUILDCONFIG.gn | 32 ++++++++++------ build/crashpad_dependencies.gni | 2 +- compat/BUILD.gn | 19 ++++++++-- snapshot/BUILD.gn | 66 ++++++++++++++++++++++++++++++++- test/BUILD.gn | 9 +++++ tools/BUILD.gn | 11 +++--- util/BUILD.gn | 56 ++++++++++++++++++++++++++++ 8 files changed, 173 insertions(+), 24 deletions(-) diff --git a/DEPS b/DEPS index e221cad1..3f15230b 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'e182031c61072f629494a1f52729a8d29a6acace', + '404f6dbf9928dd19cb437ad5b05abfdd97b5bf67', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn index 58178818..7444c6f0 100644 --- a/build/BUILDCONFIG.gn +++ b/build/BUILDCONFIG.gn @@ -28,12 +28,6 @@ if (current_cpu == "") { current_cpu = target_cpu } -declare_args() { - is_debug = false - is_clang = current_os == "mac" || current_os == "fuchsia" - clang_root = "" -} - is_mac = false is_win = false is_linux = false @@ -52,6 +46,16 @@ if (current_os == "mac") { is_fuchsia = true } +declare_args() { + # When true, enables the debug configuration, with additional run-time checks + # and logging. When false, enables the release configuration, with additional + # optimizations. + is_debug = false + + # When true, configures for compilation with Clang. + is_clang = !is_win +} + if (is_win) { set_default_toolchain( "//third_party/mini_chromium/mini_chromium/build:msvc_toolchain") @@ -60,29 +64,33 @@ if (is_win) { "//third_party/mini_chromium/mini_chromium/build:gcc_like_toolchain") } -default_configs = [ +_default_configs = [ "//third_party/mini_chromium/mini_chromium/build:default", "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", ] +default_executable_configs = + _default_configs + + [ "//third_party/mini_chromium/mini_chromium/build:executable" ] + set_defaults("source_set") { - configs = default_configs + configs = _default_configs } set_defaults("static_library") { - configs = default_configs + configs = _default_configs } set_defaults("executable") { - configs = default_configs + configs = default_executable_configs } set_defaults("loadable_module") { - configs = default_configs + configs = _default_configs } set_defaults("shared_library") { - configs = default_configs + configs = _default_configs } # These are set to constant values for Chromium build file compatibility. This diff --git a/build/crashpad_dependencies.gni b/build/crashpad_dependencies.gni index e786420f..1964facd 100644 --- a/build/crashpad_dependencies.gni +++ b/build/crashpad_dependencies.gni @@ -48,6 +48,6 @@ if (crashpad_is_in_chromium) { } set_defaults("test") { - configs = _default_configs + configs = default_executable_configs } } diff --git a/compat/BUILD.gn b/compat/BUILD.gn index af845130..bff7d5f8 100644 --- a/compat/BUILD.gn +++ b/compat/BUILD.gn @@ -15,15 +15,19 @@ config("compat_config") { include_dirs = [] + if (is_mac) { + include_dirs += [ "mac" ] + } + + if (is_linux) { + include_dirs += [ "linux" ] + } + if (is_win) { include_dirs += [ "win" ] } else { include_dirs += [ "non_win" ] } - - if (is_mac) { - include_dirs += [ "mac" ] - } } template("compat_target") { @@ -55,6 +59,13 @@ compat_target("compat") { sources += [ "non_mac/mach/mach.h" ] } + if (is_linux) { + sources += [ + "linux/signal.h", + "linux/sys/ptrace.h", + ] + } + if (is_win) { sources += [ "win/getopt.h", diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 5e9a9314..2b442211 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -94,6 +94,34 @@ static_library("snapshot") { ] } + if (is_linux) { + sources += [ + "elf/elf_dynamic_array_reader.cc", + "elf/elf_dynamic_array_reader.h", + "elf/elf_image_reader.cc", + "elf/elf_image_reader.h", + "elf/elf_symbol_table_reader.cc", + "elf/elf_symbol_table_reader.h", + "linux/cpu_context_linux.cc", + "linux/cpu_context_linux.h", + "linux/debug_rendezvous.cc", + "linux/debug_rendezvous.h", + "linux/exception_snapshot_linux.cc", + "linux/exception_snapshot_linux.h", + "linux/memory_snapshot_linux.cc", + "linux/memory_snapshot_linux.h", + "linux/process_reader.cc", + "linux/process_reader.h", + "linux/process_snapshot_linux.cc", + "linux/process_snapshot_linux.h", + "linux/signal_context.h", + "linux/system_snapshot_linux.cc", + "linux/system_snapshot_linux.h", + "linux/thread_snapshot_linux.cc", + "linux/thread_snapshot_linux.h", + ] + } + if (is_win) { sources += [ "win/capture_memory_delegate_win.cc", @@ -218,12 +246,24 @@ static_library("test_support") { } } +config("snapshot_test_link") { + visibility = [ ":*" ] + if (is_linux) { + # There’s no way to make the link depend on this file. “inputs” doesn’t have + # the intended effect in a config. https://crbug.com/781858, + # https://crbug.com/796187. + inputs = [ + "elf/test_exported_symbols.sym", + ] + ldflags = [ "-Wl,--dynamic-list," + rebase_path(inputs[0], root_build_dir) ] + } +} + source_set("snapshot_test") { testonly = true sources = [ "cpu_context_test.cc", - "crashpad_info_client_options_test.cc", "minidump/process_snapshot_minidump_test.cc", ] @@ -239,6 +279,20 @@ source_set("snapshot_test") { ] } + if (is_linux) { + sources += [ + "elf/elf_image_reader_test.cc", + "elf/elf_image_reader_test_note.S", + "elf/test_exported_symbols.sym", + "linux/debug_rendezvous_test.cc", + "linux/exception_snapshot_linux_test.cc", + "linux/process_reader_test.cc", + "linux/system_snapshot_linux_test.cc", + ] + } else { + sources += [ "crashpad_info_client_options_test.cc" ] + } + if (is_win) { sources += [ "api/module_annotations_win_test.cc", @@ -255,6 +309,12 @@ source_set("snapshot_test") { sources += [ "posix/timezone_test.cc" ] } + # public_configs isn’t quite right. snapshot_test_link sets ldflags, and + # what’s really needed is a way to push ldflags to dependent targets that + # produce linker output. Luckily in this case, all dependents do produce + # linker output. https://crbug.com/796183. + public_configs = [ ":snapshot_test_link" ] + deps = [ ":snapshot_api", ":test_support", @@ -281,6 +341,10 @@ source_set("snapshot_test") { ] } + if (is_linux) { + libs = [ "dl" ] + } + if (is_win) { cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union diff --git a/test/BUILD.gn b/test/BUILD.gn index e6a08a12..6573c362 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -65,6 +65,15 @@ static_library("test") { ] } + if (is_linux) { + sources += [ + "linux/fake_ptrace_connection.cc", + "linux/fake_ptrace_connection.h", + "linux/get_tls.cc", + "linux/get_tls.h", + ] + } + if (is_win) { sources += [ "multiprocess_exec_win.cc", diff --git a/tools/BUILD.gn b/tools/BUILD.gn index e81b4493..016daa96 100644 --- a/tools/BUILD.gn +++ b/tools/BUILD.gn @@ -72,14 +72,15 @@ executable("generate_dump") { if (is_mac) { # This would be better as a config so that it could be shared with # exception_port_tool, but configs can’t alter “inputs”. + # https://crbug.com/781858. inputs = [ - rebase_path("mac/sectaskaccess_info.plist"), + "mac/sectaskaccess_info.plist", ] ldflags = [ "-sectcreate", "__TEXT", "__info_plist", - inputs[0], + rebase_path(inputs[0], root_build_dir), ] } @@ -108,15 +109,15 @@ if (is_mac) { ] # This would be better as a config so that it could be shared with - # generate_dump, but configs can’t alter “inputs”. + # generate_dump, but configs can’t alter “inputs”. https://crbug.com/781858. inputs = [ - rebase_path("mac/sectaskaccess_info.plist"), + "mac/sectaskaccess_info.plist", ] ldflags = [ "-sectcreate", "__TEXT", "__info_plist", - inputs[0], + rebase_path(inputs[0], root_build_dir), ] deps = [ diff --git a/util/BUILD.gn b/util/BUILD.gn index e097f2fa..b24feeaa 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -240,6 +240,43 @@ static_library("util") { sources += get_target_outputs(":mig") } + if (is_linux) { + sources += [ + "linux/address_types.h", + "linux/auxiliary_vector.cc", + "linux/auxiliary_vector.h", + "linux/checked_linux_address_range.h", + "linux/direct_ptrace_connection.cc", + "linux/direct_ptrace_connection.h", + "linux/exception_information.h", + "linux/memory_map.cc", + "linux/memory_map.h", + "linux/proc_stat_reader.cc", + "linux/proc_stat_reader.h", + "linux/ptrace_broker.cc", + "linux/ptrace_broker.h", + "linux/ptrace_client.cc", + "linux/ptrace_client.h", + "linux/ptrace_connection.h", + "linux/ptracer.cc", + "linux/ptracer.h", + "linux/scoped_ptrace_attach.cc", + "linux/scoped_ptrace_attach.h", + "linux/thread_info.cc", + "linux/thread_info.h", + "linux/traits.h", + "misc/paths_linux.cc", + "net/http_transport_libcurl.cc", + "posix/process_info_linux.cc", + "process/process_memory_linux.cc", + "process/process_memory_linux.h", + + # TODO: Port to all platforms. + "process/process_memory_range.cc", + "process/process_memory_range.h", + ] + } + if (is_win) { sources += [ "file/directory_reader_win.cc", @@ -349,6 +386,10 @@ static_library("util") { include_dirs += [ "$root_build_dir/gen" ] } + if (is_linux) { + libs = [ "curl" ] + } + if (is_win) { cflags = [ "/wd4201", # nonstandard extension used : nameless struct/union. @@ -449,6 +490,21 @@ source_set("util_test") { ] } + if (is_linux) { + sources += [ + "linux/auxiliary_vector_test.cc", + "linux/memory_map_test.cc", + "linux/proc_stat_reader_test.cc", + "linux/ptrace_broker_test.cc", + "linux/ptracer_test.cc", + "linux/scoped_ptrace_attach_test.cc", + + # TODO: Port to all platforms. + "process/process_memory_range_test.cc", + "process/process_memory_test.cc", + ] + } + if (is_win) { sources += [ "win/capture_context_test.cc", From 9b2ba587f6182830e06b53a9196da449105a3f99 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 19 Dec 2017 12:03:54 -0800 Subject: [PATCH 081/326] linux: Add ExceptionHandlerServer and ExceptionHandlerClient Bug: crashpad:30 Change-Id: I60874a26ccb281144f870df2b4d16c6970a39f6b Reviewed-on: https://chromium-review.googlesource.com/772824 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- compat/android/sys/epoll.cc | 37 ++ compat/android/sys/epoll.h | 50 ++ compat/android/sys/syscall.h | 4 + compat/compat.gyp | 2 + handler/handler.gyp | 23 + handler/handler_test.gyp | 8 + handler/linux/exception_handler_server.cc | 446 ++++++++++++++++++ handler/linux/exception_handler_server.h | 155 ++++++ .../linux/exception_handler_server_test.cc | 307 ++++++++++++ handler/mac/exception_handler_server.h | 4 +- handler/win/crash_report_exception_handler.h | 2 +- snapshot/linux/cpu_context_linux.h | 2 +- snapshot/win/exception_snapshot_win_test.cc | 4 +- test/linux/scoped_pr_set_ptracer.cc | 40 ++ test/linux/scoped_pr_set_ptracer.h | 47 ++ test/test.gyp | 2 + util/linux/exception_handler_client.cc | 162 +++++++ util/linux/exception_handler_client.h | 68 +++ util/linux/exception_handler_protocol.h | 86 ++++ util/linux/ptrace_broker.h | 8 +- util/linux/ptrace_client.cc | 13 +- util/linux/scoped_ptrace_attach_test.cc | 42 +- util/posix/signals.cc | 9 + util/posix/signals.h | 8 + util/util.gyp | 5 +- util/win/exception_handler_server.h | 5 +- util/win/exception_handler_server_test.cc | 2 +- 27 files changed, 1483 insertions(+), 58 deletions(-) create mode 100644 compat/android/sys/epoll.cc create mode 100644 compat/android/sys/epoll.h create mode 100644 handler/linux/exception_handler_server.cc create mode 100644 handler/linux/exception_handler_server.h create mode 100644 handler/linux/exception_handler_server_test.cc create mode 100644 test/linux/scoped_pr_set_ptracer.cc create mode 100644 test/linux/scoped_pr_set_ptracer.h create mode 100644 util/linux/exception_handler_client.cc create mode 100644 util/linux/exception_handler_client.h create mode 100644 util/linux/exception_handler_protocol.h diff --git a/compat/android/sys/epoll.cc b/compat/android/sys/epoll.cc new file mode 100644 index 00000000..64de763f --- /dev/null +++ b/compat/android/sys/epoll.cc @@ -0,0 +1,37 @@ +// 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 + +#include +#include +#include + +#if __ANDROID_API__ < 21 + +extern "C" { + +int epoll_create1(int flags) { + static const auto epoll_create1_p = + reinterpret_cast(dlsym(RTLD_DEFAULT, "epoll_create1")); + if (epoll_create1_p) { + return epoll_create1_p(flags); + } + + return syscall(SYS_epoll_create1, flags); +} + +} // extern "C" + +#endif // __ANDROID_API__ < 21 diff --git a/compat/android/sys/epoll.h b/compat/android/sys/epoll.h new file mode 100644 index 00000000..387813e6 --- /dev/null +++ b/compat/android/sys/epoll.h @@ -0,0 +1,50 @@ +// 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_COMPAT_ANDROID_SYS_EPOLL_H_ +#define CRASHPAD_COMPAT_ANDROID_SYS_EPOLL_H_ + +#include_next + +#include +#include + +// This is missing from traditional headers before API 21. +#if !defined(EPOLLRDHUP) +#define EPOLLRDHUP 0x00002000 +#endif + +// EPOLL_CLOEXEC is undefined in traditional headers before API 21 and removed +// from unified headers at API levels < 21 as a means to indicate that +// epoll_create1 is missing from the C library, but the raw system call should +// still be available. +#if !defined(EPOLL_CLOEXEC) +#define EPOLL_CLOEXEC O_CLOEXEC +#endif + +#if __ANDROID_API__ < 21 + +#ifdef __cplusplus +extern "C" { +#endif + +int epoll_create1(int flags); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __ANDROID_API__ < 21 + +#endif // CRASHPAD_COMPAT_ANDROID_SYS_EPOLL_H_ diff --git a/compat/android/sys/syscall.h b/compat/android/sys/syscall.h index 81fce78a..facce20f 100644 --- a/compat/android/sys/syscall.h +++ b/compat/android/sys/syscall.h @@ -19,6 +19,10 @@ // Android 5.0.0 (API 21) NDK +#if !defined(SYS_epoll_create1) +#define SYS_epoll_create1 __NR_epoll_create1 +#endif + #if !defined(SYS_gettid) #define SYS_gettid __NR_gettid #endif diff --git a/compat/compat.gyp b/compat/compat.gyp index 6bc54848..d3b785b9 100644 --- a/compat/compat.gyp +++ b/compat/compat.gyp @@ -26,6 +26,8 @@ 'android/linux/prctl.h', 'android/linux/ptrace.h', 'android/sched.h', + 'android/sys/epoll.cc', + 'android/sys/epoll.h', 'android/sys/mman.cc', 'android/sys/mman.h', 'android/sys/syscall.h', diff --git a/handler/handler.gyp b/handler/handler.gyp index ff455dee..6c45e1a3 100644 --- a/handler/handler.gyp +++ b/handler/handler.gyp @@ -39,6 +39,8 @@ 'crash_report_upload_thread.h', 'handler_main.cc', 'handler_main.h', + 'linux/exception_handler_server.cc', + 'linux/exception_handler_server.h', 'mac/crash_report_exception_handler.cc', 'mac/crash_report_exception_handler.h', 'mac/exception_handler_server.cc', @@ -54,6 +56,27 @@ 'win/crash_report_exception_handler.cc', 'win/crash_report_exception_handler.h', ], + 'conditions': [ + ['OS=="linux" or OS=="android"', { + 'sources!': [ + 'handler_main.cc', + ], + }], + ['OS=="linux"', { + 'link_settings': { + 'libraries': [ + '-lcap', + ], + }, + }], + ], + 'target_conditions': [ + ['OS=="android"', { + 'sources/': [ + ['include', '^linux/'], + ], + }], + ], }, { 'target_name': 'crashpad_handler', diff --git a/handler/handler_test.gyp b/handler/handler_test.gyp index 4712c055..c047e7c9 100644 --- a/handler/handler_test.gyp +++ b/handler/handler_test.gyp @@ -38,6 +38,7 @@ ], 'sources': [ 'crashpad_handler_test.cc', + 'linux/exception_handler_server_test.cc', 'minidump_to_upload_parameters_test.cc', ], 'conditions': [ @@ -50,6 +51,13 @@ ], }], ], + 'target_conditions': [ + ['OS=="android"', { + 'sources/': [ + ['include', '^linux/'], + ], + }], + ], }, { 'target_name': 'crashpad_handler_test_extended_handler', diff --git a/handler/linux/exception_handler_server.cc b/handler/linux/exception_handler_server.cc new file mode 100644 index 00000000..3369c4f8 --- /dev/null +++ b/handler/linux/exception_handler_server.cc @@ -0,0 +1,446 @@ +// 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 "handler/linux/exception_handler_server.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/string_number_conversions.h" +#include "build/build_config.h" +#include "util/file/file_io.h" +#include "util/file/filesystem.h" +#include "util/misc/as_underlying_type.h" + +namespace crashpad { + +namespace { + +// Log an error for a socket after an EPOLLERR. +void LogSocketError(int sock) { + int err; + socklen_t err_len = sizeof(err); + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &err_len) != 0) { + PLOG(ERROR) << "getsockopt"; + } else { + errno = err; + PLOG(ERROR) << "EPOLLERR"; + } +} + +enum class PtraceScope { + kClassic = 0, + kRestricted, + kAdminOnly, + kNoAttach, + kUnknown +}; + +PtraceScope GetPtraceScope() { + const base::FilePath settings_file("/proc/sys/kernel/yama/ptrace_scope"); + if (!IsRegularFile(base::FilePath(settings_file))) { + return PtraceScope::kClassic; + } + + std::string contents; + if (!LoggingReadEntireFile(settings_file, &contents)) { + return PtraceScope::kUnknown; + } + + if (contents.back() != '\n') { + LOG(ERROR) << "format error"; + return PtraceScope::kUnknown; + } + contents.pop_back(); + + int ptrace_scope; + if (!base::StringToInt(contents, &ptrace_scope)) { + LOG(ERROR) << "format error"; + return PtraceScope::kUnknown; + } + + if (ptrace_scope < static_cast(PtraceScope::kClassic) || + ptrace_scope >= static_cast(PtraceScope::kUnknown)) { + LOG(ERROR) << "invalid ptrace scope"; + return PtraceScope::kUnknown; + } + + return static_cast(ptrace_scope); +} + +bool HaveCapSysPtrace() { + struct __user_cap_header_struct cap_header = {}; + struct __user_cap_data_struct cap_data = {}; + + cap_header.pid = getpid(); + + if (capget(&cap_header, &cap_data) != 0) { + PLOG(ERROR) << "capget"; + return false; + } + + if (cap_header.version != _LINUX_CAPABILITY_VERSION_3) { + LOG(ERROR) << "Unexpected capability version " << std::hex + << cap_header.version; + return false; + } + + return (cap_data.effective & (1 << CAP_SYS_PTRACE)) != 0; +} + +bool SendMessageToClient(int client_sock, ServerToClientMessage::Type type) { + ServerToClientMessage message = {}; + message.type = type; + if (type == ServerToClientMessage::kTypeSetPtracer) { + message.pid = getpid(); + } + return LoggingWriteFile(client_sock, &message, sizeof(message)); +} + +class PtraceStrategyDeciderImpl : public PtraceStrategyDecider { + public: + PtraceStrategyDeciderImpl() : PtraceStrategyDecider() {} + ~PtraceStrategyDeciderImpl() = default; + + Strategy ChooseStrategy(int sock, const ucred& client_credentials) override { + switch (GetPtraceScope()) { + case PtraceScope::kClassic: + return getuid() == client_credentials.uid ? Strategy::kDirectPtrace + : Strategy::kForkBroker; + + case PtraceScope::kRestricted: + if (!SendMessageToClient(sock, + ServerToClientMessage::kTypeSetPtracer)) { + return Strategy::kError; + } + + Errno status; + if (!LoggingReadFileExactly(sock, &status, sizeof(status))) { + return Strategy::kError; + } + + if (status != 0) { + errno = status; + PLOG(ERROR) << "Handler Client SetPtracer"; + return Strategy::kForkBroker; + } + return Strategy::kDirectPtrace; + + case PtraceScope::kAdminOnly: + if (HaveCapSysPtrace()) { + return Strategy::kDirectPtrace; + } + // fallthrough + case PtraceScope::kNoAttach: + LOG(WARNING) << "no ptrace"; + return Strategy::kNoPtrace; + + case PtraceScope::kUnknown: + LOG(WARNING) << "Unknown ptrace scope"; + return Strategy::kError; + } + + DCHECK(false); + } +}; + +} // namespace + +struct ExceptionHandlerServer::Event { + enum class Type { kShutdown, kClientMessage } type; + + ScopedFileHandle fd; +}; + +ExceptionHandlerServer::ExceptionHandlerServer() + : clients_(), + shutdown_event_(), + strategy_decider_(new PtraceStrategyDeciderImpl()), + delegate_(nullptr), + pollfd_(), + keep_running_(true) {} + +ExceptionHandlerServer::~ExceptionHandlerServer() = default; + +void ExceptionHandlerServer::SetPtraceStrategyDecider( + std::unique_ptr decider) { + strategy_decider_ = std::move(decider); +} + +bool ExceptionHandlerServer::InitializeWithClient(ScopedFileHandle sock) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + pollfd_.reset(epoll_create1(EPOLL_CLOEXEC)); + if (!pollfd_.is_valid()) { + PLOG(ERROR) << "epoll_create1"; + return false; + } + + shutdown_event_ = std::make_unique(); + shutdown_event_->type = Event::Type::kShutdown; + shutdown_event_->fd.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + if (!shutdown_event_->fd.is_valid()) { + PLOG(ERROR) << "eventfd"; + return false; + } + + epoll_event poll_event; + poll_event.events = EPOLLIN; + poll_event.data.ptr = shutdown_event_.get(); + if (epoll_ctl(pollfd_.get(), + EPOLL_CTL_ADD, + shutdown_event_->fd.get(), + &poll_event) != 0) { + PLOG(ERROR) << "epoll_ctl"; + return false; + } + + if (!InstallClientSocket(std::move(sock))) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +void ExceptionHandlerServer::Run(Delegate* delegate) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + delegate_ = delegate; + + while (keep_running_ && clients_.size() > 0) { + epoll_event poll_event; + int res = HANDLE_EINTR(epoll_wait(pollfd_.get(), &poll_event, 1, -1)); + if (res < 0) { + PLOG(ERROR) << "epoll_wait"; + return; + } + DCHECK_EQ(res, 1); + + Event* eventp = reinterpret_cast(poll_event.data.ptr); + if (eventp->type == Event::Type::kShutdown) { + if (poll_event.events & EPOLLERR) { + LogSocketError(eventp->fd.get()); + } + keep_running_ = false; + } else { + HandleEvent(eventp, poll_event.events); + } + } +} + +void ExceptionHandlerServer::Stop() { + keep_running_ = false; + if (shutdown_event_ && shutdown_event_->fd.is_valid()) { + uint64_t value = 1; + LoggingWriteFile(shutdown_event_->fd.get(), &value, sizeof(value)); + } +} + +void ExceptionHandlerServer::HandleEvent(Event* event, uint32_t event_type) { + DCHECK_EQ(AsUnderlyingType(event->type), + AsUnderlyingType(Event::Type::kClientMessage)); + + if (event_type & EPOLLERR) { + LogSocketError(event->fd.get()); + UninstallClientSocket(event); + return; + } + + if (event_type & EPOLLIN) { + if (!ReceiveClientMessage(event)) { + UninstallClientSocket(event); + } + return; + } + + if (event_type & EPOLLHUP || event_type & EPOLLRDHUP) { + UninstallClientSocket(event); + return; + } + + LOG(ERROR) << "Unexpected event 0x" << std::hex << event_type; + return; +} + +bool ExceptionHandlerServer::InstallClientSocket(ScopedFileHandle socket) { + int optval = 1; + socklen_t optlen = sizeof(optval); + if (setsockopt(socket.get(), SOL_SOCKET, SO_PASSCRED, &optval, optlen) != 0) { + PLOG(ERROR) << "setsockopt"; + return false; + } + + auto event = std::make_unique(); + event->type = Event::Type::kClientMessage; + event->fd.reset(socket.release()); + + Event* eventp = event.get(); + + if (!clients_.insert(std::make_pair(event->fd.get(), std::move(event))) + .second) { + LOG(ERROR) << "duplicate descriptor"; + return false; + } + + epoll_event poll_event; + poll_event.events = EPOLLIN | EPOLLRDHUP; + poll_event.data.ptr = eventp; + + if (epoll_ctl(pollfd_.get(), EPOLL_CTL_ADD, eventp->fd.get(), &poll_event) != + 0) { + PLOG(ERROR) << "epoll_ctl"; + clients_.erase(eventp->fd.get()); + return false; + } + + return true; +} + +bool ExceptionHandlerServer::UninstallClientSocket(Event* event) { + if (epoll_ctl(pollfd_.get(), EPOLL_CTL_DEL, event->fd.get(), nullptr) != 0) { + PLOG(ERROR) << "epoll_ctl"; + return false; + } + + if (clients_.erase(event->fd.get()) != 1) { + LOG(ERROR) << "event not found"; + return false; + } + + return true; +} + +bool ExceptionHandlerServer::ReceiveClientMessage(Event* event) { + ClientToServerMessage message; + iovec iov; + iov.iov_base = &message; + iov.iov_len = sizeof(message); + + msghdr msg; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + char cmsg_buf[CMSG_SPACE(sizeof(ucred))]; + msg.msg_control = cmsg_buf; + msg.msg_controllen = sizeof(cmsg_buf); + msg.msg_flags = 0; + + int res = recvmsg(event->fd.get(), &msg, 0); + if (res < 0) { + PLOG(ERROR) << "recvmsg"; + return false; + } + if (res == 0) { + // The client had an orderly shutdown. + return false; + } + + if (msg.msg_name != nullptr || msg.msg_namelen != 0) { + LOG(ERROR) << "unexpected msg name"; + return false; + } + + if (msg.msg_iovlen != 1) { + LOG(ERROR) << "unexpected iovlen"; + return false; + } + + if (msg.msg_iov[0].iov_len != sizeof(ClientToServerMessage)) { + LOG(ERROR) << "unexpected message size " << msg.msg_iov[0].iov_len; + return false; + } + auto client_msg = + reinterpret_cast(msg.msg_iov[0].iov_base); + + switch (client_msg->type) { + case ClientToServerMessage::kCrashDumpRequest: + return HandleCrashDumpRequest( + msg, client_msg->client_info, event->fd.get()); + } + + DCHECK(false); + LOG(ERROR) << "Unknown message type"; + return false; +} + +bool ExceptionHandlerServer::HandleCrashDumpRequest( + const msghdr& msg, + const ClientInformation& client_info, + int client_sock) { + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg == nullptr) { + LOG(ERROR) << "missing credentials"; + return false; + } + + if (cmsg->cmsg_level != SOL_SOCKET) { + LOG(ERROR) << "unexpected cmsg_level " << cmsg->cmsg_level; + return false; + } + + if (cmsg->cmsg_type != SCM_CREDENTIALS) { + LOG(ERROR) << "unexpected cmsg_type " << cmsg->cmsg_type; + return false; + } + + if (cmsg->cmsg_len != CMSG_LEN(sizeof(ucred))) { + LOG(ERROR) << "unexpected cmsg_len " << cmsg->cmsg_len; + return false; + } + + ucred* client_credentials = reinterpret_cast(CMSG_DATA(cmsg)); + pid_t client_process_id = client_credentials->pid; + + switch (strategy_decider_->ChooseStrategy(client_sock, *client_credentials)) { + case PtraceStrategyDecider::Strategy::kError: + return false; + + case PtraceStrategyDecider::Strategy::kNoPtrace: + return SendMessageToClient(client_sock, + ServerToClientMessage::kTypeCrashDumpFailed); + + case PtraceStrategyDecider::Strategy::kDirectPtrace: + delegate_->HandleException(client_process_id, + client_info.exception_information_address); + break; + + case PtraceStrategyDecider::Strategy::kForkBroker: + if (!SendMessageToClient(client_sock, + ServerToClientMessage::kTypeForkBroker)) { + return false; + } + + delegate_->HandleExceptionWithBroker( + client_process_id, + client_info.exception_information_address, + client_sock); + break; + } + + return SendMessageToClient(client_sock, + ServerToClientMessage::kTypeCrashDumpComplete); +} + +} // namespace crashpad diff --git a/handler/linux/exception_handler_server.h b/handler/linux/exception_handler_server.h new file mode 100644 index 00000000..fcafb887 --- /dev/null +++ b/handler/linux/exception_handler_server.h @@ -0,0 +1,155 @@ +// 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_HANDLER_LINUX_EXCEPTION_HANDLER_SERVER_H_ +#define CRASHPAD_HANDLER_LINUX_EXCEPTION_HANDLER_SERVER_H_ + +#include +#include + +#include +#include + +#include "base/macros.h" +#include "util/file/file_io.h" +#include "util/linux/exception_handler_protocol.h" +#include "util/misc/address_types.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +//! \brief Abstract base class for deciding how the handler should `ptrace` a +//! client. +class PtraceStrategyDecider { + public: + virtual ~PtraceStrategyDecider() = default; + + //! \brief The possible return values for ChooseStrategy(). + enum class Strategy { + //! \brief An error occurred, with a message logged. + kError, + + //! \brief Ptrace cannot be used. + kNoPtrace, + + //! \brief The handler should `ptrace`-attach the client directly. + kDirectPtrace, + + //! \brief The client should `fork` a PtraceBroker for the handler. + kForkBroker, + }; + + //! \brief Chooses an appropriate `ptrace` strategy. + //! + //! \param[in] sock A socket conncted to a ExceptionHandlerClient. + //! \param[in] client_credentials The credentials for the connected client. + //! \return the chosen #Strategy. + virtual Strategy ChooseStrategy(int sock, + const ucred& client_credentials) = 0; + + protected: + PtraceStrategyDecider() = default; +}; + +//! \brief Runs the main exception-handling server in Crashpad’s handler +//! process. +class ExceptionHandlerServer { + public: + class Delegate { + public: + //! \brief Called on receipt of a crash dump request from a client. + //! + //! \param[in] client_process_id The process ID of the crashing client. + //! \param[in] exception_information_address The address in the client's + //! address space of an ExceptionInformation struct. + //! \return `true` on success. `false` on failure with a message logged. + virtual bool HandleException(pid_t client_process_id, + VMAddress exception_information_address) = 0; + + //! \brief Called on the receipt of a crash dump request from a client for a + //! crash that should be mediated by a PtraceBroker. + //! + //! \param[in] client_process_id The process ID of the crashing client. + //! \param[in] exception_information_address The address in the client's + //! address space of an ExceptionInformation struct. + //! \param[in] broker_sock A socket connected to the PtraceBroker. + //! \return `true` on success. `false` on failure with a message logged. + virtual bool HandleExceptionWithBroker( + pid_t client_process_id, + VMAddress exception_information_address, + int broker_sock) = 0; + + protected: + ~Delegate() {} + }; + + ExceptionHandlerServer(); + ~ExceptionHandlerServer(); + + //! \brief Sets the handler's PtraceStrategyDecider. + //! + //! If this method is not called, a default PtraceStrategyDecider will be + //! used. + void SetPtraceStrategyDecider(std::unique_ptr decider); + + //! \brief Initializes this object. + //! + //! This method must be successfully called before Run(). + //! + //! \param[in] sock A socket on which to receive client requests. + //! \return `true` on success. `false` on failure with a message logged. + bool InitializeWithClient(ScopedFileHandle sock); + + //! \brief Runs the exception-handling server. + //! + //! This method must only be called once on an ExceptionHandlerServer object. + //! This method returns when there are no more client connections or Stop() + //! has been called. + //! + //! \param[in] delegate An object to send exceptions to. + void Run(Delegate* delegate); + + //! \brief Stops a running exception-handling server. + //! + //! Stop() may be called at any time, and may be called from a signal handler. + //! If Stop() is called before Run() it will cause Run() to return as soon as + //! it is called. It is harmless to call Stop() after Run() has already + //! returned, or to call Stop() after it has already been called. + void Stop(); + + private: + struct Event; + + void HandleEvent(Event* event, uint32_t event_type); + bool InstallClientSocket(ScopedFileHandle socket); + bool UninstallClientSocket(Event* event); + bool ReceiveClientMessage(Event* event); + bool HandleCrashDumpRequest(const msghdr& msg, + const ClientInformation& client_info, + int client_sock); + + std::unordered_map> clients_; + std::unique_ptr shutdown_event_; + std::unique_ptr strategy_decider_; + Delegate* delegate_; + ScopedFileHandle pollfd_; + bool keep_running_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer); +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_LINUX_EXCEPTION_HANDLER_SERVER_H_ diff --git a/handler/linux/exception_handler_server_test.cc b/handler/linux/exception_handler_server_test.cc new file mode 100644 index 00000000..fb6c21a9 --- /dev/null +++ b/handler/linux/exception_handler_server_test.cc @@ -0,0 +1,307 @@ +// 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 "handler/linux/exception_handler_server.h" + +#include +#include + +#include "base/logging.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/linux/scoped_pr_set_ptracer.h" +#include "test/multiprocess.h" +#include "util/linux/direct_ptrace_connection.h" +#include "util/linux/exception_handler_client.h" +#include "util/linux/ptrace_client.h" +#include "util/synchronization/semaphore.h" +#include "util/thread/thread.h" + +namespace crashpad { +namespace test { +namespace { + +// Runs the ExceptionHandlerServer on a background thread. +class RunServerThread : public Thread { + public: + RunServerThread(ExceptionHandlerServer* server, + ExceptionHandlerServer::Delegate* delegate) + : server_(server), delegate_(delegate), join_sem_(0) {} + + ~RunServerThread() override {} + + bool JoinWithTimeout(double timeout) { + if (!join_sem_.TimedWait(timeout)) { + return false; + } + Join(); + return true; + } + + private: + // Thread: + void ThreadMain() override { + server_->Run(delegate_); + join_sem_.Signal(); + } + + ExceptionHandlerServer* server_; + ExceptionHandlerServer::Delegate* delegate_; + Semaphore join_sem_; + + DISALLOW_COPY_AND_ASSIGN(RunServerThread); +}; + +class ScopedStopServerAndJoinThread { + public: + ScopedStopServerAndJoinThread(ExceptionHandlerServer* server, + RunServerThread* thread) + : server_(server), thread_(thread) {} + + ~ScopedStopServerAndJoinThread() { + server_->Stop(); + EXPECT_TRUE(thread_->JoinWithTimeout(5.0)); + } + + private: + ExceptionHandlerServer* server_; + RunServerThread* thread_; + + DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread); +}; + +class TestDelegate : public ExceptionHandlerServer::Delegate { + public: + TestDelegate() + : Delegate(), last_exception_address_(0), last_client_(-1), sem_(0) {} + + ~TestDelegate() {} + + bool WaitForException(double timeout_seconds, + pid_t* last_client, + VMAddress* last_address) { + if (sem_.TimedWait(timeout_seconds)) { + *last_client = last_client_; + *last_address = last_exception_address_; + return true; + } + + return false; + } + + bool HandleException(pid_t client_process_id, + VMAddress exception_information_address) override { + DirectPtraceConnection connection; + bool connected = connection.Initialize(client_process_id); + EXPECT_TRUE(connected); + + last_exception_address_ = exception_information_address; + last_client_ = client_process_id; + sem_.Signal(); + return connected; + } + + bool HandleExceptionWithBroker(pid_t client_process_id, + VMAddress exception_information_address, + int broker_sock) override { + PtraceClient client; + bool connected = client.Initialize(broker_sock, client_process_id); + EXPECT_TRUE(connected); + + last_exception_address_ = exception_information_address, + last_client_ = client_process_id; + sem_.Signal(); + return connected; + } + + private: + VMAddress last_exception_address_; + pid_t last_client_; + Semaphore sem_; + + DISALLOW_COPY_AND_ASSIGN(TestDelegate); +}; + +class MockPtraceStrategyDecider : public PtraceStrategyDecider { + public: + MockPtraceStrategyDecider(PtraceStrategyDecider::Strategy strategy) + : PtraceStrategyDecider(), strategy_(strategy) {} + + ~MockPtraceStrategyDecider() {} + + Strategy ChooseStrategy(int sock, const ucred& client_credentials) override { + return strategy_; + } + + private: + Strategy strategy_; + + DISALLOW_COPY_AND_ASSIGN(MockPtraceStrategyDecider); +}; + +class ExceptionHandlerServerTest : public testing::Test { + public: + ExceptionHandlerServerTest() + : server_(), + delegate_(), + server_thread_(&server_, &delegate_), + sock_to_handler_() {} + + ~ExceptionHandlerServerTest() = default; + + int SockToHandler() { return sock_to_handler_.get(); } + + TestDelegate* Delegate() { return &delegate_; } + + void Hangup() { sock_to_handler_.reset(); } + + RunServerThread* ServerThread() { return &server_thread_; } + + ExceptionHandlerServer* Server() { return &server_; } + + class CrashDumpTest : public Multiprocess { + public: + CrashDumpTest(ExceptionHandlerServerTest* server_test, bool succeeds) + : Multiprocess(), server_test_(server_test), succeeds_(succeeds) {} + + ~CrashDumpTest() = default; + + void MultiprocessParent() override { + ClientInformation info; + ASSERT_TRUE( + LoggingReadFileExactly(ReadPipeHandle(), &info, sizeof(info))); + + if (succeeds_) { + VMAddress last_address; + pid_t last_client; + ASSERT_TRUE(server_test_->Delegate()->WaitForException( + 5.0, &last_client, &last_address)); + EXPECT_EQ(last_address, info.exception_information_address); + EXPECT_EQ(last_client, ChildPID()); + } else { + CheckedReadFileAtEOF(ReadPipeHandle()); + } + } + + void MultiprocessChild() override { + ASSERT_EQ(close(server_test_->sock_to_client_), 0); + + ClientInformation info; + info.exception_information_address = 42; + + ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &info, sizeof(info))); + + // If the current ptrace_scope is restricted, the broker needs to be set + // as the ptracer for this process. Setting this process as its own + // ptracer allows the broker to inherit this condition. + ScopedPrSetPtracer set_ptracer(getpid()); + + ExceptionHandlerClient client(server_test_->SockToHandler()); + ASSERT_EQ(client.RequestCrashDump(info), 0); + } + + private: + ExceptionHandlerServerTest* server_test_; + bool succeeds_; + + DISALLOW_COPY_AND_ASSIGN(CrashDumpTest); + }; + + void ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy strategy, + bool succeeds) { + ScopedStopServerAndJoinThread stop_server(Server(), ServerThread()); + ServerThread()->Start(); + + Server()->SetPtraceStrategyDecider( + std::make_unique(strategy)); + + CrashDumpTest test(this, succeeds); + test.Run(); + } + + protected: + void SetUp() override { + int socks[2]; + ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, socks), 0); + sock_to_handler_.reset(socks[0]); + sock_to_client_ = socks[1]; + + ASSERT_TRUE(server_.InitializeWithClient(ScopedFileHandle(socks[1]))); + } + + private: + ExceptionHandlerServer server_; + TestDelegate delegate_; + RunServerThread server_thread_; + ScopedFileHandle sock_to_handler_; + int sock_to_client_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServerTest); +}; + +TEST_F(ExceptionHandlerServerTest, ShutdownWithNoClients) { + ServerThread()->Start(); + Hangup(); + ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0)); +} + +TEST_F(ExceptionHandlerServerTest, StopWithClients) { + ServerThread()->Start(); + Server()->Stop(); + ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0)); +} + +TEST_F(ExceptionHandlerServerTest, StopBeforeRun) { + Server()->Stop(); + ServerThread()->Start(); + ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0)); +} + +TEST_F(ExceptionHandlerServerTest, MultipleStops) { + ServerThread()->Start(); + Server()->Stop(); + Server()->Stop(); + ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0)); +} + +TEST_F(ExceptionHandlerServerTest, RequestCrashDumpDefault) { + ScopedStopServerAndJoinThread stop_server(Server(), ServerThread()); + ServerThread()->Start(); + + CrashDumpTest test(this, true); + test.Run(); +} + +TEST_F(ExceptionHandlerServerTest, RequestCrashDumpNoPtrace) { + ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kNoPtrace, + false); +} + +TEST_F(ExceptionHandlerServerTest, RequestCrashDumpForkBroker) { + ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kForkBroker, + true); +} + +TEST_F(ExceptionHandlerServerTest, RequestCrashDumpDirectPtrace) { + ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kDirectPtrace, + true); +} + +TEST_F(ExceptionHandlerServerTest, RequestCrashDumpError) { + ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kError, false); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/handler/mac/exception_handler_server.h b/handler/mac/exception_handler_server.h index 7d893002..272cf762 100644 --- a/handler/mac/exception_handler_server.h +++ b/handler/mac/exception_handler_server.h @@ -63,9 +63,7 @@ class ExceptionHandlerServer { //! \brief Stops a running exception-handling server. //! - //! The normal mode of operation is to call Stop() while Run() is running. It - //! is expected that Stop() would be called from a signal handler. - //! + //! Stop() may be called at any time, and may be called from a signal handler. //! If Stop() is called before Run() it will cause Run() to return as soon as //! it is called. It is harmless to call Stop() after Run() has already //! returned, or to call Stop() after it has already been called. diff --git a/handler/win/crash_report_exception_handler.h b/handler/win/crash_report_exception_handler.h index 54dfa971..e1fb725d 100644 --- a/handler/win/crash_report_exception_handler.h +++ b/handler/win/crash_report_exception_handler.h @@ -58,7 +58,7 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { const std::map* process_annotations, const UserStreamDataSources* user_stream_data_sources); - ~CrashReportExceptionHandler() override; + ~CrashReportExceptionHandler(); // ExceptionHandlerServer::Delegate: diff --git a/snapshot/linux/cpu_context_linux.h b/snapshot/linux/cpu_context_linux.h index 8a2e812c..092762c2 100644 --- a/snapshot/linux/cpu_context_linux.h +++ b/snapshot/linux/cpu_context_linux.h @@ -53,7 +53,7 @@ void InitializeCPUContextX86_NoFloatingPoint( const SignalThreadContext32& thread_context, CPUContextX86* context); -// \{ +//! \{ //! \brief Initializes a CPUContextX86_64 structure from native context //! structures on Linux. //! diff --git a/snapshot/win/exception_snapshot_win_test.cc b/snapshot/win/exception_snapshot_win_test.cc index 843ad263..a1ab8c65 100644 --- a/snapshot/win/exception_snapshot_win_test.cc +++ b/snapshot/win/exception_snapshot_win_test.cc @@ -79,7 +79,7 @@ class CrashingDelegate : public ExceptionHandlerServer::Delegate { : server_ready_(server_ready), completed_test_event_(completed_test_event), break_near_(0) {} - ~CrashingDelegate() override {} + ~CrashingDelegate() {} void set_break_near(WinVMAddress break_near) { break_near_ = break_near; } @@ -183,7 +183,7 @@ class SimulateDelegate : public ExceptionHandlerServer::Delegate { : server_ready_(server_ready), completed_test_event_(completed_test_event), dump_near_(0) {} - ~SimulateDelegate() override {} + ~SimulateDelegate() {} void set_dump_near(WinVMAddress dump_near) { dump_near_ = dump_near; } diff --git a/test/linux/scoped_pr_set_ptracer.cc b/test/linux/scoped_pr_set_ptracer.cc new file mode 100644 index 00000000..bc9695ae --- /dev/null +++ b/test/linux/scoped_pr_set_ptracer.cc @@ -0,0 +1,40 @@ +// 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 "test/linux/scoped_pr_set_ptracer.h" + +#include +#include + +#include "gtest/gtest.h" +#include "test/errors.h" + +namespace crashpad { +namespace test { + +ScopedPrSetPtracer::ScopedPrSetPtracer(pid_t pid) { + success_ = prctl(PR_SET_PTRACER, pid, 0, 0, 0) == 0; + if (!success_) { + EXPECT_EQ(errno, EINVAL) << ErrnoMessage("prctl"); + } +} + +ScopedPrSetPtracer::~ScopedPrSetPtracer() { + if (success_) { + EXPECT_EQ(prctl(PR_SET_PTRACER, 0, 0, 0, 0), 0) << ErrnoMessage("prctl"); + } +} + +} // namespace test +} // namespace crashpad diff --git a/test/linux/scoped_pr_set_ptracer.h b/test/linux/scoped_pr_set_ptracer.h new file mode 100644 index 00000000..df9cff78 --- /dev/null +++ b/test/linux/scoped_pr_set_ptracer.h @@ -0,0 +1,47 @@ +// 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_TEST_LINUX_SCOPED_PR_SET_PTRACER_H_ +#define CRASHPAD_TEST_LINUX_SCOPED_PR_SET_PTRACER_H_ + +#include + +#include "base/macros.h" + +namespace crashpad { +namespace test { + +class ScopedPrSetPtracer { + public: + //! \brief Uses `PR_SET_PTRACER` to set \a pid as the caller's ptracer or + //! expects `EINVAL`. + //! + //! `PR_SET_PTRACER` is only supported if the Yama Linux security module (LSM) + //! is enabled. Otherwise, `prctl(PR_SET_PTRACER, ...)` fails with `EINVAL`. + //! See linux-4.9.20/security/yama/yama_lsm.c yama_task_prctl() and + //! linux-4.9.20/kernel/sys.c [sys_]prctl(). + explicit ScopedPrSetPtracer(pid_t pid); + + ~ScopedPrSetPtracer(); + + private: + bool success_; + + DISALLOW_COPY_AND_ASSIGN(ScopedPrSetPtracer); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_LINUX_SCOPED_PR_SET_PTRACER_H_ diff --git a/test/test.gyp b/test/test.gyp index ce2ba7d5..ad88e036 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -45,6 +45,8 @@ 'linux/fake_ptrace_connection.h', 'linux/get_tls.cc', 'linux/get_tls.h', + 'linux/scoped_pr_set_ptracer.cc', + 'linux/scoped_pr_set_ptracer.h', 'mac/dyld.cc', 'mac/dyld.h', 'mac/exception_swallower.cc', diff --git a/util/linux/exception_handler_client.cc b/util/linux/exception_handler_client.cc new file mode 100644 index 00000000..d6d22338 --- /dev/null +++ b/util/linux/exception_handler_client.cc @@ -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 "util/linux/exception_handler_client.h" + +#include +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "util/file/file_io.h" +#include "util/linux/ptrace_broker.h" +#include "util/posix/signals.h" + +namespace crashpad { + +ExceptionHandlerClient::ExceptionHandlerClient(int sock) + : server_sock_(sock), ptracer_(-1), can_set_ptracer_(true) {} + +ExceptionHandlerClient::~ExceptionHandlerClient() = default; + +int ExceptionHandlerClient::RequestCrashDump(const ClientInformation& info) { + int status = SendCrashDumpRequest(info); + if (status != 0) { + return status; + } + return WaitForCrashDumpComplete(); +} + +int ExceptionHandlerClient::SetPtracer(pid_t pid) { + if (ptracer_ == pid) { + return 0; + } + + if (!can_set_ptracer_) { + return EPERM; + } + + if (prctl(PR_SET_PTRACER, pid, 0, 0, 0) == 0) { + return 0; + } + return errno; +} + +void ExceptionHandlerClient::SetCanSetPtracer(bool can_set_ptracer) { + can_set_ptracer_ = can_set_ptracer; +} + +int ExceptionHandlerClient::SendCrashDumpRequest( + const ClientInformation& info) { + ClientToServerMessage message; + message.type = ClientToServerMessage::kCrashDumpRequest; + message.client_info = info; + + iovec iov; + iov.iov_base = &message; + iov.iov_len = sizeof(message); + + msghdr msg; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ucred creds; + creds.pid = getpid(); + creds.uid = geteuid(); + creds.gid = getegid(); + + char cmsg_buf[CMSG_SPACE(sizeof(creds))]; + msg.msg_control = cmsg_buf; + msg.msg_controllen = sizeof(cmsg_buf); + + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(creds)); + *reinterpret_cast(CMSG_DATA(cmsg)) = creds; + + if (sendmsg(server_sock_, &msg, MSG_NOSIGNAL) < 0) { + PLOG(ERROR) << "sendmsg"; + return errno; + } + + return 0; +} + +int ExceptionHandlerClient::WaitForCrashDumpComplete() { + ServerToClientMessage message; + + // If the server hangs up, ReadFileExactly will return false without setting + // errno. + errno = 0; + while (ReadFileExactly(server_sock_, &message, sizeof(message))) { + switch (message.type) { + case ServerToClientMessage::kTypeForkBroker: { + Signals::InstallDefaultHandler(SIGCHLD); + + pid_t pid = fork(); + if (pid < 0) { + Errno error = errno; + if (!WriteFile(server_sock_, &error, sizeof(error))) { + return errno; + } + continue; + } + + if (pid == 0) { +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif // ARCH_CPU_64_BITS + + PtraceBroker broker(server_sock_, am_64_bit); + _exit(broker.Run()); + } + + int status = 0; + pid_t child = HANDLE_EINTR(waitpid(pid, &status, 0)); + DCHECK_EQ(child, pid); + + if (child == pid && status != 0) { + return status; + } + continue; + } + + case ServerToClientMessage::kTypeSetPtracer: { + Errno result = SetPtracer(message.pid); + if (!WriteFile(server_sock_, &result, sizeof(result))) { + return errno; + } + continue; + } + + case ServerToClientMessage::kTypeCrashDumpComplete: + case ServerToClientMessage::kTypeCrashDumpFailed: + return 0; + } + + DCHECK(false); + } + + return errno; +} + +} // namespace crashpad diff --git a/util/linux/exception_handler_client.h b/util/linux/exception_handler_client.h new file mode 100644 index 00000000..a60b0656 --- /dev/null +++ b/util/linux/exception_handler_client.h @@ -0,0 +1,68 @@ +// 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_UTIL_LINUX_EXCEPTION_HANDLER_CLIENT_H_ +#define CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_CLIENT_H_ + +#include + +#include "base/macros.h" +#include "util/linux/exception_handler_protocol.h" + +namespace crashpad { + +//! A client for an ExceptionHandlerServer +class ExceptionHandlerClient { + public: + //! \brief Constructs this object. + //! + //! \param[in] sock A socket connected to an ExceptionHandlerServer. + explicit ExceptionHandlerClient(int sock); + + ~ExceptionHandlerClient(); + + //! \brief Request a crash dump from the ExceptionHandlerServer. + //! + //! This method blocks until the crash dump is complete. + //! + //! \param[in] info Information about this client. + //! \return 0 on success or an error code on failure. + int RequestCrashDump(const ClientInformation& info); + + //! \brief Uses `prctl(PR_SET_PTRACER, ...)` to set the process with + //! process ID \a pid as the ptracer for this process. + //! + //! \param[in] pid The process ID of the process to be set as this process' + //! ptracer. + //! \return 0 on success or an error code on failure. + int SetPtracer(pid_t pid); + + //! \brief Enables or disables SetPtracer(). + //! \param[in] can_set_ptracer Whether SetPtracer should be enabled. + void SetCanSetPtracer(bool can_set_ptracer); + + private: + int SendCrashDumpRequest(const ClientInformation& info); + int WaitForCrashDumpComplete(); + + int server_sock_; + pid_t ptracer_; + bool can_set_ptracer_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerClient); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_CLIENT_H_ diff --git a/util/linux/exception_handler_protocol.h b/util/linux/exception_handler_protocol.h new file mode 100644 index 00000000..3f71abf7 --- /dev/null +++ b/util/linux/exception_handler_protocol.h @@ -0,0 +1,86 @@ +// 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_UTIL_LINUX_EXCEPTION_HANDLER_PROTOCOL_H_ +#define CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_PROTOCOL_H_ + +#include +#include +#include + +#include "util/file/file_io.h" +#include "util/misc/address_types.h" + +namespace crashpad { + +#pragma pack(push, 1) + +//! \brief The type used for error reporting. +using Errno = int32_t; +static_assert(sizeof(Errno) >= sizeof(errno), "Errno type is too small"); + +//! \brief A boolean status suitable for communication between processes. +enum Bool : char { kBoolFalse, kBoolTrue }; + +//! \brief Information about a client registered with an ExceptionHandlerServer. +struct ClientInformation { + //! \brief The address in the client's address space of an + //! ExceptionInformation struct. + VMAddress exception_information_address; +}; + +//! \brief The message passed from client to server. +struct ClientToServerMessage { + static constexpr int32_t kVersion = 1; + + //! \brief Indicates what message version is being used. + int32_t version = kVersion; + + enum Type : uint32_t { + //! \brief Used to request a crash dump for the sending client. + kCrashDumpRequest + } type; + + union { + //! \brief Valid for type == kCrashDumpRequest + ClientInformation client_info; + }; +}; + +//! \brief The message passed from server to client. +struct ServerToClientMessage { + enum Type : uint32_t { + //! \brief Indicates that the client should fork a PtraceBroker process. + kTypeForkBroker, + + //! \brief Inidicates that the client should set allow the handler to trace + //! it using PR_SET_PTRACER. + kTypeSetPtracer, + + //! \brief Indicates that the handler has completed a requested crash dump. + kTypeCrashDumpComplete, + + //! \brief Indicicates that the handler was unable to produce a crash dump. + kTypeCrashDumpFailed + } type; + + //! \brief The handler's process ID. Valid for kTypeSetPtracer. + pid_t pid; +}; + +#pragma pack(pop) + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_PROTOCOL_H_ diff --git a/util/linux/ptrace_broker.h b/util/linux/ptrace_broker.h index d725cb7b..96cbee58 100644 --- a/util/linux/ptrace_broker.h +++ b/util/linux/ptrace_broker.h @@ -20,6 +20,7 @@ #include #include "base/macros.h" +#include "util/linux/exception_handler_protocol.h" #include "util/linux/ptrace_connection.h" #include "util/linux/ptracer.h" #include "util/linux/scoped_ptrace_attach.h" @@ -87,13 +88,6 @@ class PtraceBroker { } iov; }; - //! \brief The type used for error reporting. - using Errno = int32_t; - static_assert(sizeof(Errno) >= sizeof(errno), "Errno type is too small"); - - //! \brief A boolean status suitable for communication between processes. - enum Bool : char { kBoolFalse, kBoolTrue }; - //! \brief The response sent for a Request with type kTypeGetThreadInfo. struct GetThreadInfoResponse { //! \brief Information about the specified thread. Only valid if #success diff --git a/util/linux/ptrace_client.cc b/util/linux/ptrace_client.cc index dabda12e..3cdfcbe2 100644 --- a/util/linux/ptrace_client.cc +++ b/util/linux/ptrace_client.cc @@ -27,7 +27,7 @@ namespace crashpad { namespace { bool ReceiveAndLogError(int sock, const std::string& operation) { - PtraceBroker::Errno error; + Errno error; if (!LoggingReadFileExactly(sock, &error, sizeof(error))) { return false; } @@ -44,13 +44,14 @@ bool AttachImpl(int sock, pid_t tid) { return false; } - PtraceBroker::Bool success; + Bool success; if (!LoggingReadFileExactly(sock, &success, sizeof(success))) { return false; } - if (success != PtraceBroker::kBoolTrue) { + if (success != kBoolTrue) { ReceiveAndLogError(sock, "PtraceBroker Attach"); + return false; } return true; @@ -90,11 +91,11 @@ bool PtraceClient::Initialize(int sock, pid_t pid) { return false; } - PtraceBroker::Bool is_64_bit; + Bool is_64_bit; if (!LoggingReadFileExactly(sock_, &is_64_bit, sizeof(is_64_bit))) { return false; } - is_64_bit_ = is_64_bit == PtraceBroker::kBoolTrue; + is_64_bit_ = is_64_bit == kBoolTrue; INITIALIZATION_STATE_SET_VALID(initialized_); return true; @@ -130,7 +131,7 @@ bool PtraceClient::GetThreadInfo(pid_t tid, ThreadInfo* info) { return false; } - if (response.success == PtraceBroker::kBoolTrue) { + if (response.success == kBoolTrue) { *info = response.info; return true; } diff --git a/util/linux/scoped_ptrace_attach_test.cc b/util/linux/scoped_ptrace_attach_test.cc index 99072e4c..78552e77 100644 --- a/util/linux/scoped_ptrace_attach_test.cc +++ b/util/linux/scoped_ptrace_attach_test.cc @@ -15,12 +15,12 @@ #include "util/linux/scoped_ptrace_attach.h" #include -#include #include #include #include "gtest/gtest.h" #include "test/errors.h" +#include "test/linux/scoped_pr_set_ptracer.h" #include "test/multiprocess.h" #include "util/file/file_io.h" @@ -28,39 +28,13 @@ namespace crashpad { namespace test { namespace { -class ScopedPrSetPtracer { - public: - explicit ScopedPrSetPtracer(pid_t pid) { - // PR_SET_PTRACER is only supported if the Yama Linux security module (LSM) - // is enabled. Otherwise, this prctl() call fails with EINVAL. See - // linux-4.9.20/security/yama/yama_lsm.c yama_task_prctl() and - // linux-4.9.20/kernel/sys.c [sys_]prctl(). - // - // If Yama is not enabled, the default ptrace restrictions should be - // sufficient for these tests. - // - // If Yama is enabled, then /proc/sys/kernel/yama/ptrace_scope must be 0 - // (YAMA_SCOPE_DISABLED, in which case this prctl() is not necessary) or 1 - // (YAMA_SCOPE_RELATIONAL) for these tests to succeed. If it is 2 - // (YAMA_SCOPE_CAPABILITY) then the test requires CAP_SYS_PTRACE, and if it - // is 3 (YAMA_SCOPE_NO_ATTACH), these tests will fail. - success_ = prctl(PR_SET_PTRACER, pid, 0, 0, 0) == 0; - if (!success_) { - EXPECT_EQ(errno, EINVAL) << ErrnoMessage("prctl"); - } - } - - ~ScopedPrSetPtracer() { - if (success_) { - EXPECT_EQ(prctl(PR_SET_PTRACER, 0, 0, 0, 0), 0) << ErrnoMessage("prctl"); - } - } - - private: - bool success_; - - DISALLOW_COPY_AND_ASSIGN(ScopedPrSetPtracer); -}; +// If Yama is not enabled, the default ptrace restrictions should be +// sufficient for these tests. +// +// If Yama is enabled, then /proc/sys/kernel/yama/ptrace_scope must be 0 +// (YAMA_SCOPE_DISABLED) or 1 (YAMA_SCOPE_RELATIONAL) for these tests to +// succeed. If it is 2 (YAMA_SCOPE_CAPABILITY) then the test requires +// CAP_SYS_PTRACE, and if it is 3 (YAMA_SCOPE_NO_ATTACH), these tests will fail. class AttachTest : public Multiprocess { public: diff --git a/util/posix/signals.cc b/util/posix/signals.cc index d79d53ab..63764ab8 100644 --- a/util/posix/signals.cc +++ b/util/posix/signals.cc @@ -138,6 +138,15 @@ bool Signals::InstallHandler(int sig, return true; } +// static +bool Signals::InstallDefaultHandler(int sig) { + struct sigaction action; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + action.sa_handler = SIG_DFL; + return sigaction(sig, &action, nullptr) == 0; +} + // static bool Signals::InstallCrashHandlers(Handler handler, int flags, diff --git a/util/posix/signals.h b/util/posix/signals.h index 36d33cd6..ade093bf 100644 --- a/util/posix/signals.h +++ b/util/posix/signals.h @@ -85,6 +85,14 @@ class Signals { int flags, struct sigaction* old_action); + //! \brief Installs `SIG_DFL` for the signal \a sig. + //! + //! \param[in] sig The signal to set the default action for. + //! + //! \return `true` on success, `false` on failure with errno set. No message + //! is logged. + static bool InstallDefaultHandler(int sig); + //! \brief Installs a new signal handler for all signals associated with //! crashes. //! diff --git a/util/util.gyp b/util/util.gyp index a7bed771..43b6e32b 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -58,6 +58,10 @@ 'linux/checked_address_range.h', 'linux/direct_ptrace_connection.cc', 'linux/direct_ptrace_connection.h', + 'linux/exception_handler_client.cc', + 'linux/exception_handler_client.h', + 'linux/exception_handler_protocol.h', + 'linux/exception_information.h', 'linux/memory_map.cc', 'linux/memory_map.h', 'linux/proc_stat_reader.cc', @@ -69,7 +73,6 @@ 'linux/ptrace_connection.h', 'linux/ptracer.cc', 'linux/ptracer.h', - 'linux/exception_information.h', 'linux/scoped_ptrace_attach.cc', 'linux/scoped_ptrace_attach.h', 'linux/thread_info.cc', diff --git a/util/win/exception_handler_server.h b/util/win/exception_handler_server.h index 69f760d2..994cdba4 100644 --- a/util/win/exception_handler_server.h +++ b/util/win/exception_handler_server.h @@ -38,8 +38,6 @@ class ExceptionHandlerServer { public: class Delegate { public: - virtual ~Delegate(); - //! \brief Called when the server has created the named pipe connection //! points and is ready to service requests. virtual void ExceptionHandlerServerStarted() = 0; @@ -60,6 +58,9 @@ class ExceptionHandlerServer { HANDLE process, WinVMAddress exception_information_address, WinVMAddress debug_critical_section_address) = 0; + + protected: + ~Delegate(); }; //! \brief Constructs the exception handling server. diff --git a/util/win/exception_handler_server_test.cc b/util/win/exception_handler_server_test.cc index 6f880516..ce316774 100644 --- a/util/win/exception_handler_server_test.cc +++ b/util/win/exception_handler_server_test.cc @@ -56,7 +56,7 @@ class RunServerThread : public Thread { class TestDelegate : public ExceptionHandlerServer::Delegate { public: explicit TestDelegate(HANDLE server_ready) : server_ready_(server_ready) {} - ~TestDelegate() override {} + ~TestDelegate() {} void ExceptionHandlerServerStarted() override { SetEvent(server_ready_); From 3a41c51668cf0d87092b41d2cb6874d88d19c22c Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Tue, 19 Dec 2017 17:13:13 -0500 Subject: [PATCH 082/326] gn, linux: Update build after 9b2ba587f618 Bug: crashpad:30, crashpad:79 Change-Id: Ib50352cfd36d40786b9732e7c4ab50781963369b Reviewed-on: https://chromium-review.googlesource.com/835028 Reviewed-by: Joshua Peraza Commit-Queue: Mark Mentovai --- handler/BUILD.gn | 24 ++++++++++++++++++++---- handler/handler.gyp | 7 ------- test/BUILD.gn | 2 ++ util/BUILD.gn | 3 +++ 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/handler/BUILD.gn b/handler/BUILD.gn index 1223110a..162e83d3 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -16,8 +16,6 @@ static_library("handler") { sources = [ "crash_report_upload_thread.cc", "crash_report_upload_thread.h", - "handler_main.cc", - "handler_main.h", "minidump_to_upload_parameters.cc", "minidump_to_upload_parameters.h", "prune_crash_reports_thread.cc", @@ -37,6 +35,18 @@ static_library("handler") { ] } + if (is_linux) { + sources += [ + "linux/exception_handler_server.cc", + "linux/exception_handler_server.h", + ] + } else { + sources += [ + "handler_main.cc", + "handler_main.h", + ] + } + if (is_win) { sources += [ "win/crash_report_exception_handler.cc", @@ -77,6 +87,14 @@ source_set("handler_test") { "minidump_to_upload_parameters_test.cc", ] + if (is_linux) { + sources += [ "linux/exception_handler_server_test.cc" ] + } + + if (is_win) { + sources += [ "crashpad_handler_test.cc" ] + } + deps = [ ":handler", "../client", @@ -90,8 +108,6 @@ source_set("handler_test") { ] if (is_win) { - sources += [ "crashpad_handler_test.cc" ] - data_deps = [ ":crashpad_handler_test_extended_handler", ] diff --git a/handler/handler.gyp b/handler/handler.gyp index 6c45e1a3..8263dfde 100644 --- a/handler/handler.gyp +++ b/handler/handler.gyp @@ -62,13 +62,6 @@ 'handler_main.cc', ], }], - ['OS=="linux"', { - 'link_settings': { - 'libraries': [ - '-lcap', - ], - }, - }], ], 'target_conditions': [ ['OS=="android"', { diff --git a/test/BUILD.gn b/test/BUILD.gn index 6573c362..e25f6e1e 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -71,6 +71,8 @@ static_library("test") { "linux/fake_ptrace_connection.h", "linux/get_tls.cc", "linux/get_tls.h", + "linux/scoped_pr_set_ptracer.cc", + "linux/scoped_pr_set_ptracer.h", ] } diff --git a/util/BUILD.gn b/util/BUILD.gn index b24feeaa..6285f00e 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -248,6 +248,9 @@ static_library("util") { "linux/checked_linux_address_range.h", "linux/direct_ptrace_connection.cc", "linux/direct_ptrace_connection.h", + "linux/exception_handler_client.cc", + "linux/exception_handler_client.h", + "linux/exception_handler_protocol.h", "linux/exception_information.h", "linux/memory_map.cc", "linux/memory_map.h", From ab153f7e1bf6faeb9b7d833f13e31f935dbdb190 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 19 Dec 2017 15:31:04 -0800 Subject: [PATCH 083/326] gn: Avoid depending on BUILDCONFIG.gn globals Goes with https://chromium-review.googlesource.com/c/chromium/mini_chromium/+/834648. Includes mini_chromium DEPS roll to pull in https://chromium.googlesource.com/chromium/mini_chromium/+/edfe51ce818e55eae73ab3f144a360e855533888 Bug: crashpad:79, crashpad:196 Change-Id: Ib45cc738aecf9ae727f8faeff81f3b71e2dc9de8 Reviewed-on: https://chromium-review.googlesource.com/834543 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- BUILD.gn | 3 +- DEPS | 2 +- build/BUILD.gn | 2 +- build/BUILDCONFIG.gn | 54 +++++++++------------------ build/crashpad_buildconfig.gni | 59 ++++++++++++++++++++++++++++++ build/test.gni | 26 +++++++++++++ client/BUILD.gn | 16 ++++---- compat/BUILD.gn | 18 +++++---- handler/BUILD.gn | 52 ++++++++++++++------------ minidump/BUILD.gn | 8 ++-- snapshot/BUILD.gn | 34 ++++++++--------- test/BUILD.gn | 20 +++++----- third_party/gtest/BUILD.gn | 9 +++-- third_party/mini_chromium/BUILD.gn | 2 +- third_party/zlib/BUILD.gn | 10 ++--- tools/BUILD.gn | 8 ++-- util/BUILD.gn | 36 +++++++++--------- 17 files changed, 221 insertions(+), 138 deletions(-) create mode 100644 build/crashpad_buildconfig.gni create mode 100644 build/test.gni diff --git a/BUILD.gn b/BUILD.gn index e3d23173..9f386afa 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -12,7 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("build/crashpad_dependencies.gni") +import("build/crashpad_buildconfig.gni") +import("build/test.gni") config("crashpad_config") { include_dirs = [ "." ] diff --git a/DEPS b/DEPS index 3f15230b..b197e656 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '404f6dbf9928dd19cb437ad5b05abfdd97b5bf67', + 'edfe51ce818e55eae73ab3f144a360e855533888', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/build/BUILD.gn b/build/BUILD.gn index c55c7b78..ceae81ec 100644 --- a/build/BUILD.gn +++ b/build/BUILD.gn @@ -16,7 +16,7 @@ # whether code is being built standalone, or in Chromium, or potentially in some # other configutation. -import("crashpad_dependencies.gni") +import("crashpad_buildconfig.gni") config("crashpad_is_in_chromium") { if (crashpad_is_in_chromium) { diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn index 7444c6f0..67d56ae4 100644 --- a/build/BUILDCONFIG.gn +++ b/build/BUILDCONFIG.gn @@ -12,6 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Intentionally very minimal, so that Crashpad can build in-tree in a variety of +# other projects, unrelated to the variables that are set in those projects' +# BUILDCONFIG.gn. Do not add more variables here. Instead, make them available +# in build/crashpad_buildconfig.gni if they must be globally available. + if (target_os == "") { target_os = host_os } @@ -28,35 +33,7 @@ if (current_cpu == "") { current_cpu = target_cpu } -is_mac = false -is_win = false -is_linux = false -is_android = false -is_fuchsia = false - -if (current_os == "mac") { - is_mac = true -} else if (current_os == "win") { - is_win = true -} else if (current_os == "android") { - is_android = true -} else if (current_os == "linux") { - is_linux = true -} else if (current_os == "fuchsia") { - is_fuchsia = true -} - -declare_args() { - # When true, enables the debug configuration, with additional run-time checks - # and logging. When false, enables the release configuration, with additional - # optimizations. - is_debug = false - - # When true, configures for compilation with Clang. - is_clang = !is_win -} - -if (is_win) { +if (current_os == "win") { set_default_toolchain( "//third_party/mini_chromium/mini_chromium/build:msvc_toolchain") } else { @@ -64,12 +41,19 @@ if (is_win) { "//third_party/mini_chromium/mini_chromium/build:gcc_like_toolchain") } +declare_args() { + # When true, enables the debug configuration, with additional run-time checks + # and logging. When false, enables the release configuration, with additional + # optimizations. + is_debug = false +} + _default_configs = [ "//third_party/mini_chromium/mini_chromium/build:default", "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", ] -default_executable_configs = +_default_executable_configs = _default_configs + [ "//third_party/mini_chromium/mini_chromium/build:executable" ] @@ -82,7 +66,7 @@ set_defaults("static_library") { } set_defaults("executable") { - configs = default_executable_configs + configs = _default_executable_configs } set_defaults("loadable_module") { @@ -93,8 +77,6 @@ set_defaults("shared_library") { configs = _default_configs } -# These are set to constant values for Chromium build file compatibility. This -# generally avoids extra explicit checking of whether or not Crashpad is -# building in Chromium mode or not. -is_ios = false -is_component_build = false +set_defaults("test") { + configs = _default_executable_configs +} diff --git a/build/crashpad_buildconfig.gni b/build/crashpad_buildconfig.gni new file mode 100644 index 00000000..021bd53a --- /dev/null +++ b/build/crashpad_buildconfig.gni @@ -0,0 +1,59 @@ +# 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. + +declare_args() { + # Determines various flavors of build configuration, and which concrete + # targets to use for dependencies. Valid values are "standalone", "chromium", + # and "fuchsia". + crashpad_dependencies = "standalone" +} + +assert( + crashpad_dependencies == "chromium" || crashpad_dependencies == "fuchsia" || + crashpad_dependencies == "standalone") + +crashpad_is_in_chromium = crashpad_dependencies == "chromium" +crashpad_is_in_fuchsia = crashpad_dependencies == "fuchsia" +crashpad_is_standalone = crashpad_dependencies == "standalone" + +if (crashpad_is_in_chromium) { + crashpad_is_mac = is_mac + crashpad_is_win = is_win + crashpad_is_linux = is_linux + crashpad_is_android = is_android + crashpad_is_fuchsia = is_fuchsia + + crashpad_is_posix = is_posix + + crashpad_is_clang = is_clang +} else { + # Using mini_chromium, but in different locations depending on whether in + # Fuchsia, or standalone. + if (crashpad_is_in_fuchsia) { + import("//third_party/mini_chromium/build/compiler.gni") + import("//third_party/mini_chromium/build/platform.gni") + } else { + import("../third_party/mini_chromium/mini_chromium/build/compiler.gni") + import("../third_party/mini_chromium/mini_chromium/build/platform.gni") + } + crashpad_is_mac = mini_chromium_is_mac + crashpad_is_win = mini_chromium_is_win + crashpad_is_linux = mini_chromium_is_linux + crashpad_is_android = mini_chromium_is_android + crashpad_is_fuchsia = mini_chromium_is_fuchsia + + crashpad_is_posix = mini_chromium_is_posix + + crashpad_is_clang = mini_chromium_is_clang +} diff --git a/build/test.gni b/build/test.gni new file mode 100644 index 00000000..f46520b7 --- /dev/null +++ b/build/test.gni @@ -0,0 +1,26 @@ +# 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. + +import("crashpad_buildconfig.gni") + +if (crashpad_is_in_chromium) { + import("//testing/test.gni") +} else { + template("test") { + executable(target_name) { + testonly = true + forward_variables_from(invoker, "*") + } + } +} diff --git a/client/BUILD.gn b/client/BUILD.gn index 8d8c79f4..f7fd288f 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import("../build/crashpad_buildconfig.gni") + static_library("client") { sources = [ "annotation.cc", @@ -32,7 +34,7 @@ static_library("client") { "simulate_crash.h", ] - if (is_mac) { + if (crashpad_is_mac) { sources += [ "capture_context_mac.S", "capture_context_mac.h", @@ -43,7 +45,7 @@ static_library("client") { ] } - if (is_win) { + if (crashpad_is_win) { sources += [ "crash_report_database_win.cc", "crashpad_client_win.cc", @@ -51,7 +53,7 @@ static_library("client") { ] } - if (is_fuchsia) { + if (crashpad_is_fuchsia) { sources += [ "crash_report_database_fuchsia.cc", "crashpad_client_fuchsia.cc", @@ -66,7 +68,7 @@ static_library("client") { "../util", ] - if (is_win) { + if (crashpad_is_win) { libs = [ "rpcrt4.lib" ] cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union } @@ -85,14 +87,14 @@ source_set("client_test") { "simple_string_dictionary_test.cc", ] - if (is_mac) { + if (crashpad_is_mac) { sources += [ "capture_context_mac_test.cc", "simulate_crash_mac_test.cc", ] } - if (is_win) { + if (crashpad_is_win) { sources += [ "crashpad_client_win_test.cc" ] } @@ -110,7 +112,7 @@ source_set("client_test") { "../handler:crashpad_handler", ] - if (is_win) { + if (crashpad_is_win) { data_deps += [ "../handler:crashpad_handler_console" ] } } diff --git a/compat/BUILD.gn b/compat/BUILD.gn index bff7d5f8..52eda78b 100644 --- a/compat/BUILD.gn +++ b/compat/BUILD.gn @@ -12,18 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. +import("../build/crashpad_buildconfig.gni") + config("compat_config") { include_dirs = [] - if (is_mac) { + if (crashpad_is_mac) { include_dirs += [ "mac" ] } - if (is_linux) { + if (crashpad_is_linux) { include_dirs += [ "linux" ] } - if (is_win) { + if (crashpad_is_win) { include_dirs += [ "win" ] } else { include_dirs += [ "non_win" ] @@ -31,7 +33,7 @@ config("compat_config") { } template("compat_target") { - if (is_mac) { + if (crashpad_is_mac) { # There are no sources to compile, which doesn’t mix will with a # static_library. group(target_name) { @@ -47,7 +49,7 @@ template("compat_target") { compat_target("compat") { sources = [] - if (is_mac) { + if (crashpad_is_mac) { sources += [ "mac/AvailabilityMacros.h", "mac/kern/exc_resource.h", @@ -59,14 +61,14 @@ compat_target("compat") { sources += [ "non_mac/mach/mach.h" ] } - if (is_linux) { + if (crashpad_is_linux) { sources += [ "linux/signal.h", "linux/sys/ptrace.h", ] } - if (is_win) { + if (crashpad_is_win) { sources += [ "win/getopt.h", "win/strings.cc", @@ -96,7 +98,7 @@ compat_target("compat") { deps = [] - if (is_win) { + if (crashpad_is_win) { deps += [ "../third_party/getopt" ] } } diff --git a/handler/BUILD.gn b/handler/BUILD.gn index 162e83d3..b04799c5 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import("../build/crashpad_buildconfig.gni") + static_library("handler") { sources = [ "crash_report_upload_thread.cc", @@ -24,7 +26,7 @@ static_library("handler") { "user_stream_data_source.h", ] - if (is_mac) { + if (crashpad_is_mac) { sources += [ "mac/crash_report_exception_handler.cc", "mac/crash_report_exception_handler.h", @@ -35,7 +37,7 @@ static_library("handler") { ] } - if (is_linux) { + if (crashpad_is_linux) { sources += [ "linux/exception_handler_server.cc", "linux/exception_handler_server.h", @@ -47,14 +49,14 @@ static_library("handler") { ] } - if (is_win) { + if (crashpad_is_win) { sources += [ "win/crash_report_exception_handler.cc", "win/crash_report_exception_handler.h", ] } - if (is_fuchsia) { + if (crashpad_is_fuchsia) { sources += [ "fuchsia/crash_report_exception_handler.cc", "fuchsia/crash_report_exception_handler.h", @@ -75,7 +77,7 @@ static_library("handler") { "../util", ] - if (is_win) { + if (crashpad_is_win) { cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union } } @@ -87,11 +89,11 @@ source_set("handler_test") { "minidump_to_upload_parameters_test.cc", ] - if (is_linux) { + if (crashpad_is_linux) { sources += [ "linux/exception_handler_server_test.cc" ] } - if (is_win) { + if (crashpad_is_win) { sources += [ "crashpad_handler_test.cc" ] } @@ -107,7 +109,9 @@ source_set("handler_test") { "../util", ] - if (is_win) { + if (crashpad_is_win) { + sources += [ "crashpad_handler_test.cc" ] + data_deps = [ ":crashpad_handler_test_extended_handler", ] @@ -126,23 +130,25 @@ executable("crashpad_handler") { "../third_party/mini_chromium:base", ] - if (is_mac && is_component_build) { - ldflags = [ - # The handler is in - # Chromium.app/Contents/Versions/X/Chromium Framework.framework/Versions/A/Helpers/ - # so set rpath up to the base. - "-rpath", - "@loader_path/../../../../../../../..", + if (crashpad_is_mac && crashpad_is_in_chromium) { + if (is_component_build) { + ldflags = [ + # The handler is in + # Chromium.app/Contents/Versions/X/Chromium Framework.framework/Versions/A/Helpers/ + # so set rpath up to the base. + "-rpath", + "@loader_path/../../../../../../../..", - # The handler is also in - # Content Shell.app/Contents/Frameworks/Content Shell Framework.framework/Helpers/ - # so set the rpath for that too. - "-rpath", - "@loader_path/../../../../..", - ] + # The handler is also in + # Content Shell.app/Contents/Frameworks/Content Shell Framework.framework/Helpers/ + # so set the rpath for that too. + "-rpath", + "@loader_path/../../../../..", + ] + } } - if (is_win) { + if (crashpad_is_win) { configs -= [ "//build/config/win:console" ] configs += [ "//build/config/win:windowed" ] } @@ -165,7 +171,7 @@ executable("crashpad_handler_test_extended_handler") { ] } -if (is_win) { +if (crashpad_is_win) { executable("crashpad_handler_com") { sources = [ "main.cc", diff --git a/minidump/BUILD.gn b/minidump/BUILD.gn index 7a51a49a..88a10815 100644 --- a/minidump/BUILD.gn +++ b/minidump/BUILD.gn @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import("../build/crashpad_buildconfig.gni") + static_library("minidump") { sources = [ "minidump_annotation_writer.cc", @@ -79,7 +81,7 @@ static_library("minidump") { "../util", ] - if (is_win) { + if (crashpad_is_win) { cflags = [ "/wd4201", # nonstandard extension used : nameless struct/union "/wd4324", # 'struct' : structure was padded due to __declspec(align()) @@ -120,7 +122,7 @@ static_library("test_support") { "../third_party/mini_chromium:base", ] - if (is_win) { + if (crashpad_is_win) { cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union } } @@ -161,7 +163,7 @@ source_set("minidump_test") { "../util", ] - if (is_win) { + if (crashpad_is_win) { cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union } } diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 2b442211..00caea50 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("../build/crashpad_dependencies.gni") +import("../build/crashpad_buildconfig.gni") static_library("snapshot") { sources = [ @@ -52,7 +52,7 @@ static_library("snapshot") { "unloaded_module_snapshot.h", ] - if (is_mac) { + if (crashpad_is_mac) { sources += [ "mac/cpu_context_mac.cc", "mac/cpu_context_mac.h", @@ -94,7 +94,7 @@ static_library("snapshot") { ] } - if (is_linux) { + if (crashpad_is_linux) { sources += [ "elf/elf_dynamic_array_reader.cc", "elf/elf_dynamic_array_reader.h", @@ -122,7 +122,7 @@ static_library("snapshot") { ] } - if (is_win) { + if (crashpad_is_win) { sources += [ "win/capture_memory_delegate_win.cc", "win/capture_memory_delegate_win.h", @@ -155,7 +155,7 @@ static_library("snapshot") { ] } - if (is_fuchsia) { + if (crashpad_is_fuchsia) { sources += [ "fuchsia/process_snapshot_fuchsia.cc", "fuchsia/process_snapshot_fuchsia.h", @@ -178,13 +178,13 @@ static_library("snapshot") { "../util", ] - if (is_win) { + if (crashpad_is_win) { cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union libs = [ "powrprof.lib" ] } } -if (is_win) { +if (crashpad_is_win) { static_library("snapshot_api") { sources = [ "api/module_annotations_win.cc", @@ -241,14 +241,14 @@ static_library("test_support") { "../util", ] - if (is_win) { + if (crashpad_is_win) { cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union } } config("snapshot_test_link") { visibility = [ ":*" ] - if (is_linux) { + if (crashpad_is_linux) { # There’s no way to make the link depend on this file. “inputs” doesn’t have # the intended effect in a config. https://crbug.com/781858, # https://crbug.com/796187. @@ -267,7 +267,7 @@ source_set("snapshot_test") { "minidump/process_snapshot_minidump_test.cc", ] - if (is_mac) { + if (crashpad_is_mac) { sources += [ "mac/cpu_context_mac_test.cc", "mac/mach_o_image_annotations_reader_test.cc", @@ -279,7 +279,7 @@ source_set("snapshot_test") { ] } - if (is_linux) { + if (crashpad_is_linux) { sources += [ "elf/elf_image_reader_test.cc", "elf/elf_image_reader_test_note.S", @@ -293,7 +293,7 @@ source_set("snapshot_test") { sources += [ "crashpad_info_client_options_test.cc" ] } - if (is_win) { + if (crashpad_is_win) { sources += [ "api/module_annotations_win_test.cc", "win/cpu_context_win_test.cc", @@ -332,7 +332,7 @@ source_set("snapshot_test") { ":crashpad_snapshot_test_module_small", ] - if (is_mac) { + if (crashpad_is_mac) { libs = [ "OpenCL.framework" ] data_deps += [ @@ -341,11 +341,11 @@ source_set("snapshot_test") { ] } - if (is_linux) { + if (crashpad_is_linux) { libs = [ "dl" ] } - if (is_win) { + if (crashpad_is_win) { cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union data_deps += [ @@ -392,7 +392,7 @@ loadable_module("crashpad_snapshot_test_module_small") { ] } -if (is_mac) { +if (crashpad_is_mac) { loadable_module("crashpad_snapshot_test_module_crashy_initializer") { testonly = true sources = [ @@ -408,7 +408,7 @@ if (is_mac) { } } -if (is_win) { +if (crashpad_is_win) { executable("crashpad_snapshot_test_annotations") { testonly = true sources = [ diff --git a/test/BUILD.gn b/test/BUILD.gn index e25f6e1e..b8e11d00 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("../build/crashpad_dependencies.gni") +import("../build/crashpad_buildconfig.gni") static_library("test") { testonly = true @@ -47,12 +47,12 @@ static_library("test") { "scoped_temp_dir_posix.cc", ] - if (!is_fuchsia) { + if (!crashpad_is_fuchsia) { sources += [ "multiprocess_exec_posix.cc" ] } } - if (is_mac) { + if (crashpad_is_mac) { sources += [ "mac/dyld.cc", "mac/dyld.h", @@ -65,7 +65,7 @@ static_library("test") { ] } - if (is_linux) { + if (crashpad_is_linux) { sources += [ "linux/fake_ptrace_connection.cc", "linux/fake_ptrace_connection.h", @@ -76,7 +76,7 @@ static_library("test") { ] } - if (is_win) { + if (crashpad_is_win) { sources += [ "multiprocess_exec_win.cc", "scoped_temp_dir_win.cc", @@ -106,7 +106,7 @@ static_library("test") { "../util", ] - if (is_mac) { + if (crashpad_is_mac) { libs = [ "bsm" ] deps += [ "../handler", @@ -125,22 +125,22 @@ source_set("test_test") { "test_paths_test.cc", ] - if (crashpad_is_posix && !is_fuchsia) { + if (crashpad_is_posix && !crashpad_is_fuchsia) { sources += [ "multiprocess_posix_test.cc" ] } - if (is_mac) { + if (crashpad_is_mac) { sources += [ "mac/mach_multiprocess_test.cc" ] } - if (is_win) { + if (crashpad_is_win) { sources += [ "win/win_child_process_test.cc", "win/win_multiprocess_test.cc", ] } - if (!is_fuchsia) { + if (!crashpad_is_fuchsia) { sources += [ # TODO(scottmg): A MultiprocessExecFuchsia is probably desirable, but # hasn't been implemented yet. diff --git a/third_party/gtest/BUILD.gn b/third_party/gtest/BUILD.gn index bbb6e4c4..3a955a54 100644 --- a/third_party/gtest/BUILD.gn +++ b/third_party/gtest/BUILD.gn @@ -12,7 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("../../build/crashpad_dependencies.gni") +import("../../build/crashpad_buildconfig.gni") +import("../../build/test.gni") if (crashpad_is_in_chromium) { group("gtest") { @@ -168,7 +169,7 @@ if (crashpad_is_in_chromium) { ":gtest", ] - if (is_clang) { + if (crashpad_is_clang) { cflags_cc = [ # For gtest/googlemock/test/gmock-matchers_test.cc’s # Unstreamable::value_. @@ -249,7 +250,7 @@ if (crashpad_is_in_chromium) { config("gmock_public_config") { include_dirs = [ "gtest/googlemock/include" ] - if (is_clang) { + if (crashpad_is_clang) { cflags_cc = [ # The MOCK_METHODn() macros do not specify “override”, which triggers # this warning in users: “error: 'Method' overrides a member function @@ -336,7 +337,7 @@ if (crashpad_is_in_chromium) { ":gtest", ] - if (is_clang) { + if (crashpad_is_clang) { cflags_cc = [ # For gtest/googlemock/test/gmock-matchers_test.cc’s # testing::gmock_matchers_test::Unprintable::c_. diff --git a/third_party/mini_chromium/BUILD.gn b/third_party/mini_chromium/BUILD.gn index acd4a588..8fb67140 100644 --- a/third_party/mini_chromium/BUILD.gn +++ b/third_party/mini_chromium/BUILD.gn @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("../../build/crashpad_dependencies.gni") +import("../../build/crashpad_buildconfig.gni") group("base") { if (crashpad_is_in_chromium) { diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn index 6edddfb9..db0f99a1 100644 --- a/third_party/zlib/BUILD.gn +++ b/third_party/zlib/BUILD.gn @@ -12,11 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("../../build/crashpad_dependencies.gni") +import("../../build/crashpad_buildconfig.gni") if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { zlib_source = "external" -} else if (!is_win && !is_fuchsia) { +} else if (!crashpad_is_win && !crashpad_is_fuchsia) { zlib_source = "system" } else { zlib_source = "embedded" @@ -82,7 +82,7 @@ if (zlib_source == "external") { defines = [ "HAVE_STDARG_H" ] public_configs = [ ":zlib_config" ] - if (is_win) { + if (crashpad_is_win) { cflags += [ "/wd4131", # uses old-style declarator "/wd4244", # conversion from 't1' to 't2', possible loss of data @@ -104,13 +104,13 @@ if (zlib_source == "external") { "zlib/x86.c", "zlib/x86.h", ] - if (!is_win || is_clang) { + if (!crashpad_is_win || crashpad_is_clang) { cflags += [ "-msse4.2", "-mpclmul", ] } - if (is_clang) { + if (crashpad_is_clang) { cflags += [ "-Wno-incompatible-pointer-types" ] } } else { diff --git a/tools/BUILD.gn b/tools/BUILD.gn index 016daa96..54188198 100644 --- a/tools/BUILD.gn +++ b/tools/BUILD.gn @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import("../build/crashpad_buildconfig.gni") + source_set("tool_support") { sources = [ "tool_support.cc", @@ -69,7 +71,7 @@ executable("generate_dump") { "../util", ] - if (is_mac) { + if (crashpad_is_mac) { # This would be better as a config so that it could be shared with # exception_port_tool, but configs can’t alter “inputs”. # https://crbug.com/781858. @@ -84,12 +86,12 @@ executable("generate_dump") { ] } - if (is_win) { + if (crashpad_is_win) { cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union } } -if (is_mac) { +if (crashpad_is_mac) { executable("catch_exception_tool") { sources = [ "mac/catch_exception_tool.cc", diff --git a/util/BUILD.gn b/util/BUILD.gn index 6285f00e..9a4970fd 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("../build/crashpad_dependencies.gni") +import("../build/crashpad_buildconfig.gni") -if (is_mac) { +if (crashpad_is_mac) { if (crashpad_is_in_chromium) { import("//build/config/sysroot.gni") } else { @@ -168,7 +168,7 @@ static_library("util") { "thread/thread_posix.cc", ] - if (!is_fuchsia) { + if (!crashpad_is_fuchsia) { sources += [ "posix/close_multiple.cc", "posix/close_multiple.h", @@ -187,7 +187,7 @@ static_library("util") { } } - if (is_mac) { + if (crashpad_is_mac) { sources += [ "mac/checked_mach_address_range.h", "mac/launchd.h", @@ -240,7 +240,7 @@ static_library("util") { sources += get_target_outputs(":mig") } - if (is_linux) { + if (crashpad_is_linux) { sources += [ "linux/address_types.h", "linux/auxiliary_vector.cc", @@ -280,7 +280,7 @@ static_library("util") { ] } - if (is_win) { + if (crashpad_is_win) { sources += [ "file/directory_reader_win.cc", "file/file_io_win.cc", @@ -357,7 +357,7 @@ static_library("util") { } } - if (is_fuchsia) { + if (crashpad_is_fuchsia) { sources += [ "misc/paths_fuchsia.cc", "net/http_transport_fuchsia.cc", @@ -378,7 +378,7 @@ static_library("util") { "../third_party/zlib", ] - if (is_mac) { + if (crashpad_is_mac) { libs = [ "bsm", "CoreFoundation.framework", @@ -389,11 +389,11 @@ static_library("util") { include_dirs += [ "$root_build_dir/gen" ] } - if (is_linux) { + if (crashpad_is_linux) { libs = [ "curl" ] } - if (is_win) { + if (crashpad_is_win) { cflags = [ "/wd4201", # nonstandard extension used : nameless struct/union. "/wd4577", # 'noexcept' used with no exception handling mode specified. @@ -450,7 +450,7 @@ source_set("util_test") { "thread/worker_thread_test.cc", ] - if (!is_fuchsia) { + if (!crashpad_is_fuchsia) { # TODO(scottmg): This requires an implementation of MultiprocessExec for # testing, and a solution to http_transport_test_server.py -- either a port # to non-Python, or method of forwarding those requests back to the builder @@ -459,7 +459,7 @@ source_set("util_test") { } if (crashpad_is_posix) { - if (!is_fuchsia) { + if (!crashpad_is_fuchsia) { sources += [ "posix/process_info_test.cc", "posix/signals_test.cc", @@ -469,7 +469,7 @@ source_set("util_test") { sources += [ "posix/scoped_mmap_test.cc" ] } - if (is_mac) { + if (crashpad_is_mac) { sources += [ "mac/launchd_test.mm", "mac/mac_util_test.mm", @@ -493,7 +493,7 @@ source_set("util_test") { ] } - if (is_linux) { + if (crashpad_is_linux) { sources += [ "linux/auxiliary_vector_test.cc", "linux/memory_map_test.cc", @@ -508,7 +508,7 @@ source_set("util_test") { ] } - if (is_win) { + if (crashpad_is_win) { sources += [ "win/capture_context_test.cc", "win/command_line_test.cc", @@ -541,11 +541,11 @@ source_set("util_test") { "../third_party/zlib", ] - if (is_mac) { + if (crashpad_is_mac) { libs = [ "Foundation.framework" ] } - if (is_win) { + if (crashpad_is_win) { libs = [ "rpcrt4.lib" ] data_deps = [ ":crashpad_util_test_process_info_test_child", @@ -554,7 +554,7 @@ source_set("util_test") { } } -if (is_win) { +if (crashpad_is_win) { executable("crashpad_util_test_process_info_test_child") { testonly = true sources = [ From 41c7ace7e85a959dbca9ad9353bcd68ef7807a99 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 19 Dec 2017 16:22:42 -0800 Subject: [PATCH 084/326] linux: Add missing build_config.h include Otherwise, Chromium complains about ARCH_CPU_64_BITS usage without it. Bug: crashpad:30 Change-Id: I4e10595280d309ae891266c03d0467c6c8471d4e Reviewed-on: https://chromium-review.googlesource.com/835429 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- util/linux/exception_handler_client.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/util/linux/exception_handler_client.cc b/util/linux/exception_handler_client.cc index d6d22338..65f68093 100644 --- a/util/linux/exception_handler_client.cc +++ b/util/linux/exception_handler_client.cc @@ -22,6 +22,7 @@ #include "base/logging.h" #include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" #include "util/file/file_io.h" #include "util/linux/ptrace_broker.h" #include "util/posix/signals.h" From e7ebdf019ee084e79cd7044262d44b7042a692b7 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 19 Dec 2017 16:48:09 -0800 Subject: [PATCH 085/326] gn: Add missing testonly for chromium and fuchsia styles Bug: crashpad:79, crashpad:196 Change-Id: I8354b8430cfe4728a635991fb59fcc8ef8652773 Reviewed-on: https://chromium-review.googlesource.com/835468 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- third_party/gtest/BUILD.gn | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/third_party/gtest/BUILD.gn b/third_party/gtest/BUILD.gn index 3a955a54..d2702315 100644 --- a/third_party/gtest/BUILD.gn +++ b/third_party/gtest/BUILD.gn @@ -17,23 +17,29 @@ import("../../build/test.gni") if (crashpad_is_in_chromium) { group("gtest") { + testonly = true public_deps = [ "//testing/gtest", ] } group("gmock") { + testonly = true public_deps = [ "//testing/gmock", ] } } else if (crashpad_is_in_fuchsia) { - # TODO(scottmg): Fuchsia doesn't have a third_party/gmock, and has a - # pre-gmock-integration gtest. group("gtest") { + testonly = true public_deps = [ "//third_party/gtest", ] } + group("gmock") { + testonly = true + # TODO(scottmg): Fuchsia doesn't have a third_party/gmock, and has a + # pre-gmock-integration gtest. + } } else if (crashpad_is_standalone) { config("gtest_private_config") { visibility = [ ":*" ] @@ -84,7 +90,9 @@ if (crashpad_is_in_chromium) { ] sources -= [ "gtest/googletest/src/gtest-all.cc" ] public_configs = [ ":gtest_public_config" ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs -= [ + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", + ] configs += [ ":gtest_private_config" ] } @@ -121,7 +129,9 @@ if (crashpad_is_in_chromium) { "gtest/googletest/test/production.cc", "gtest/googletest/test/production.h", ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs -= [ + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", + ] configs += [ ":gtest_private_config" ] deps = [ ":gtest", @@ -163,7 +173,9 @@ if (crashpad_is_in_chromium) { "gtest/googletest/test/gtest-param-test_test.cc", "gtest/googletest/test/gtest-param-test_test.h", ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs -= [ + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", + ] configs += [ ":gtest_private_config" ] deps = [ ":gtest", @@ -291,7 +303,9 @@ if (crashpad_is_in_chromium) { ] sources -= [ "gtest/googlemock/src/gmock-all.cc" ] public_configs = [ ":gmock_public_config" ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs -= [ + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", + ] configs += [ ":gmock_private_config" ] deps = [ ":gtest", @@ -364,7 +378,9 @@ if (crashpad_is_in_chromium) { sources = [ "gtest/googlemock/test/gmock_stress_test.cc", ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs -= [ + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", + ] configs += [ ":gmock_private_config" ] deps = [ ":gmock", From cd7428971f0d2d6be86170acfa2e201eb15fc337 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 19 Dec 2017 17:15:10 -0800 Subject: [PATCH 086/326] gn: Fix symbol_level use in chromium style Bug: crashpad:79 Change-Id: I417f17194ee1a8ef157ea1e67e64878ccb6f5c10 Reviewed-on: https://chromium-review.googlesource.com/835528 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- snapshot/BUILD.gn | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 00caea50..665620fd 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -14,6 +14,10 @@ import("../build/crashpad_buildconfig.gni") +if (crashpad_is_in_chromium) { + import("//build/config/compiler/compiler.gni") +} + static_library("snapshot") { sources = [ "annotation_snapshot.cc", @@ -470,10 +474,13 @@ if (crashpad_is_win) { "../third_party/mini_chromium:base", "../util", ] - if (crashpad_is_in_chromium && symbol_level == 0) { - # The tests that use this executable rely on at least minimal debug info. - configs -= [ "//build/config/compiler:default_symbols" ] - configs += [ "//build/config/compiler:minimal_symbols" ] + if (crashpad_is_in_chromium) { + if (symbol_level == 0) { + # The tests that use this executable rely on at least minimal debug + # info. + configs -= [ "//build/config/compiler:default_symbols" ] + configs += [ "//build/config/compiler:minimal_symbols" ] + } } } @@ -486,10 +493,12 @@ if (crashpad_is_win) { "../client", "../third_party/mini_chromium:base", ] - if (crashpad_is_in_chromium && symbol_level == 0) { - # The tests that use this module rely on at least minimal debug info. - configs -= [ "//build/config/compiler:default_symbols" ] - configs += [ "//build/config/compiler:minimal_symbols" ] + if (crashpad_is_in_chromium) { + if (symbol_level == 0) { + # The tests that use this module rely on at least minimal debug info. + configs -= [ "//build/config/compiler:default_symbols" ] + configs += [ "//build/config/compiler:minimal_symbols" ] + } } } } From c86779fd96dd3e89130c4ab6aad528065fde63cc Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 19 Dec 2017 18:09:59 -0800 Subject: [PATCH 087/326] gn: Remove duplicated listing of crashpad_handler_test.cc Messed up during rebase. Bug: crashpad:79 Change-Id: I401c2112ec2810cb2fce792cf7b2a55643eeb4d8 Reviewed-on: https://chromium-review.googlesource.com/835530 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- handler/BUILD.gn | 2 -- 1 file changed, 2 deletions(-) diff --git a/handler/BUILD.gn b/handler/BUILD.gn index b04799c5..71037bb0 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -110,8 +110,6 @@ source_set("handler_test") { ] if (crashpad_is_win) { - sources += [ "crashpad_handler_test.cc" ] - data_deps = [ ":crashpad_handler_test_extended_handler", ] From ab0b9438b994e0306a15b60cbc542b0a9ea74eee Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 19 Dec 2017 18:56:48 -0800 Subject: [PATCH 088/326] gn: Fix reference to test_support in chromium style Bug: crashpad:79 Change-Id: I25c02c2f38819ea1edfd89bf9a4254c83d5ff9f5 Reviewed-on: https://chromium-review.googlesource.com/835848 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- third_party/mini_chromium/BUILD.gn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/mini_chromium/BUILD.gn b/third_party/mini_chromium/BUILD.gn index 8fb67140..90747dd8 100644 --- a/third_party/mini_chromium/BUILD.gn +++ b/third_party/mini_chromium/BUILD.gn @@ -33,7 +33,7 @@ group("base") { group("base_test_support") { if (crashpad_is_in_chromium) { public_deps = [ - "//base:test_support", + "//base/test:test_support", ] } } From e9d2ca60f7329bf86951a6c9704953303ecaf6a6 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 19 Dec 2017 19:36:46 -0800 Subject: [PATCH 089/326] gn: Mark base_test_support as testonly=true TBR=mark@chromium.org Bug: crashpad:79 Change-Id: Iaed4031d7d0bc67d2379501a0dd68c9d9585a61c Reviewed-on: https://chromium-review.googlesource.com/835793 Reviewed-by: Scott Graham Commit-Queue: Scott Graham --- third_party/mini_chromium/BUILD.gn | 2 ++ 1 file changed, 2 insertions(+) diff --git a/third_party/mini_chromium/BUILD.gn b/third_party/mini_chromium/BUILD.gn index 90747dd8..9ab81780 100644 --- a/third_party/mini_chromium/BUILD.gn +++ b/third_party/mini_chromium/BUILD.gn @@ -31,6 +31,8 @@ group("base") { } group("base_test_support") { + testonly = true + if (crashpad_is_in_chromium) { public_deps = [ "//base/test:test_support", From 7a285816e9e342de8dc2974a3c40be820e203c85 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Thu, 21 Dec 2017 17:54:12 -0500 Subject: [PATCH 090/326] gn, android: Build for Android with GN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With a companion mini_chromium change at https://crrev.com/c/841203, it’s possible to configure via “gn args” as follows: android_ndk = "/android/android-ndk-r16" target_cpu = "x86_64" target_os = "android" Note that a standalone toolchain is not required. Bug: crashpad:30, crashpad:79 Change-Id: Ica55bdcb82c730909c05dd9fecb40a74eca78c8a Reviewed-on: https://chromium-review.googlesource.com/841286 Reviewed-by: Joshua Peraza Reviewed-by: Robert Sesek --- compat/BUILD.gn | 24 ++++++++++++++++++++++-- handler/BUILD.gn | 4 ++-- snapshot/BUILD.gn | 8 ++++---- test/BUILD.gn | 2 +- third_party/gtest/BUILD.gn | 20 +++++--------------- util/BUILD.gn | 20 ++++++++++++++------ 6 files changed, 48 insertions(+), 30 deletions(-) diff --git a/compat/BUILD.gn b/compat/BUILD.gn index 52eda78b..6c81e70f 100644 --- a/compat/BUILD.gn +++ b/compat/BUILD.gn @@ -21,10 +21,14 @@ config("compat_config") { include_dirs += [ "mac" ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { include_dirs += [ "linux" ] } + if (crashpad_is_android) { + include_dirs += [ "android" ] + } + if (crashpad_is_win) { include_dirs += [ "win" ] } else { @@ -61,13 +65,29 @@ compat_target("compat") { sources += [ "non_mac/mach/mach.h" ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { sources += [ "linux/signal.h", "linux/sys/ptrace.h", ] } + if (crashpad_is_android) { + sources += [ + "android/elf.h", + "android/linux/elf.h", + "android/linux/prctl.h", + "android/linux/ptrace.h", + "android/sched.h", + "android/sys/epoll.cc", + "android/sys/epoll.h", + "android/sys/mman.cc", + "android/sys/mman.h", + "android/sys/syscall.h", + "android/sys/user.h", + ] + } + if (crashpad_is_win) { sources += [ "win/getopt.h", diff --git a/handler/BUILD.gn b/handler/BUILD.gn index 71037bb0..9c337697 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -37,7 +37,7 @@ static_library("handler") { ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { sources += [ "linux/exception_handler_server.cc", "linux/exception_handler_server.h", @@ -89,7 +89,7 @@ source_set("handler_test") { "minidump_to_upload_parameters_test.cc", ] - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { sources += [ "linux/exception_handler_server_test.cc" ] } diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 665620fd..306610f5 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -98,7 +98,7 @@ static_library("snapshot") { ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { sources += [ "elf/elf_dynamic_array_reader.cc", "elf/elf_dynamic_array_reader.h", @@ -252,7 +252,7 @@ static_library("test_support") { config("snapshot_test_link") { visibility = [ ":*" ] - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { # There’s no way to make the link depend on this file. “inputs” doesn’t have # the intended effect in a config. https://crbug.com/781858, # https://crbug.com/796187. @@ -283,7 +283,7 @@ source_set("snapshot_test") { ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { sources += [ "elf/elf_image_reader_test.cc", "elf/elf_image_reader_test_note.S", @@ -345,7 +345,7 @@ source_set("snapshot_test") { ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { libs = [ "dl" ] } diff --git a/test/BUILD.gn b/test/BUILD.gn index b8e11d00..2b7991e2 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -65,7 +65,7 @@ static_library("test") { ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { sources += [ "linux/fake_ptrace_connection.cc", "linux/fake_ptrace_connection.h", diff --git a/third_party/gtest/BUILD.gn b/third_party/gtest/BUILD.gn index d2702315..d6160ef1 100644 --- a/third_party/gtest/BUILD.gn +++ b/third_party/gtest/BUILD.gn @@ -90,9 +90,7 @@ if (crashpad_is_in_chromium) { ] sources -= [ "gtest/googletest/src/gtest-all.cc" ] public_configs = [ ":gtest_public_config" ] - configs -= [ - "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", - ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] configs += [ ":gtest_private_config" ] } @@ -129,9 +127,7 @@ if (crashpad_is_in_chromium) { "gtest/googletest/test/production.cc", "gtest/googletest/test/production.h", ] - configs -= [ - "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", - ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] configs += [ ":gtest_private_config" ] deps = [ ":gtest", @@ -173,9 +169,7 @@ if (crashpad_is_in_chromium) { "gtest/googletest/test/gtest-param-test_test.cc", "gtest/googletest/test/gtest-param-test_test.h", ] - configs -= [ - "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", - ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] configs += [ ":gtest_private_config" ] deps = [ ":gtest", @@ -303,9 +297,7 @@ if (crashpad_is_in_chromium) { ] sources -= [ "gtest/googlemock/src/gmock-all.cc" ] public_configs = [ ":gmock_public_config" ] - configs -= [ - "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", - ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] configs += [ ":gmock_private_config" ] deps = [ ":gtest", @@ -378,9 +370,7 @@ if (crashpad_is_in_chromium) { sources = [ "gtest/googlemock/test/gmock_stress_test.cc", ] - configs -= [ - "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", - ] + configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] configs += [ ":gmock_private_config" ] deps = [ ":gmock", diff --git a/util/BUILD.gn b/util/BUILD.gn index 9a4970fd..3008a6e7 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -241,6 +241,10 @@ static_library("util") { } if (crashpad_is_linux) { + sources += [ "net/http_transport_libcurl.cc" ] + } + + if (crashpad_is_linux || crashpad_is_android) { sources += [ "linux/address_types.h", "linux/auxiliary_vector.cc", @@ -269,7 +273,6 @@ static_library("util") { "linux/thread_info.h", "linux/traits.h", "misc/paths_linux.cc", - "net/http_transport_libcurl.cc", "posix/process_info_linux.cc", "process/process_memory_linux.cc", "process/process_memory_linux.h", @@ -450,11 +453,16 @@ source_set("util_test") { "thread/worker_thread_test.cc", ] - if (!crashpad_is_fuchsia) { - # TODO(scottmg): This requires an implementation of MultiprocessExec for - # testing, and a solution to http_transport_test_server.py -- either a port - # to non-Python, or method of forwarding those requests back to the builder + if (!crashpad_is_android && !crashpad_is_fuchsia) { + # Android and Fuchsia will each require an HTTPTransport implementation + # (libcurl isn’t in either’s SDK) and a solution to + # http_transport_test_server.py, because Python isn’t available on either. + # The latter could be ported to non-Python, or the test server could run on + # the build host with a method to forward requests from the device to the # host. + # + # TODO(scottmg): Fuchsia will also require an implementation of + # MultiprocessExec for testing. sources += [ "net/http_transport_test.cc" ] } @@ -493,7 +501,7 @@ source_set("util_test") { ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { sources += [ "linux/auxiliary_vector_test.cc", "linux/memory_map_test.cc", From 990c6d9cb6f0176ba8acfe0cbe92facc819b7a90 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Fri, 22 Dec 2017 15:32:37 -0800 Subject: [PATCH 091/326] android: add Dlsym() which wraps `dlsym` `dlsym` on Android KitKat (4.4.*) raises SIGFPE when searching for non-existent symbols. This wrapper installs a signal handler prior to calling `dlsym`. Bug: crashpad:30 Change-Id: Iee94672d3c11b1fad1b01526eea7df688c0356cb Reviewed-on: https://chromium-review.googlesource.com/835411 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- compat/BUILD.gn | 2 + compat/android/dlfcn_internal.cc | 166 +++++++++++++++++++++++++++++++ compat/android/dlfcn_internal.h | 35 +++++++ compat/android/sys/epoll.cc | 13 ++- compat/android/sys/mman.cc | 6 +- compat/compat.gyp | 2 + 6 files changed, 215 insertions(+), 9 deletions(-) create mode 100644 compat/android/dlfcn_internal.cc create mode 100644 compat/android/dlfcn_internal.h diff --git a/compat/BUILD.gn b/compat/BUILD.gn index 6c81e70f..8db7b4de 100644 --- a/compat/BUILD.gn +++ b/compat/BUILD.gn @@ -74,6 +74,8 @@ compat_target("compat") { if (crashpad_is_android) { sources += [ + "android/dlfcn_internal.cc", + "android/dlfcn_internal.h", "android/elf.h", "android/linux/elf.h", "android/linux/prctl.h", diff --git a/compat/android/dlfcn_internal.cc b/compat/android/dlfcn_internal.cc new file mode 100644 index 00000000..5d98fe84 --- /dev/null +++ b/compat/android/dlfcn_internal.cc @@ -0,0 +1,166 @@ +// 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 "dlfcn_internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace crashpad { +namespace internal { + +// KitKat supports API levels up to 20. +#if __ANDROID_API__ < 21 + +namespace { + +class ScopedSigactionRestore { + public: + ScopedSigactionRestore() : old_action_(), signo_(-1), valid_(false) {} + + ~ScopedSigactionRestore() { Reset(); } + + bool Reset() { + bool result = true; + if (valid_) { + result = sigaction(signo_, &old_action_, nullptr) == 0; + if (!result) { + PrintErrmsg(errno); + } + } + valid_ = false; + signo_ = -1; + return result; + } + + bool ResetAndInstallHandler(int signo, + void (*handler)(int, siginfo_t*, void*)) { + Reset(); + + struct sigaction act; + act.sa_sigaction = handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + if (sigaction(signo, &act, &old_action_) != 0) { + PrintErrmsg(errno); + return false; + } + signo_ = signo; + valid_ = true; + return true; + } + + private: + void PrintErrmsg(int err) { + char errmsg[256]; + + if (strerror_r(err, errmsg, sizeof(errmsg)) != 0) { + snprintf(errmsg, + sizeof(errmsg), + "%s:%d: Couldn't set errmsg for %d: %d", + __FILE__, + __LINE__, + err, + errno); + return; + } + + fprintf(stderr, "%s:%d: sigaction: %s", __FILE__, __LINE__, errmsg); + } + + struct sigaction old_action_; + int signo_; + bool valid_; +}; + +bool IsKitKat() { + char prop_buf[PROP_VALUE_MAX]; + int length = __system_property_get("ro.build.version.sdk", prop_buf); + if (length <= 0) { + fprintf(stderr, "%s:%d: Couldn't get version", __FILE__, __LINE__); + // It's safer to assume this is KitKat and execute dlsym with a signal + // handler installed. + return true; + } + if (strcmp(prop_buf, "19") == 0 || strcmp(prop_buf, "20") == 0) { + return true; + } + return false; +} + +class ScopedSetTID { + public: + explicit ScopedSetTID(pid_t* tid) : tid_(tid) { *tid_ = syscall(SYS_gettid); } + + ~ScopedSetTID() { *tid_ = -1; } + + private: + pid_t* tid_; +}; + +sigjmp_buf dlsym_sigjmp_env; + +pid_t dlsym_tid = -1; + +void HandleSIGFPE(int signo, siginfo_t* siginfo, void* context) { + if (siginfo->si_code != FPE_INTDIV || syscall(SYS_gettid) != dlsym_tid) { + return; + } + siglongjmp(dlsym_sigjmp_env, 1); +} + +} // namespace + +void* Dlsym(void* handle, const char* symbol) { + if (!IsKitKat()) { + return dlsym(handle, symbol); + } + + static std::mutex* signal_handler_mutex = new std::mutex(); + std::lock_guard lock(*signal_handler_mutex); + + ScopedSetTID set_tid(&dlsym_tid); + + ScopedSigactionRestore sig_restore; + if (!sig_restore.ResetAndInstallHandler(SIGFPE, HandleSIGFPE)) { + return nullptr; + } + + if (sigsetjmp(dlsym_sigjmp_env, 1) != 0) { + return nullptr; + } + + return dlsym(handle, symbol); +} + +#else + +void* Dlsym(void* handle, const char* symbol) { + return dlsym(handle, symbol); +} + +#endif // __ANDROID_API__ < 21 + +} // namespace internal +} // namespace crashpad diff --git a/compat/android/dlfcn_internal.h b/compat/android/dlfcn_internal.h new file mode 100644 index 00000000..ed4083db --- /dev/null +++ b/compat/android/dlfcn_internal.h @@ -0,0 +1,35 @@ +// 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_COMPAT_ANDROID_DLFCN_INTERNAL_H_ +#define CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_ + +namespace crashpad { +namespace internal { + +//! \brief Provide a wrapper for `dlsym`. +//! +//! dlsym on Android KitKat (4.4.*) raises SIGFPE when searching for a +//! non-existent symbol. This wrapper avoids crashing in this circumstance. +//! https://code.google.com/p/android/issues/detail?id=61799 +//! +//! The parameters and return value for this function are the same as for +//! `dlsym`, but a return value for `dlerror` may not be set in the event of an +//! error. +void* Dlsym(void* handle, const char* symbol); + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_ diff --git a/compat/android/sys/epoll.cc b/compat/android/sys/epoll.cc index 64de763f..7fd3bb37 100644 --- a/compat/android/sys/epoll.cc +++ b/compat/android/sys/epoll.cc @@ -18,18 +18,17 @@ #include #include +#include "dlfcn_internal.h" + #if __ANDROID_API__ < 21 extern "C" { int epoll_create1(int flags) { - static const auto epoll_create1_p = - reinterpret_cast(dlsym(RTLD_DEFAULT, "epoll_create1")); - if (epoll_create1_p) { - return epoll_create1_p(flags); - } - - return syscall(SYS_epoll_create1, flags); + static const auto epoll_create1_p = reinterpret_cast( + crashpad::internal::Dlsym(RTLD_DEFAULT, "epoll_create1")); + return epoll_create1_p ? epoll_create1_p(flags) + : syscall(SYS_epoll_create1, flags); } } // extern "C" diff --git a/compat/android/sys/mman.cc b/compat/android/sys/mman.cc index f4d722c9..4c29a9df 100644 --- a/compat/android/sys/mman.cc +++ b/compat/android/sys/mman.cc @@ -19,6 +19,8 @@ #include #include +#include "dlfcn_internal.h" + #if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21 // Bionic has provided a wrapper for __mmap2() since the beginning of time. See @@ -87,8 +89,8 @@ void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset) { // Use the system’s mmap64() wrapper if available. It will be available on // Android 5.0 (“Lollipop”) and later. using Mmap64Type = void* (*)(void*, size_t, int, int, int, off64_t); - static const Mmap64Type mmap64 = - reinterpret_cast(dlsym(RTLD_DEFAULT, "mmap64")); + static const Mmap64Type mmap64 = reinterpret_cast( + crashpad::internal::Dlsym(RTLD_DEFAULT, "mmap64")); if (mmap64) { return mmap64(addr, size, prot, flags, fd, offset); } diff --git a/compat/compat.gyp b/compat/compat.gyp index d3b785b9..c5fe350f 100644 --- a/compat/compat.gyp +++ b/compat/compat.gyp @@ -21,6 +21,8 @@ 'target_name': 'crashpad_compat', 'type': 'static_library', 'sources': [ + 'android/dlfcn_internal.cc', + 'android/dlfcn_internal.h', 'android/elf.h', 'android/linux/elf.h', 'android/linux/prctl.h', From 8bbe985004125060f1864dc465518992768324d4 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Mon, 8 Jan 2018 15:10:31 -0800 Subject: [PATCH 092/326] elf: Use d_ptr instead of d_val when reading from a dynamic array The dynamic array reader should treat data as unsigned when initially reading values from the array to prevent premature sign-extension. The glibc and traditional android headers define d_val using Elf32_Word, an unsigned type. linux/elf.h, used by unified android headers, defines d_val using Elf32_Sword, a signed type. Use d_ptr instead since it's always an unsigned type. Bug: crashpad:30 Change-Id: Ie8e88941fefc7075621aefe226fdba33b1f6129c Reviewed-on: https://chromium-review.googlesource.com/847818 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- snapshot/elf/elf_dynamic_array_reader.cc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/snapshot/elf/elf_dynamic_array_reader.cc b/snapshot/elf/elf_dynamic_array_reader.cc index 681b1b57..9a44e12e 100644 --- a/snapshot/elf/elf_dynamic_array_reader.cc +++ b/snapshot/elf/elf_dynamic_array_reader.cc @@ -16,6 +16,8 @@ #include +#include + #include "util/stdlib/map_insert.h" namespace crashpad { @@ -48,8 +50,14 @@ bool Read(const ProcessMemoryRange& memory, // Skip these entries for now. break; default: + static_assert(std::is_unsigned::value, + "type must be unsigned"); + static_assert(static_cast(&entry.d_un.d_ptr) == + static_cast(&entry.d_un.d_val) && + sizeof(entry.d_un.d_ptr) == sizeof(entry.d_un.d_val), + "d_ptr and d_val must be aliases"); if (!MapInsertOrReplace( - &local_values, entry.d_tag, entry.d_un.d_val, nullptr)) { + &local_values, entry.d_tag, entry.d_un.d_ptr, nullptr)) { LOG(ERROR) << "duplicate dynamic array entry"; return false; } From 70563e92f455b0863e1d7816e24be1cd72df5282 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Mon, 8 Jan 2018 17:17:38 -0800 Subject: [PATCH 093/326] Add CrashpadInfoReader to read CrashpadInfo via ProcessMemory Bug: crashpad:30 Change-Id: I295d518ee0eef1fd61e5544cd6bad25827d07a02 Reviewed-on: https://chromium-review.googlesource.com/846025 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- snapshot/BUILD.gn | 3 + .../crashpad_types/crashpad_info_reader.cc | 187 ++++++++++++++++++ .../crashpad_types/crashpad_info_reader.h | 75 +++++++ .../crashpad_info_reader_test.cc | 132 +++++++++++++ snapshot/snapshot.gyp | 3 + snapshot/snapshot_test.gyp | 2 + util/misc/as_underlying_type.h | 3 +- 7 files changed, 404 insertions(+), 1 deletion(-) create mode 100644 snapshot/crashpad_types/crashpad_info_reader.cc create mode 100644 snapshot/crashpad_types/crashpad_info_reader.h create mode 100644 snapshot/crashpad_types/crashpad_info_reader_test.cc diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 306610f5..cd6bb3e1 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -100,6 +100,8 @@ static_library("snapshot") { if (crashpad_is_linux || crashpad_is_android) { sources += [ + "crashpad_types/crashpad_info_reader.cc", + "crashpad_types/crashpad_info_reader.h", "elf/elf_dynamic_array_reader.cc", "elf/elf_dynamic_array_reader.h", "elf/elf_image_reader.cc", @@ -285,6 +287,7 @@ source_set("snapshot_test") { if (crashpad_is_linux || crashpad_is_android) { sources += [ + "crashpad_types/crashpad_info_reader_test.cc", "elf/elf_image_reader_test.cc", "elf/elf_image_reader_test_note.S", "elf/test_exported_symbols.sym", diff --git a/snapshot/crashpad_types/crashpad_info_reader.cc b/snapshot/crashpad_types/crashpad_info_reader.cc new file mode 100644 index 00000000..ade9931b --- /dev/null +++ b/snapshot/crashpad_types/crashpad_info_reader.cc @@ -0,0 +1,187 @@ +// 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/crashpad_info_reader.h" + +#include + +#include "build/build_config.h" +#include "client/crashpad_info.h" +#include "util/linux/traits.h" +#include "util/misc/as_underlying_type.h" + +namespace crashpad { + +namespace { + +void UnsetIfNotValidTriState(TriState* value) { + switch (AsUnderlyingType(*value)) { + case AsUnderlyingType(TriState::kUnset): + case AsUnderlyingType(TriState::kEnabled): + case AsUnderlyingType(TriState::kDisabled): + return; + } + LOG(WARNING) << "Unsetting invalid TriState " << AsUnderlyingType(*value); + *value = TriState::kUnset; +} + +} // namespace + +class CrashpadInfoReader::InfoContainer { + public: + virtual ~InfoContainer() = default; + + virtual bool Read(const ProcessMemoryRange* memory, VMAddress address) = 0; + + protected: + InfoContainer() = default; +}; + +template +class CrashpadInfoReader::InfoContainerSpecific : public InfoContainer { + public: + InfoContainerSpecific() : InfoContainer() {} + ~InfoContainerSpecific() override = default; + + bool Read(const ProcessMemoryRange* memory, VMAddress address) override { + if (!memory->Read(address, + offsetof(decltype(info), size) + sizeof(info.size), + &info)) { + return false; + } + + if (info.signature != CrashpadInfo::kSignature) { + LOG(ERROR) << "invalid signature 0x" << std::hex << info.signature; + return false; + } + + if (!memory->Read(address, + std::min(VMSize{info.size}, VMSize{sizeof(info)}), + &info)) { + return false; + } + + if (info.size > sizeof(info)) { + LOG(INFO) << "large crashpad info size " << info.size; + } + + if (info.version != 1) { + LOG(ERROR) << "unexpected version " << info.version; + return false; + } + + memset(reinterpret_cast(&info), 0, sizeof(info) - info.size); + + UnsetIfNotValidTriState(&info.crashpad_handler_behavior); + UnsetIfNotValidTriState(&info.system_crash_reporter_forwarding); + UnsetIfNotValidTriState(&info.gather_indirectly_referenced_memory); + + return true; + } + + struct { + uint32_t signature; + uint32_t size; + uint32_t version; + uint32_t indirectly_referenced_memory_cap; + uint32_t padding_0; + TriState crashpad_handler_behavior; + TriState system_crash_reporter_forwarding; + TriState gather_indirectly_referenced_memory; + uint8_t padding_1; + typename Traits::Address extra_memory_ranges; + typename Traits::Address simple_annotations; + typename Traits::Address user_data_minidump_stream_head; + typename Traits::Address annotations_list; + } info; + +#if defined(ARCH_CPU_64_BITS) +#define NATIVE_TRAITS Traits64 +#else +#define NATIVE_TRAITS Traits32 +#endif + static_assert(!std::is_same::value || + sizeof(info) == sizeof(CrashpadInfo), + "CrashpadInfo size mismtach"); +#undef NATIVE_TRAITS +}; + +CrashpadInfoReader::CrashpadInfoReader() + : container_(), is_64_bit_(false), initialized_() {} + +CrashpadInfoReader::~CrashpadInfoReader() = default; + +bool CrashpadInfoReader::Initialize(const ProcessMemoryRange* memory, + VMAddress address) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + is_64_bit_ = memory->Is64Bit(); + + std::unique_ptr new_container; + if (is_64_bit_) { + new_container = std::make_unique>(); + } else { + new_container = std::make_unique>(); + } + + if (!new_container->Read(memory, address)) { + return false; + } + container_ = std::move(new_container); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +#define GET_MEMBER(name) \ + (is_64_bit_ \ + ? reinterpret_cast*>(container_.get()) \ + ->info.name \ + : reinterpret_cast*>(container_.get()) \ + ->info.name) + +#define DEFINE_GETTER(type, method, member) \ + type CrashpadInfoReader::method() { \ + INITIALIZATION_STATE_DCHECK_VALID(initialized_); \ + return GET_MEMBER(member); \ + } + +DEFINE_GETTER(TriState, CrashpadHandlerBehavior, crashpad_handler_behavior); + +DEFINE_GETTER(TriState, + SystemCrashReporterForwarding, + system_crash_reporter_forwarding); + +DEFINE_GETTER(TriState, + GatherIndirectlyReferencedMemory, + gather_indirectly_referenced_memory); + +DEFINE_GETTER(uint32_t, + IndirectlyReferencedMemoryCap, + indirectly_referenced_memory_cap); + +DEFINE_GETTER(VMAddress, ExtraMemoryRanges, extra_memory_ranges); + +DEFINE_GETTER(VMAddress, SimpleAnnotations, simple_annotations); + +DEFINE_GETTER(VMAddress, AnnotationsList, annotations_list); + +DEFINE_GETTER(VMAddress, + UserDataMinidumpStreamHead, + user_data_minidump_stream_head); + +#undef DEFINE_GETTER +#undef GET_MEMBER + +} // namespace crashpad diff --git a/snapshot/crashpad_types/crashpad_info_reader.h b/snapshot/crashpad_types/crashpad_info_reader.h new file mode 100644 index 00000000..5f2352ef --- /dev/null +++ b/snapshot/crashpad_types/crashpad_info_reader.h @@ -0,0 +1,75 @@ +// 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_CRASHPAD_INFO_READER_H_ +#define CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_ + +#include + +#include + +#include "base/macros.h" +#include "util/misc/address_types.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/tri_state.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +//! \brief Reads CrashpadInfo structs from another process via a +//! ProcessMemoryRange. +class CrashpadInfoReader { + public: + CrashpadInfoReader(); + ~CrashpadInfoReader(); + + //! \brief Initializes this object. + //! + //! This method must be successfully called bfore any other method in this + //! class. + //! + //! \param[in] memory The reader for the remote process. + //! \param[in] address The address in the remote process' address space of a + //! CrashpadInfo struct. + //! \return `true` on success. `false` on failure with a message logged. + bool Initialize(const ProcessMemoryRange* memory, VMAddress address); + + //! \{ + //! \see CrashpadInfo + TriState CrashpadHandlerBehavior(); + TriState SystemCrashReporterForwarding(); + TriState GatherIndirectlyReferencedMemory(); + uint32_t IndirectlyReferencedMemoryCap(); + VMAddress ExtraMemoryRanges(); + VMAddress SimpleAnnotations(); + VMAddress AnnotationsList(); + VMAddress UserDataMinidumpStreamHead(); + //! \} + + private: + class InfoContainer; + + template + class InfoContainerSpecific; + + std::unique_ptr container_; + bool is_64_bit_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(CrashpadInfoReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_ diff --git a/snapshot/crashpad_types/crashpad_info_reader_test.cc b/snapshot/crashpad_types/crashpad_info_reader_test.cc new file mode 100644 index 00000000..23565dab --- /dev/null +++ b/snapshot/crashpad_types/crashpad_info_reader_test.cc @@ -0,0 +1,132 @@ +// 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/crashpad_info_reader.h" + +#include +#include + +#include "build/build_config.h" +#include "client/annotation_list.h" +#include "client/crashpad_info.h" +#include "client/simple_address_range_bag.h" +#include "client/simple_string_dictionary.h" +#include "gtest/gtest.h" +#include "test/multiprocess.h" +#include "util/file/file_io.h" +#include "util/misc/from_pointer_cast.h" +#include "util/process/process_memory_linux.h" + +namespace crashpad { +namespace test { +namespace { + +constexpr TriState kCrashpadHandlerBehavior = TriState::kEnabled; +constexpr TriState kSystemCrashReporterForwarding = TriState::kDisabled; +constexpr TriState kGatherIndirectlyReferencedMemory = TriState::kUnset; + +constexpr uint32_t kIndirectlyReferencedMemoryCap = 42; + +class CrashpadInfoTest { + public: + CrashpadInfoTest() + : extra_memory_(), simple_annotations_(), annotation_list_() { + CrashpadInfo* info = CrashpadInfo::GetCrashpadInfo(); + info->set_extra_memory_ranges(&extra_memory_); + info->set_simple_annotations(&simple_annotations_); + info->set_annotations_list(&annotation_list_); + info->set_crashpad_handler_behavior(kCrashpadHandlerBehavior); + info->set_system_crash_reporter_forwarding(kSystemCrashReporterForwarding); + info->set_gather_indirectly_referenced_memory( + kGatherIndirectlyReferencedMemory, kIndirectlyReferencedMemoryCap); + } + + void ExpectCrashpadInfo(pid_t pid, bool is_64_bit) { + ProcessMemoryLinux memory; + ASSERT_TRUE(memory.Initialize(pid)); + + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); + + CrashpadInfo* info = CrashpadInfo::GetCrashpadInfo(); + + CrashpadInfoReader reader; + ASSERT_TRUE(reader.Initialize(&range, FromPointerCast(info))); + EXPECT_EQ(reader.CrashpadHandlerBehavior(), kCrashpadHandlerBehavior); + EXPECT_EQ(reader.SystemCrashReporterForwarding(), + kSystemCrashReporterForwarding); + EXPECT_EQ(reader.GatherIndirectlyReferencedMemory(), + kGatherIndirectlyReferencedMemory); + EXPECT_EQ(reader.IndirectlyReferencedMemoryCap(), + kIndirectlyReferencedMemoryCap); + EXPECT_EQ(reader.ExtraMemoryRanges(), + FromPointerCast(&extra_memory_)); + EXPECT_EQ(reader.SimpleAnnotations(), + FromPointerCast(info->simple_annotations())); + EXPECT_EQ(reader.AnnotationsList(), + FromPointerCast(info->annotations_list())); + } + + private: + SimpleAddressRangeBag extra_memory_; + SimpleStringDictionary simple_annotations_; + AnnotationList annotation_list_; + + DISALLOW_COPY_AND_ASSIGN(CrashpadInfoTest); +}; + +TEST(CrashpadInfoReader, ReadFromSelf) { + CrashpadInfoTest test; + +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif + + test.ExpectCrashpadInfo(getpid(), am_64_bit); +} + +class ReadFromChildTest : public Multiprocess { + public: + ReadFromChildTest() : Multiprocess(), info_test_() {} + + ~ReadFromChildTest() = default; + + private: + void MultiprocessParent() { +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif + + info_test_.ExpectCrashpadInfo(ChildPID(), am_64_bit); + } + + void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } + + CrashpadInfoTest info_test_; + + DISALLOW_COPY_AND_ASSIGN(ReadFromChildTest); +}; + +TEST(CrashpadInfoReader, ReadFromChild) { + ReadFromChildTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index d2fc91f5..ba14eb3e 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -39,6 +39,8 @@ 'cpu_context.h', 'crashpad_info_client_options.cc', 'crashpad_info_client_options.h', + 'crashpad_types/crashpad_info_reader.cc', + 'crashpad_types/crashpad_info_reader.h', 'elf/elf_dynamic_array_reader.cc', 'elf/elf_dynamic_array_reader.h', 'elf/elf_image_reader.cc', @@ -171,6 +173,7 @@ }, { # else: OS!="linux" and OS!="android" 'sources/': [ ['exclude', '^elf/'], + ['exclude', '^crashpad_types/'], ], }], ['target_arch!="ia32" and target_arch!="x64"', { diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index 1e32b8e1..fc3ad4c8 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -72,6 +72,7 @@ 'api/module_annotations_win_test.cc', 'cpu_context_test.cc', 'crashpad_info_client_options_test.cc', + 'crashpad_types/crashpad_info_reader_test.cc', 'elf/elf_image_reader_test.cc', 'elf/elf_image_reader_test_note.S', 'linux/debug_rendezvous_test.cc', @@ -139,6 +140,7 @@ }, { # else: OS!="linux" and OS!="android" 'sources/': [ ['exclude', '^elf/'], + ['exclude', '^crashpad_types/'], ], }], ], diff --git a/util/misc/as_underlying_type.h b/util/misc/as_underlying_type.h index 8afd0ef3..ba673ae5 100644 --- a/util/misc/as_underlying_type.h +++ b/util/misc/as_underlying_type.h @@ -24,7 +24,8 @@ namespace crashpad { //! \param[in] from The value to be casted. //! \return \a from casted to its underlying type. template -typename std::underlying_type::type AsUnderlyingType(From from) { +constexpr typename std::underlying_type::type AsUnderlyingType( + From from) { return static_cast::type>(from); } From 878af9cbbdb400b2e7025b680c613dcf9df5ed2c Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 9 Jan 2018 10:22:24 -0800 Subject: [PATCH 094/326] 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 Reviewed-by: Mark Mentovai --- snapshot/BUILD.gn | 3 + .../crashpad_types/image_annotation_reader.cc | 153 ++++++++++++++++ .../crashpad_types/image_annotation_reader.h | 76 ++++++++ .../image_annotation_reader_test.cc | 164 ++++++++++++++++++ snapshot/snapshot.gyp | 2 + snapshot/snapshot_test.gyp | 1 + 6 files changed, 399 insertions(+) create mode 100644 snapshot/crashpad_types/image_annotation_reader.cc create mode 100644 snapshot/crashpad_types/image_annotation_reader.h create mode 100644 snapshot/crashpad_types/image_annotation_reader_test.cc diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index cd6bb3e1..b98f9d25 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -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", diff --git a/snapshot/crashpad_types/image_annotation_reader.cc b/snapshot/crashpad_types/image_annotation_reader.cc new file mode 100644 index 00000000..bd904979 --- /dev/null +++ b/snapshot/crashpad_types/image_annotation_reader.cc @@ -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 +#include + +#include +#include + +#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 +struct Annotation { + typename Traits::Address link_node; + typename Traits::Address name; + typename Traits::Address value; + uint32_t size; + uint16_t type; +}; + +template +struct AnnotationList { + typename Traits::Address tail_pointer; + Annotation head; + Annotation 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) == + sizeof(Annotation), + "Annotation size mismatch"); + +static_assert(sizeof(process_types::AnnotationList) == + 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* annotations) const { + std::vector 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* annotations) const { + return memory_->Is64Bit() + ? ReadAnnotationList(address, annotations) + : ReadAnnotationList(address, annotations); +} + +template +bool ImageAnnotationReader::ReadAnnotationList( + VMAddress address, + std::vector* annotations) const { + process_types::AnnotationList annotation_list; + if (!memory_->Read(address, sizeof(annotation_list), &annotation_list)) { + LOG(ERROR) << "could not read annotation list"; + return false; + } + + process_types::Annotation 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(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 diff --git a/snapshot/crashpad_types/image_annotation_reader.h b/snapshot/crashpad_types/image_annotation_reader.h new file mode 100644 index 00000000..e425bef6 --- /dev/null +++ b/snapshot/crashpad_types/image_annotation_reader.h @@ -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 +#include +#include + +#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* 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* annotations) const; + + private: + template + bool ReadAnnotationList(VMAddress address, + std::vector* annotations) const; + + const ProcessMemoryRange* memory_; // weak + + DISALLOW_COPY_AND_ASSIGN(ImageAnnotationReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_ diff --git a/snapshot/crashpad_types/image_annotation_reader_test.cc b/snapshot/crashpad_types/image_annotation_reader_test.cc new file mode 100644 index 00000000..b0e635ff --- /dev/null +++ b/snapshot/crashpad_types/image_annotation_reader_test.cc @@ -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 +#include +#include + +#include + +#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& 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& 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::Type::kString, + kAnnotationName, + reinterpret_cast(const_cast(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::Type::kString, + kAnnotationName2, + reinterpret_cast(const_cast(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 simple_map; + ASSERT_TRUE(reader.SimpleMap( + FromPointerCast(&expected_simple_map_), &simple_map)); + ExpectSimpleMap(simple_map, expected_simple_map_); + + std::vector annotation_list; + ASSERT_TRUE(reader.AnnotationsList( + FromPointerCast(&expected_annotation_list_), + &annotation_list)); + ExpectAnnotationList(annotation_list, expected_annotation_list_); + } + + private: + SimpleStringDictionary expected_simple_map_; + std::vector> 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 diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index ba14eb3e..35fb614e 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -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', diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index fc3ad4c8..ae4b7e9f 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -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', From fb379a9242b454dcb540c7bddb4f1967e9184713 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 10 Jan 2018 11:23:56 -0800 Subject: [PATCH 095/326] Add ModuleSnapshotLinux Bug: crashpad:30 Change-Id: Ibf1f62b82a4926e1dfd9ad92231bfff44b811d78 Reviewed-on: https://chromium-review.googlesource.com/842187 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- client/crashpad_info.cc | 27 ++-- snapshot/BUILD.gn | 2 + snapshot/elf/elf_image_reader.h | 5 + snapshot/linux/module_snapshot_linux.cc | 188 +++++++++++++++++++++++ snapshot/linux/module_snapshot_linux.h | 96 ++++++++++++ snapshot/linux/process_reader.cc | 93 +++++++++++ snapshot/linux/process_reader.h | 33 ++++ snapshot/linux/process_reader_test.cc | 84 ++++++++++ snapshot/linux/process_snapshot_linux.cc | 18 ++- snapshot/linux/process_snapshot_linux.h | 3 + snapshot/snapshot.gyp | 2 + 11 files changed, 538 insertions(+), 13 deletions(-) create mode 100644 snapshot/linux/module_snapshot_linux.cc create mode 100644 snapshot/linux/module_snapshot_linux.h diff --git a/client/crashpad_info.cc b/client/crashpad_info.cc index d2896f4a..b545a3cd 100644 --- a/client/crashpad_info.cc +++ b/client/crashpad_info.cc @@ -51,17 +51,24 @@ static_assert(std::is_standard_layout::value, // This may result in a static module initializer in debug-mode builds, but // because it’s POD, no code should need to run to initialize this under // release-mode optimization. + +// Platforms that use ELF objects need to locate this structure via the dynamic +// symbol table, so avoid name mangling. +#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) +extern "C" { +#endif + #if defined(OS_POSIX) __attribute__(( +#if defined(OS_MACOSX) // Put the structure in a well-known section name where it can be easily // found without having to consult the symbol table. -#if defined(OS_MACOSX) section(SEG_DATA ",crashpad_info"), -#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) - section("crashpad_info"), -#else -#error Port + + // There's no need to expose this as a public symbol from the symbol table. + // All accesses from the outside can locate the well-known section name. + visibility("hidden"), #endif #if defined(ADDRESS_SANITIZER) @@ -74,11 +81,7 @@ __attribute__(( #endif // defined(ADDRESS_SANITIZER) // The “used” attribute prevents the structure from being dead-stripped. - used, - - // There’s no need to expose this as a public symbol from the symbol table. - // All accesses from the outside can locate the well-known section name. - visibility("hidden"))) + used)) #elif defined(OS_WIN) @@ -93,6 +96,10 @@ __declspec(allocate("CPADinfo")) CrashpadInfo g_crashpad_info; +#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) +} // extern "C" +#endif + // static CrashpadInfo* CrashpadInfo::GetCrashpadInfo() { return &g_crashpad_info; diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index b98f9d25..67291c7d 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -118,6 +118,8 @@ static_library("snapshot") { "linux/exception_snapshot_linux.h", "linux/memory_snapshot_linux.cc", "linux/memory_snapshot_linux.h", + "linux/module_snapshot_linux.cc", + "linux/module_snapshot_linux.h", "linux/process_reader.cc", "linux/process_reader.h", "linux/process_snapshot_linux.cc", diff --git a/snapshot/elf/elf_image_reader.h b/snapshot/elf/elf_image_reader.h index b6b59979..67cbc1fa 100644 --- a/snapshot/elf/elf_image_reader.h +++ b/snapshot/elf/elf_image_reader.h @@ -202,6 +202,11 @@ class ElfImageReader { NoteReader::NoteType type, ssize_t max_note_size); + //! \brief Return a ProcessMemoryRange restricted to the range of this image. + //! + //! The caller does not take ownership of the returned object. + const ProcessMemoryRange* Memory() const; + private: template class ProgramHeaderTableSpecific; diff --git a/snapshot/linux/module_snapshot_linux.cc b/snapshot/linux/module_snapshot_linux.cc new file mode 100644 index 00000000..0ddbebfa --- /dev/null +++ b/snapshot/linux/module_snapshot_linux.cc @@ -0,0 +1,188 @@ +// Copyright 2018 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/linux/module_snapshot_linux.h" + +#include + +#include "base/files/file_path.h" +#include "snapshot/crashpad_types/image_annotation_reader.h" + +namespace crashpad { +namespace internal { + +ModuleSnapshotLinux::ModuleSnapshotLinux() + : ModuleSnapshot(), + name_(), + elf_reader_(nullptr), + crashpad_info_(), + type_(kModuleTypeUnknown), + initialized_() {} + +ModuleSnapshotLinux::~ModuleSnapshotLinux() = default; + +bool ModuleSnapshotLinux::Initialize( + const ProcessReader::Module& process_reader_module) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (!process_reader_module.elf_reader) { + LOG(ERROR) << "no elf reader"; + return false; + } + + name_ = process_reader_module.name; + elf_reader_ = process_reader_module.elf_reader; + type_ = process_reader_module.type; + + VMAddress info_address; + VMSize info_size; + if (elf_reader_->GetDynamicSymbol( + "g_crashpad_info", &info_address, &info_size)) { + ProcessMemoryRange range; + if (range.Initialize(*elf_reader_->Memory()) && + range.RestrictRange(info_address, info_size)) { + auto info = std::make_unique(); + if (info->Initialize(&range, info_address)) { + crashpad_info_ = std::move(info); + } + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ModuleSnapshotLinux::GetCrashpadOptions( + CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!crashpad_info_) { + return false; + } + + options->crashpad_handler_behavior = + crashpad_info_->CrashpadHandlerBehavior(); + options->system_crash_reporter_forwarding = + crashpad_info_->SystemCrashReporterForwarding(); + options->gather_indirectly_referenced_memory = + crashpad_info_->GatherIndirectlyReferencedMemory(); + options->indirectly_referenced_memory_cap = + crashpad_info_->IndirectlyReferencedMemoryCap(); + return true; +} + +std::string ModuleSnapshotLinux::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return name_; +} + +uint64_t ModuleSnapshotLinux::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return elf_reader_->Address(); +} + +uint64_t ModuleSnapshotLinux::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return elf_reader_->Size(); +} + +time_t ModuleSnapshotLinux::Timestamp() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return 0; +} + +void ModuleSnapshotLinux::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; +} + +void ModuleSnapshotLinux::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; +} + +ModuleSnapshot::ModuleType ModuleSnapshotLinux::GetModuleType() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return type_; +} + +void ModuleSnapshotLinux::UUIDAndAge(crashpad::UUID* uuid, + uint32_t* age) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *age = 0; + + std::unique_ptr notes = + elf_reader_->NotesWithNameAndType(ELF_NOTE_GNU, NT_GNU_BUILD_ID, 64); + std::string desc; + notes->NextNote(nullptr, nullptr, &desc); + desc.insert(desc.end(), 16 - std::min(desc.size(), size_t{16}), '\0'); + uuid->InitializeFromBytes(reinterpret_cast(&desc[0])); +} + +std::string ModuleSnapshotLinux::DebugFileName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return base::FilePath(Name()).BaseName().value(); +} + +std::vector ModuleSnapshotLinux::AnnotationsVector() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::map ModuleSnapshotLinux::AnnotationsSimpleMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::map annotations; + if (crashpad_info_ && crashpad_info_->SimpleAnnotations()) { + ImageAnnotationReader reader(elf_reader_->Memory()); + reader.SimpleMap(crashpad_info_->SimpleAnnotations(), &annotations); + } + return annotations; +} + +std::vector ModuleSnapshotLinux::AnnotationObjects() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector annotations; + if (crashpad_info_ && crashpad_info_->AnnotationsList()) { + ImageAnnotationReader reader(elf_reader_->Memory()); + reader.AnnotationsList(crashpad_info_->AnnotationsList(), &annotations); + } + return annotations; +} + +std::set> ModuleSnapshotLinux::ExtraMemoryRanges() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::set>(); +} + +std::vector +ModuleSnapshotLinux::CustomMinidumpStreams() const { + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/linux/module_snapshot_linux.h b/snapshot/linux/module_snapshot_linux.h new file mode 100644 index 00000000..b277ff7a --- /dev/null +++ b/snapshot/linux/module_snapshot_linux.h @@ -0,0 +1,96 @@ +// Copyright 2018 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_LINUX_MODULE_SNAPSHOT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_MODULE_SNAPSHOT_LINUX_H_ + +#include +#include + +#include +#include +#include + +#include "base/macros.h" +#include "client/crashpad_info.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/crashpad_types/crashpad_info_reader.h" +#include "snapshot/elf/elf_image_reader.h" +#include "snapshot/linux/process_reader.h" +#include "snapshot/module_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +namespace internal { + +//! \brief A ModuleSnapshot of a code module (binary image) loaded into a +//! running (or crashed) process on a Linux system. +class ModuleSnapshotLinux final : public ModuleSnapshot { + public: + ModuleSnapshotLinux(); + ~ModuleSnapshotLinux() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader_module The module within the ProcessReader for + //! which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(const ProcessReader::Module& process_reader_module); + + //! \brief Returns options from the module’s CrashpadInfo structure. + //! + //! \param[out] options Options set in the module’s CrashpadInfo structure. + //! \return `true` if there were options returned. Otherwise `false`. + bool GetCrashpadOptions(CrashpadInfoClientOptions* options); + + // 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 UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector AnnotationsVector() const override; + std::map AnnotationsSimpleMap() const override; + std::vector AnnotationObjects() const override; + std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; + + private: + std::string name_; + ElfImageReader* elf_reader_; + std::unique_ptr crashpad_info_; + ModuleType type_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotLinux); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_MODULE_SNAPSHOT_LINUX_H_ diff --git a/snapshot/linux/process_reader.cc b/snapshot/linux/process_reader.cc index c9a5a382..fb5b8eef 100644 --- a/snapshot/linux/process_reader.cc +++ b/snapshot/linux/process_reader.cc @@ -14,6 +14,7 @@ #include "snapshot/linux/process_reader.h" +#include #include #include #include @@ -26,7 +27,9 @@ #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "build/build_config.h" +#include "snapshot/linux/debug_rendezvous.h" #include "util/file/directory_reader.h" +#include "util/linux/auxiliary_vector.h" #include "util/linux/proc_stat_reader.h" #include "util/misc/as_underlying_type.h" @@ -166,14 +169,22 @@ void ProcessReader::Thread::InitializeStack(ProcessReader* reader) { } } +ProcessReader::Module::Module() + : name(), elf_reader(nullptr), type(ModuleSnapshot::kModuleTypeUnknown) {} + +ProcessReader::Module::~Module() = default; + ProcessReader::ProcessReader() : connection_(), process_info_(), memory_map_(), threads_(), + modules_(), + elf_readers_(), process_memory_(), is_64_bit_(false), initialized_threads_(false), + initialized_modules_(false), initialized_() {} ProcessReader::~ProcessReader() {} @@ -250,6 +261,14 @@ const std::vector& ProcessReader::Threads() { return threads_; } +const std::vector& ProcessReader::Modules() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!initialized_modules_) { + InitializeModules(); + } + return modules_; +} + void ProcessReader::InitializeThreads() { DCHECK(threads_.empty()); @@ -307,4 +326,78 @@ void ProcessReader::InitializeThreads() { DCHECK(main_thread_found); } +void ProcessReader::InitializeModules() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + AuxiliaryVector aux; + if (!aux.Initialize(ProcessID(), is_64_bit_)) { + return; + } + + LinuxVMAddress phdrs; + if (!aux.GetValue(AT_PHDR, &phdrs)) { + return; + } + + const MemoryMap::Mapping* exe_mapping; + if (!(exe_mapping = GetMemoryMap()->FindMapping(phdrs)) && + !(exe_mapping = GetMemoryMap()->FindFileMmapStart(*exe_mapping))) { + return; + } + + ProcessMemoryRange range; + if (!range.Initialize(Memory(), is_64_bit_)) { + return; + } + + auto exe_reader = std::make_unique(); + if (!exe_reader->Initialize(range, exe_mapping->range.Base())) { + return; + } + + LinuxVMAddress debug_address; + if (!exe_reader->GetDebugAddress(&debug_address)) { + return; + } + + DebugRendezvous debug; + if (!debug.Initialize(range, debug_address)) { + return; + } + + Module exe = {}; + exe.name = !debug.Executable()->name.empty() ? debug.Executable()->name + : exe_mapping->name; + exe.elf_reader = exe_reader.get(); + exe.type = ModuleSnapshot::ModuleType::kModuleTypeExecutable; + + modules_.push_back(exe); + elf_readers_.push_back(std::move(exe_reader)); + + LinuxVMAddress loader_base = 0; + aux.GetValue(AT_BASE, &loader_base); + + for (const DebugRendezvous::LinkEntry& entry : debug.Modules()) { + const MemoryMap::Mapping* mapping; + if (!(mapping = memory_map_.FindMapping(entry.dynamic_array)) || + !(mapping = memory_map_.FindFileMmapStart(*mapping))) { + continue; + } + + auto elf_reader = std::make_unique(); + if (!elf_reader->Initialize(range, mapping->range.Base())) { + continue; + } + + Module module = {}; + module.name = !entry.name.empty() ? entry.name : mapping->name; + module.elf_reader = elf_reader.get(); + module.type = loader_base && elf_reader->Address() == loader_base + ? ModuleSnapshot::kModuleTypeDynamicLoader + : ModuleSnapshot::kModuleTypeSharedLibrary; + modules_.push_back(module); + elf_readers_.push_back(std::move(elf_reader)); + } +} + } // namespace crashpad diff --git a/snapshot/linux/process_reader.h b/snapshot/linux/process_reader.h index 26d777e4..59a3c7ef 100644 --- a/snapshot/linux/process_reader.h +++ b/snapshot/linux/process_reader.h @@ -18,9 +18,13 @@ #include #include +#include +#include #include #include "base/macros.h" +#include "snapshot/elf/elf_image_reader.h" +#include "snapshot/module_snapshot.h" #include "util/linux/address_types.h" #include "util/linux/memory_map.h" #include "util/linux/ptrace_connection.h" @@ -56,6 +60,27 @@ class ProcessReader { void InitializeStack(ProcessReader* reader); }; + //! \brief Contains information about a module loaded into a process. + struct Module { + Module(); + ~Module(); + + //! \brief The pathname used to load the module from disk. + std::string name; + + //! \brief An image reader for the module. + //! + //! The lifetime of this ElfImageReader is scoped to the lifetime of the + //! ProcessReader that created it. + //! + //! This field may be `nullptr` if a reader could not be created for the + //! module. + ElfImageReader* elf_reader; + + //! \brief The module's type. + ModuleSnapshot::ModuleType type; + }; + ProcessReader(); ~ProcessReader(); @@ -107,16 +132,24 @@ class ProcessReader { //! index `0`. const std::vector& Threads(); + //! \return The modules loaded in the process. The first element (at index + //! `0`) corresponds to the main executable. + const std::vector& Modules(); + private: void InitializeThreads(); + void InitializeModules(); PtraceConnection* connection_; // weak ProcessInfo process_info_; MemoryMap memory_map_; std::vector threads_; + std::vector modules_; + std::vector> elf_readers_; ProcessMemoryLinux process_memory_; bool is_64_bit_; bool initialized_threads_; + bool initialized_modules_; InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ProcessReader); diff --git a/snapshot/linux/process_reader_test.cc b/snapshot/linux/process_reader_test.cc index 4bdee743..f877bf86 100644 --- a/snapshot/linux/process_reader_test.cc +++ b/snapshot/linux/process_reader_test.cc @@ -15,6 +15,7 @@ #include "snapshot/linux/process_reader.h" #include +#include #include #include #include @@ -440,6 +441,89 @@ TEST(ProcessReader, ChildWithSplitStack) { test.Run(); } +int ExpectFindModule(dl_phdr_info* info, size_t size, void* data) { + SCOPED_TRACE( + base::StringPrintf("module %s at 0x%" PRIx64 " phdrs 0x%" PRIx64, + info->dlpi_name, + LinuxVMAddress{info->dlpi_addr}, + FromPointerCast(info->dlpi_phdr))); + auto modules = + reinterpret_cast*>(data); + + auto phdr_addr = FromPointerCast(info->dlpi_phdr); + +#if defined(OS_ANDROID) + // Bionic includes a null entry. + if (!phdr_addr) { + EXPECT_EQ(info->dlpi_name, nullptr); + EXPECT_EQ(info->dlpi_addr, 0u); + EXPECT_EQ(info->dlpi_phnum, 0u); + return 0; + } +#endif + + // TODO(jperaza): This can use a range map when one is available. + bool found = false; + for (const auto& module : *modules) { + if (module.elf_reader && phdr_addr >= module.elf_reader->Address() && + phdr_addr < module.elf_reader->Address() + module.elf_reader->Size()) { + found = true; + break; + } + } + EXPECT_TRUE(found); + return 0; +} + +void ExpectModulesFromSelf(const std::vector& modules) { + for (const auto& module : modules) { + EXPECT_FALSE(module.name.empty()); + EXPECT_NE(module.type, ModuleSnapshot::kModuleTypeUnknown); + } + + EXPECT_EQ(dl_iterate_phdr( + ExpectFindModule, + reinterpret_cast( + const_cast*>(&modules))), + 0); +} + +TEST(ProcessReader, SelfModules) { + FakePtraceConnection connection; + connection.Initialize(getpid()); + + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + ExpectModulesFromSelf(process_reader.Modules()); +} + +class ChildModuleTest : public Multiprocess { + public: + ChildModuleTest() : Multiprocess() {} + ~ChildModuleTest() = default; + + private: + void MultiprocessParent() override { + DirectPtraceConnection connection; + ASSERT_TRUE(connection.Initialize(ChildPID())); + + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + ExpectModulesFromSelf(process_reader.Modules()); + } + + void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); } + + DISALLOW_COPY_AND_ASSIGN(ChildModuleTest); +}; + +TEST(ProcessReader, ChildModules) { + ChildModuleTest test; + test.Run(); +} + } // namespace } // namespace test } // namespace crashpad diff --git a/snapshot/linux/process_snapshot_linux.cc b/snapshot/linux/process_snapshot_linux.cc index 346da085..acfdcba0 100644 --- a/snapshot/linux/process_snapshot_linux.cc +++ b/snapshot/linux/process_snapshot_linux.cc @@ -49,6 +49,7 @@ bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection) { system_.Initialize(&process_reader_, &snapshot_time_); InitializeThreads(); + InitializeModules(); INITIALIZATION_STATE_SET_VALID(initialized_); return true; @@ -136,9 +137,11 @@ std::vector ProcessSnapshotLinux::Threads() const { std::vector ProcessSnapshotLinux::Modules() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - // TODO(jperaza): do this. - LOG(ERROR) << "Not implemented"; - return std::vector(); + std::vector modules; + for (const auto& module : modules_) { + modules.push_back(module.get()); + } + return modules; } std::vector ProcessSnapshotLinux::UnloadedModules() @@ -182,4 +185,13 @@ void ProcessSnapshotLinux::InitializeThreads() { } } +void ProcessSnapshotLinux::InitializeModules() { + for (const ProcessReader::Module& reader_module : process_reader_.Modules()) { + auto module = std::make_unique(); + if (module->Initialize(reader_module)) { + modules_.push_back(std::move(module)); + } + } +} + } // namespace crashpad diff --git a/snapshot/linux/process_snapshot_linux.h b/snapshot/linux/process_snapshot_linux.h index 446ddb54..68d625b0 100644 --- a/snapshot/linux/process_snapshot_linux.h +++ b/snapshot/linux/process_snapshot_linux.h @@ -25,6 +25,7 @@ #include "base/macros.h" #include "snapshot/linux/exception_snapshot_linux.h" +#include "snapshot/linux/module_snapshot_linux.h" #include "snapshot/linux/process_reader.h" #include "snapshot/linux/system_snapshot_linux.h" #include "snapshot/linux/thread_snapshot_linux.h" @@ -108,12 +109,14 @@ class ProcessSnapshotLinux final : public ProcessSnapshot { private: void InitializeThreads(); + void InitializeModules(); std::map annotations_simple_map_; timeval snapshot_time_; UUID report_id_; UUID client_id_; std::vector> threads_; + std::vector> modules_; std::unique_ptr exception_; internal::SystemSnapshotLinux system_; ProcessReader process_reader_; diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index 35fb614e..caa5f0e0 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -60,6 +60,8 @@ 'linux/exception_snapshot_linux.h', 'linux/memory_snapshot_linux.cc', 'linux/memory_snapshot_linux.h', + 'linux/module_snapshot_linux.cc', + 'linux/module_snapshot_linux.h', 'linux/process_reader.cc', 'linux/process_reader.h', 'linux/process_snapshot_linux.cc', From 94d65e0e9c759afde2bab449a791ec9a42bba6f7 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 10 Jan 2018 11:49:03 -0800 Subject: [PATCH 096/326] linux: Detect memory map failures correctly Bug: crashpad:30 Change-Id: I3ff66b72621b8b29ce2676778b1f2f404495869f Reviewed-on: https://chromium-review.googlesource.com/860887 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- snapshot/linux/process_reader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapshot/linux/process_reader.cc b/snapshot/linux/process_reader.cc index fb5b8eef..0f196c63 100644 --- a/snapshot/linux/process_reader.cc +++ b/snapshot/linux/process_reader.cc @@ -340,7 +340,7 @@ void ProcessReader::InitializeModules() { } const MemoryMap::Mapping* exe_mapping; - if (!(exe_mapping = GetMemoryMap()->FindMapping(phdrs)) && + if (!(exe_mapping = GetMemoryMap()->FindMapping(phdrs)) || !(exe_mapping = GetMemoryMap()->FindFileMmapStart(*exe_mapping))) { return; } From fa197c267cc3c68ea03a8c73fa8ade88934c97e8 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 10 Jan 2018 11:53:15 -0800 Subject: [PATCH 097/326] linux: Collect Crashpad options from CrashpadInfos Bug: crashpad:30 Change-Id: I233780e97a4b23238995af409c5f40d2e809906b Reviewed-on: https://chromium-review.googlesource.com/848248 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- snapshot/linux/process_snapshot_linux.cc | 39 ++++++++++++++++++++++++ snapshot/linux/process_snapshot_linux.h | 8 +++++ 2 files changed, 47 insertions(+) diff --git a/snapshot/linux/process_snapshot_linux.cc b/snapshot/linux/process_snapshot_linux.cc index acfdcba0..8397ad9b 100644 --- a/snapshot/linux/process_snapshot_linux.cc +++ b/snapshot/linux/process_snapshot_linux.cc @@ -79,6 +79,45 @@ bool ProcessSnapshotLinux::InitializeException( return true; } +void ProcessSnapshotLinux::GetCrashpadOptions( + CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + CrashpadInfoClientOptions local_options; + + for (const auto& module : modules_) { + CrashpadInfoClientOptions module_options; + if (!module->GetCrashpadOptions(&module_options)) { + continue; + } + + if (local_options.crashpad_handler_behavior == TriState::kUnset) { + local_options.crashpad_handler_behavior = + module_options.crashpad_handler_behavior; + } + if (local_options.system_crash_reporter_forwarding == TriState::kUnset) { + local_options.system_crash_reporter_forwarding = + module_options.system_crash_reporter_forwarding; + } + if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) { + local_options.gather_indirectly_referenced_memory = + module_options.gather_indirectly_referenced_memory; + local_options.indirectly_referenced_memory_cap = + module_options.indirectly_referenced_memory_cap; + } + + // If non-default values have been found for all options, the loop can end + // early. + if (local_options.crashpad_handler_behavior != TriState::kUnset && + local_options.system_crash_reporter_forwarding != TriState::kUnset && + local_options.gather_indirectly_referenced_memory != TriState::kUnset) { + break; + } + } + + *options = local_options; +} + pid_t ProcessSnapshotLinux::ProcessID() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return process_reader_.ProcessID(); diff --git a/snapshot/linux/process_snapshot_linux.h b/snapshot/linux/process_snapshot_linux.h index 68d625b0..aa6964c4 100644 --- a/snapshot/linux/process_snapshot_linux.h +++ b/snapshot/linux/process_snapshot_linux.h @@ -24,6 +24,7 @@ #include #include "base/macros.h" +#include "snapshot/crashpad_info_client_options.h" #include "snapshot/linux/exception_snapshot_linux.h" #include "snapshot/linux/module_snapshot_linux.h" #include "snapshot/linux/process_reader.h" @@ -87,6 +88,13 @@ class ProcessSnapshotLinux final : public ProcessSnapshot { annotations_simple_map_ = annotations_simple_map; } + //! \brief Returns options from CrashpadInfo structures found in modules in + //! the process. + //! + //! \param[out] options Options set in CrashpadInfo structures in modules in + //! the process. + void GetCrashpadOptions(CrashpadInfoClientOptions* options); + // ProcessSnapshot: pid_t ProcessID() const override; From 9fa60710f9628f8fc292b33b8f4c8b10c89506fd Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 10 Jan 2018 14:16:31 -0800 Subject: [PATCH 098/326] fuchsia: Don't use {0} for initialization Fuchsia builds with -Wmissing-field-initializers. Remove these {0}s. It all seems a bit awful, but as far as I can tell from reading http://en.cppreference.com/w/cpp/language/aggregate_initialization, the 0 is unnecessary. ../../third_party/crashpad/minidump/minidump_system_info_writer_test.cc:42:27: error: missing field 'Buffer' initializer [-Werror,-Wmissing-field-initializers] MINIDUMP_STRING tmp = {0}; ^ 1 error generated. Bug: crashpad:196 Change-Id: I21f48eb24238a607475b0e92ffe5fd88386b40b6 Reviewed-on: https://chromium-review.googlesource.com/833454 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- minidump/minidump_memory_info_writer_test.cc | 4 +++- minidump/minidump_string_writer_test.cc | 15 +++++++-------- minidump/minidump_system_info_writer_test.cc | 6 +++--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/minidump/minidump_memory_info_writer_test.cc b/minidump/minidump_memory_info_writer_test.cc index 73710392..634d3f1a 100644 --- a/minidump/minidump_memory_info_writer_test.cc +++ b/minidump/minidump_memory_info_writer_test.cc @@ -84,14 +84,16 @@ TEST(MinidumpMemoryInfoWriter, OneRegion) { auto memory_map_region = std::make_unique(); - MINIDUMP_MEMORY_INFO mmi = {0}; + MINIDUMP_MEMORY_INFO mmi; mmi.BaseAddress = 0x12340000; mmi.AllocationBase = 0x12000000; mmi.AllocationProtect = PAGE_READWRITE; + mmi.__alignment1 = 0; mmi.RegionSize = 0x6000; mmi.State = MEM_COMMIT; mmi.Protect = PAGE_NOACCESS; mmi.Type = MEM_PRIVATE; + mmi.__alignment2 = 0; memory_map_region->SetMindumpMemoryInfo(mmi); std::vector memory_map; diff --git a/minidump/minidump_string_writer_test.cc b/minidump/minidump_string_writer_test.cc index b8eb7030..382baaf7 100644 --- a/minidump/minidump_string_writer_test.cc +++ b/minidump/minidump_string_writer_test.cc @@ -84,12 +84,11 @@ TEST(MinidumpStringWriter, MinidumpUTF16StringWriter) { const size_t expected_utf16_units_with_nul = kTestData[index].output_length + 1; - MINIDUMP_STRING tmp = {0}; + MINIDUMP_STRING* tmp; ALLOW_UNUSED_LOCAL(tmp); const size_t expected_utf16_bytes = - expected_utf16_units_with_nul * sizeof(tmp.Buffer[0]); - ASSERT_EQ(string_file.string().size(), - sizeof(MINIDUMP_STRING) + expected_utf16_bytes); + expected_utf16_units_with_nul * sizeof(tmp->Buffer[0]); + ASSERT_EQ(string_file.string().size(), sizeof(*tmp) + expected_utf16_bytes); const MINIDUMP_STRING* minidump_string = MinidumpStringAtRVA(string_file.string(), 0); @@ -129,11 +128,11 @@ TEST(MinidumpStringWriter, ConvertInvalidUTF8ToUTF16) { const MINIDUMP_STRING* minidump_string = MinidumpStringAtRVA(string_file.string(), 0); EXPECT_TRUE(minidump_string); - MINIDUMP_STRING tmp = {0}; + MINIDUMP_STRING* tmp; ALLOW_UNUSED_LOCAL(tmp); - EXPECT_EQ(minidump_string->Length, - string_file.string().size() - sizeof(MINIDUMP_STRING) - - sizeof(tmp.Buffer[0])); + EXPECT_EQ( + minidump_string->Length, + string_file.string().size() - sizeof(*tmp) - sizeof(tmp->Buffer[0])); base::string16 output_string = MinidumpStringAtRVAAsString(string_file.string(), 0); EXPECT_FALSE(output_string.empty()); diff --git a/minidump/minidump_system_info_writer_test.cc b/minidump/minidump_system_info_writer_test.cc index c3d02472..99c599c1 100644 --- a/minidump/minidump_system_info_writer_test.cc +++ b/minidump/minidump_system_info_writer_test.cc @@ -39,11 +39,11 @@ void GetSystemInfoStream(const std::string& file_contents, const MINIDUMP_SYSTEM_INFO** system_info, const MINIDUMP_STRING** csd_version) { // The expected number of bytes for the CSD version’s MINIDUMP_STRING::Buffer. - MINIDUMP_STRING tmp = {0}; + MINIDUMP_STRING* tmp; ALLOW_UNUSED_LOCAL(tmp); - const size_t kCSDVersionBytes = csd_version_length * sizeof(tmp.Buffer[0]); + const size_t kCSDVersionBytes = csd_version_length * sizeof(tmp->Buffer[0]); const size_t kCSDVersionBytesWithNUL = - kCSDVersionBytes + sizeof(tmp.Buffer[0]); + kCSDVersionBytes + sizeof(tmp->Buffer[0]); constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); constexpr size_t kSystemInfoStreamOffset = From 2a51cb5fd50b6f45616928f729ff9dba90989126 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 11 Jan 2018 14:19:49 -0800 Subject: [PATCH 099/326] fuchsia: Fixes for finding GN when in bot configuration On buildbot, GN isn't in the path, and must be run with a full explicit path. GN saves this to the build.gn for regeneration purposes, so extract it out of there. Additionally, set cwd when calling GN, otherwise, it will be unable to find the .gn in the root of the source tree. Bug: crashpad:196 Change-Id: Ia14db175ba0af6dc61b215da6ba5c624bca56886 Reviewed-on: https://chromium-review.googlesource.com/862547 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- build/run_tests.py | 57 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/build/run_tests.py b/build/run_tests.py index e180a6cb..b33478cf 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -30,19 +30,54 @@ CRASHPAD_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), IS_WINDOWS_HOST = sys.platform.startswith('win') +def _FindGNFromBinaryDir(binary_dir): + """Attempts to determine the path to a GN binary used to generate the build + files in the given binary_dir. This is necessary because `gn` might not be in + the path or might be in a non-standard location, particularly on build + machines.""" + + build_ninja = os.path.join(binary_dir, 'build.ninja') + if os.path.isfile(build_ninja): + with open(build_ninja, 'rb') as f: + # Look for the always-generated regeneration rule of the form: + # + # rule gn + # command = ... arguments ... + # + # to extract the gn binary's full path. + found_rule_gn = False + for line in f: + if line.strip() == 'rule gn': + found_rule_gn = True + continue + if found_rule_gn: + if len(line) == 0 or line[0] != ' ': + return None + if line.startswith(' command = '): + gn_command_line_parts = line.strip().split(' ') + if len(gn_command_line_parts) > 2: + return gn_command_line_parts[2] + + return None + + def _BinaryDirTargetOS(binary_dir): """Returns the apparent target OS of binary_dir, or None if none appear to be explicitly specified.""" - # Look for a GN “target_os”. - popen = subprocess.Popen( - ['gn', 'args', binary_dir, '--list=target_os', '--short'], - shell=IS_WINDOWS_HOST, stdout=subprocess.PIPE, stderr=open(os.devnull)) - value = popen.communicate()[0] - if popen.returncode == 0: - match = re.match('target_os = "(.*)"$', value.decode('utf-8')) - if match: - return match.group(1) + gn_path = _FindGNFromBinaryDir(binary_dir) + + if gn_path: + # Look for a GN “target_os”. + popen = subprocess.Popen( + [gn_path, 'args', binary_dir, '--list=target_os', '--short'], + shell=IS_WINDOWS_HOST, stdout=subprocess.PIPE, stderr=open(os.devnull), + cwd=CRASHPAD_DIR) + value = popen.communicate()[0] + if popen.returncode == 0: + match = re.match('target_os = "(.*)"$', value.decode('utf-8')) + if match: + return match.group(1) # For GYP with Ninja, look for the appearance of “linux-android” in the path # to ar. This path is configured by gyp_crashpad_android.py. @@ -277,8 +312,10 @@ def _GenerateFuchsiaRuntimeDepsFiles(binary_dir, tests): targets_file = os.path.abspath(os.path.join(binary_dir, 'targets.txt')) with open(targets_file, 'wb') as f: f.write('//:' + '\n//:'.join(tests) + '\n') + gn_path = _FindGNFromBinaryDir(binary_dir) subprocess.check_call( - ['gn', 'gen', binary_dir, '--runtime-deps-list-file=' + targets_file]) + [gn_path, 'gen', binary_dir, '--runtime-deps-list-file=' + targets_file], + cwd=CRASHPAD_DIR) def _HandleOutputFromFuchsiaLogListener(process, done_message): From f35799cb410f89003c5ae84ab6e711eb4b9e3321 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 11 Jan 2018 15:46:36 -0800 Subject: [PATCH 100/326] fuchsia: Ensure qemu instance is ready before continuing after 'start' Ensures that enough networking is set up before returning so that the VM will be ready to accept commands to run tests. Otherwise requests to do so immediately after the VM is started can flakily fail. Bug: crashpad:196 Change-Id: Idc231e7fc418ad054bb9ec115c1598c804055a96 Reviewed-on: https://chromium-review.googlesource.com/862709 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- build/run_fuchsia_qemu.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/build/run_fuchsia_qemu.py b/build/run_fuchsia_qemu.py index e3f4efc3..edc51db3 100755 --- a/build/run_fuchsia_qemu.py +++ b/build/run_fuchsia_qemu.py @@ -27,6 +27,7 @@ import signal import subprocess import sys import tempfile +import time try: from subprocess import DEVNULL @@ -57,7 +58,7 @@ def _CheckForTun(): if returncode != 0: print('To use QEMU with networking on Linux, configure TUN/TAP. See:', file=sys.stderr) - print(' https://fuchsia.googlesource.com/magenta/+/HEAD/docs/qemu.md#enabling-networking-under-qemu-x86_64-only', + print(' https://fuchsia.googlesource.com/zircon/+/HEAD/docs/qemu.md#enabling-networking-under-qemu-x86_64-only', file=sys.stderr) return 2 return 0 @@ -77,6 +78,8 @@ def _Start(pid_file): initrd_path = os.path.join(kernel_data_dir, 'bootdata.bin') mac_tail = ':'.join('%02x' % random.randint(0, 255) for x in range(3)) + instance_name = 'crashpad_qemu_' + \ + ''.join(chr(random.randint(ord('A'), ord('Z'))) for x in range(8)) # These arguments are from the Fuchsia repo in zircon/scripts/run-zircon. popen = subprocess.Popen([ @@ -93,12 +96,22 @@ def _Start(pid_file): '-enable-kvm', '-netdev', 'type=tap,ifname=qemu,script=no,downscript=no,id=net0', '-device', 'e1000,netdev=net0,mac=52:54:00:' + mac_tail, - '-append', 'TERM=dumb', + '-append', 'TERM=dumb zircon.nodename=' + instance_name, ], stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL) with open(pid_file, 'wb') as f: f.write('%d\n' % popen.pid) + for i in range(10): + netaddr_path = os.path.join(fuchsia_dir, 'sdk', arch, 'tools', 'netaddr') + if subprocess.call([netaddr_path, '--nowait', instance_name], + stdout=open(os.devnull), stderr=open(os.devnull)) == 0: + break + time.sleep(.5) + else: + print('instance did not respond after start', file=sys.stderr) + return 1 + def main(args): if len(args) != 1 or args[0] not in ('start', 'stop'): From 3a07faf171876a821d22e6a6e0b3e648ab4074b7 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 11 Jan 2018 15:51:23 -0800 Subject: [PATCH 101/326] fuchsia: Fully initialize TestCrashpadInfo structure The in-Fuchsia build fails with: ../../third_party/crashpad/snapshot/crashpad_info_size_test_module.cc:89:77: error: missing field 'indirectly_referenced_memory_cap_' initializer [-Werror,-Wmissing-field-initializers] TestCrashpadInfo g_test_crashpad_info = {'CPad', sizeof(TestCrashpadInfo), 1}; kulakowski mentioned in the context of the = {0} CL recently that they've turned on some somewhat unusual warnings because they have a higher-than-usual amount of C code, as well as code that has to build as both C and C++. I think that's where this one comes from. Bug: crashpad:196 Change-Id: Ie1b373a32f99615366c7fcd65cd4ae4761385ff9 Reviewed-on: https://chromium-review.googlesource.com/862802 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- snapshot/crashpad_info_size_test_module.cc | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/snapshot/crashpad_info_size_test_module.cc b/snapshot/crashpad_info_size_test_module.cc index 1f5a811e..d39fada3 100644 --- a/snapshot/crashpad_info_size_test_module.cc +++ b/snapshot/crashpad_info_size_test_module.cc @@ -86,7 +86,25 @@ __declspec(allocate("CPADinfo")) #else // !defined(OS_POSIX) && !defined(OS_WIN) #error Port #endif // !defined(OS_POSIX) && !defined(OS_WIN) -TestCrashpadInfo g_test_crashpad_info = {'CPad', sizeof(TestCrashpadInfo), 1}; +TestCrashpadInfo g_test_crashpad_info = {'CPad', + sizeof(TestCrashpadInfo), + 1, + 0, + 0, + 0, + 0, + 0, + 0, + nullptr, + nullptr, +#if !defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL) + nullptr, + nullptr, +#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL +#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE) + {} +#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE +}; } // namespace } // namespace crashpad From 3bc4156c1e279d43e7312cd9ebb609c89e82f262 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 11 Jan 2018 17:36:12 -0800 Subject: [PATCH 102/326] fuchsia: Fix qemu script return code on failure https://build.chromium.org/p/client.crashpad/builders/crashpad_fuchsia_x64_dbg/builds/6 failed in "start qemu", but it wasn't clear that it did because the script returned 0 when it did so. Bug: crashpad:196 Change-Id: I7b8f42bc58c273b5b8e9a64e52288b32e9f9addf Reviewed-on: https://chromium-review.googlesource.com/862990 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- build/run_fuchsia_qemu.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/run_fuchsia_qemu.py b/build/run_fuchsia_qemu.py index edc51db3..135b314e 100755 --- a/build/run_fuchsia_qemu.py +++ b/build/run_fuchsia_qemu.py @@ -112,6 +112,8 @@ def _Start(pid_file): print('instance did not respond after start', file=sys.stderr) return 1 + return 0 + def main(args): if len(args) != 1 or args[0] not in ('start', 'stop'): @@ -123,7 +125,7 @@ def main(args): pid_file = os.path.join(tempfile.gettempdir(), 'crashpad_fuchsia_qemu_pid') _Stop(pid_file) if command == 'start': - _Start(pid_file) + return _Start(pid_file) return 0 From 54dc88724e7e13bb06293a66eac71238983000fd Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 12 Jan 2018 10:25:34 -0800 Subject: [PATCH 103/326] fuchsia: Fix run when gn binary path non-absolute Oops. I thought GN abspath()d this in the regen rule, but it does not. Bug: crashpad:196 Change-Id: I33dadc5502a5e56f20ba7e4c0403fbc138f052c9 Reviewed-on: https://chromium-review.googlesource.com/864629 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- build/run_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/run_tests.py b/build/run_tests.py index b33478cf..c25f5590 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -56,7 +56,7 @@ def _FindGNFromBinaryDir(binary_dir): if line.startswith(' command = '): gn_command_line_parts = line.strip().split(' ') if len(gn_command_line_parts) > 2: - return gn_command_line_parts[2] + return os.path.join(binary_dir, gn_command_line_parts[2]) return None From 4431f76d864b47c6ea8ee100068fbb60a145625f Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 12 Jan 2018 14:38:09 -0800 Subject: [PATCH 104/326] Switch run_tests.py to argparse, and support --gtest_filter Starting to work on individual tests on Fuchsia, and it's nice to be to run only one. Bug: crashpad:196 Change-Id: I1c5d924d2c93ee943673883de0a6022a3666f98c Reviewed-on: https://chromium-review.googlesource.com/865103 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- build/run_tests.py | 54 ++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/build/run_tests.py b/build/run_tests.py index c25f5590..fe5d6262 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -17,6 +17,7 @@ from __future__ import print_function +import argparse import os import pipes import posixpath @@ -132,7 +133,7 @@ def _EnableVTProcessingOnWindowsConsole(): return True -def _RunOnAndroidTarget(binary_dir, test, android_device): +def _RunOnAndroidTarget(binary_dir, test, android_device, extra_command_line): local_test_path = os.path.join(binary_dir, test) MAYBE_UNSUPPORTED_TESTS = ( 'crashpad_client_test', @@ -297,7 +298,7 @@ def _RunOnAndroidTarget(binary_dir, test, android_device): else: gtest_color = 'no' env['GTEST_COLOR'] = gtest_color - _adb_shell([posixpath.join(device_out_dir, test)], env) + _adb_shell([posixpath.join(device_out_dir, test)] + extra_command_line, env) finally: _adb_shell(['rm', '-rf', device_temp_dir]) @@ -337,7 +338,7 @@ def _HandleOutputFromFuchsiaLogListener(process, done_message): return success -def _RunOnFuchsiaTarget(binary_dir, test, device_name): +def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line): """Runs the given Fuchsia |test| executable on the given |device_name|. The device must already be booted. @@ -403,7 +404,7 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name): done_message = 'TERMINATED: ' + unique_id namespace_command = [ 'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root, '--', - staging_root + '/bin/' + test] + staging_root + '/bin/' + test] + extra_command_line netruncmd(namespace_command, ['echo', done_message]) success = _HandleOutputFromFuchsiaLogListener( @@ -418,12 +419,12 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name): # that are run is maintained in-tree, rather than in a separate infrastructure # location in the recipe. def main(args): - if len(args) != 1 and len(args) != 2: - print('usage: run_tests.py [test_to_run]', file=sys.stderr) - return 1 - - binary_dir = args[0] - single_test = args[1] if len(args) == 2 else None + parser = argparse.ArgumentParser(description='Run Crashpad unittests.') + parser.add_argument('binary_dir', help='Root of build dir') + parser.add_argument('test', nargs='*', help='Specific test(s) to run.') + parser.add_argument('--gtest_filter', + help='GTest filter applied to GTest binary runs.') + args = parser.parse_args() # Tell 64-bit Windows tests where to find 32-bit test executables, for # cross-bitted testing. This relies on the fact that the GYP build by default @@ -431,13 +432,13 @@ def main(args): # 64-bit build. This is not a universally valid assumption, and if it’s not # met, 64-bit tests that require 32-bit build output will disable themselves # dynamically. - if (sys.platform == 'win32' and binary_dir.endswith('_x64') and + if (sys.platform == 'win32' and args.binary_dir.endswith('_x64') and 'CRASHPAD_TEST_32_BIT_OUTPUT' not in os.environ): - binary_dir_32 = binary_dir[:-4] + binary_dir_32 = args.binary_dir[:-4] if os.path.isdir(binary_dir_32): os.environ['CRASHPAD_TEST_32_BIT_OUTPUT'] = binary_dir_32 - target_os = _BinaryDirTargetOS(binary_dir) + target_os = _BinaryDirTargetOS(args.binary_dir) is_android = target_os == 'android' is_fuchsia = target_os == 'fuchsia' @@ -482,15 +483,16 @@ def main(args): zircon_nodename = devices[0].strip().split()[1] print('Using autodetected Fuchsia device:', zircon_nodename) _GenerateFuchsiaRuntimeDepsFiles( - binary_dir, [t for t in tests if not t.endswith('.py')]) + args.binary_dir, [t for t in tests if not t.endswith('.py')]) elif IS_WINDOWS_HOST: tests.append('snapshot/win/end_to_end_test.py') - if single_test: - if single_test not in tests: - print('Unrecognized test:', single_test, file=sys.stderr) - return 3 - tests = [single_test] + if args.test: + for t in args.test: + if t not in tests: + print('Unrecognized test:', t, file=sys.stderr) + return 3 + tests = args.test for test in tests: print('-' * 80) @@ -498,14 +500,20 @@ def main(args): print('-' * 80) if test.endswith('.py'): subprocess.check_call( - [sys.executable, os.path.join(CRASHPAD_DIR, test), binary_dir]) + [sys.executable, os.path.join(CRASHPAD_DIR, test), args.binary_dir]) else: + extra_command_line = [] + if args.gtest_filter: + extra_command_line.append('--gtest_filter=' + args.gtest_filter) if is_android: - _RunOnAndroidTarget(binary_dir, test, android_device) + _RunOnAndroidTarget(args.binary_dir, test, android_device, + extra_command_line) elif is_fuchsia: - _RunOnFuchsiaTarget(binary_dir, test, zircon_nodename) + _RunOnFuchsiaTarget(args.binary_dir, test, zircon_nodename, + extra_command_line) else: - subprocess.check_call(os.path.join(binary_dir, test)) + subprocess.check_call([os.path.join(args.binary_dir, test)] + + extra_command_line) return 0 From 1d1bf89d62eabba1e6db9c9aab7ad20f0f56b857 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 12 Jan 2018 16:03:03 -0800 Subject: [PATCH 105/326] Roll buildtools to 6fe4a3251488f7af86d64fc25cf442e817cf6133 Includes a number of gn rolls. Change-Id: Id2cdbaca493b6537de9741109bb81ef8e05327fa Reviewed-on: https://chromium-review.googlesource.com/865829 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index b197e656..d4bb9a65 100644 --- a/DEPS +++ b/DEPS @@ -19,7 +19,7 @@ vars = { deps = { 'buildtools': Var('chromium_git') + '/chromium/buildtools.git@' + - '505de88083136eefd056e5ee4ca0f01fe9b33de8', + '6fe4a3251488f7af86d64fc25cf442e817cf6133', 'crashpad/third_party/gtest/gtest': Var('chromium_git') + '/external/github.com/google/googletest@' + 'd175c8bf823e709d570772b038757fadf63bc632', From 970ac5a636b94c4e88f5ae25d83070b6b45064a8 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 12 Jan 2018 17:21:48 -0800 Subject: [PATCH 106/326] fuchsia: Package setup for crashpad_test for in-Fuchsia build third_party/googletest has been added to the Fuchsia manifest now (which is a recent copy of googletest that includes googlemock). The BUILD.gn file isn't there yet, but is in progress. With these changes, all targets build in-Fuchsia once the googletest BUILD.gn file lands. Bug: crashpad:196 Change-Id: I34d2d7046dd2762b4d1f4873be97258df32adddf Reviewed-on: https://chromium-review.googlesource.com/861242 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- BUILD.gn | 22 ++++++++++++++++++++-- third_party/gtest/BUILD.gn | 27 +++++++++++++++++++-------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 9f386afa..ffd1c576 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -19,7 +19,7 @@ config("crashpad_config") { include_dirs = [ "." ] } -if (crashpad_is_in_chromium) { +if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { test("crashpad_tests") { deps = [ "client:client_test", @@ -31,7 +31,25 @@ if (crashpad_is_in_chromium) { "util:util_test", ] } -} else { + + if (crashpad_is_in_fuchsia) { + import("//build/package.gni") + package("crashpad_test") { + testonly = true + system_image = true + + deps = [ + ":crashpad_tests", + ] + + tests = [ + { + name = "crashpad_tests" + }, + ] + } + } +} else if (crashpad_is_standalone) { test("crashpad_client_test") { deps = [ "client:client_test", diff --git a/third_party/gtest/BUILD.gn b/third_party/gtest/BUILD.gn index d6160ef1..1a026119 100644 --- a/third_party/gtest/BUILD.gn +++ b/third_party/gtest/BUILD.gn @@ -32,13 +32,14 @@ if (crashpad_is_in_chromium) { group("gtest") { testonly = true public_deps = [ - "//third_party/gtest", + "//third_party/googletest:gtest", ] } group("gmock") { testonly = true - # TODO(scottmg): Fuchsia doesn't have a third_party/gmock, and has a - # pre-gmock-integration gtest. + public_deps = [ + "//third_party/googletest:gmock", + ] } } else if (crashpad_is_standalone) { config("gtest_private_config") { @@ -90,7 +91,9 @@ if (crashpad_is_in_chromium) { ] sources -= [ "gtest/googletest/src/gtest-all.cc" ] public_configs = [ ":gtest_public_config" ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs -= [ + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", + ] configs += [ ":gtest_private_config" ] } @@ -127,7 +130,9 @@ if (crashpad_is_in_chromium) { "gtest/googletest/test/production.cc", "gtest/googletest/test/production.h", ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs -= [ + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", + ] configs += [ ":gtest_private_config" ] deps = [ ":gtest", @@ -169,7 +174,9 @@ if (crashpad_is_in_chromium) { "gtest/googletest/test/gtest-param-test_test.cc", "gtest/googletest/test/gtest-param-test_test.h", ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs -= [ + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", + ] configs += [ ":gtest_private_config" ] deps = [ ":gtest", @@ -297,7 +304,9 @@ if (crashpad_is_in_chromium) { ] sources -= [ "gtest/googlemock/src/gmock-all.cc" ] public_configs = [ ":gmock_public_config" ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs -= [ + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", + ] configs += [ ":gmock_private_config" ] deps = [ ":gtest", @@ -370,7 +379,9 @@ if (crashpad_is_in_chromium) { sources = [ "gtest/googlemock/test/gmock_stress_test.cc", ] - configs -= [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] + configs -= [ + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", + ] configs += [ ":gmock_private_config" ] deps = [ ":gmock", From 4d2414e38e47e3b223d8646e109928f822b367a4 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 16 Jan 2018 14:46:29 -0800 Subject: [PATCH 107/326] fuchsia: Add implemention of ProcessMemory Bug: crashpad:196 Change-Id: I04a29afcbb16b5ef14d6f83d7af1d954f5164ee9 Reviewed-on: https://chromium-review.googlesource.com/868736 Reviewed-by: Mark Mentovai Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- util/BUILD.gn | 13 ++++++ util/misc/address_types.h | 7 +++ util/process/process_memory.cc | 55 ++++++++++++++++++++++ util/process/process_memory.h | 2 +- util/process/process_memory_fuchsia.cc | 57 +++++++++++++++++++++++ util/process/process_memory_fuchsia.h | 56 ++++++++++++++++++++++ util/process/process_memory_linux.cc | 48 ++----------------- util/process/process_memory_linux.h | 7 +-- util/process/process_memory_range_test.cc | 13 ++++++ util/process/process_memory_test.cc | 6 +++ 10 files changed, 214 insertions(+), 50 deletions(-) create mode 100644 util/process/process_memory.cc create mode 100644 util/process/process_memory_fuchsia.cc create mode 100644 util/process/process_memory_fuchsia.h diff --git a/util/BUILD.gn b/util/BUILD.gn index 3008a6e7..fb6d71aa 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -276,6 +276,13 @@ static_library("util") { "posix/process_info_linux.cc", "process/process_memory_linux.cc", "process/process_memory_linux.h", + ] + } + + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ + "process/process_memory.cc", + "process/process_memory.h", # TODO: Port to all platforms. "process/process_memory_range.cc", @@ -364,6 +371,8 @@ static_library("util") { sources += [ "misc/paths_fuchsia.cc", "net/http_transport_fuchsia.cc", + "process/process_memory_fuchsia.cc", + "process/process_memory_fuchsia.h", ] } @@ -509,7 +518,11 @@ source_set("util_test") { "linux/ptrace_broker_test.cc", "linux/ptracer_test.cc", "linux/scoped_ptrace_attach_test.cc", + ] + } + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ # TODO: Port to all platforms. "process/process_memory_range_test.cc", "process/process_memory_test.cc", diff --git a/util/misc/address_types.h b/util/misc/address_types.h index bfce35fc..870938d3 100644 --- a/util/misc/address_types.h +++ b/util/misc/address_types.h @@ -27,6 +27,8 @@ #include "util/win/address_types.h" #elif defined(OS_LINUX) || defined(OS_ANDROID) #include "util/linux/address_types.h" +#elif defined(OS_FUCHSIA) +#include #else #error "Unhandled OS type" #endif @@ -58,6 +60,11 @@ using VMSize = WinVMSize; using VMAddress = LinuxVMAddress; using VMSize = LinuxVMSize; +#elif defined(OS_FUCHSIA) + +using VMAddress = zx_vaddr_t; +using VMSize = size_t; + #endif //! \brief Type used to represent an offset from a VMAddress, potentially diff --git a/util/process/process_memory.cc b/util/process/process_memory.cc new file mode 100644 index 00000000..2d7460dd --- /dev/null +++ b/util/process/process_memory.cc @@ -0,0 +1,55 @@ +// Copyright 2018 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 "util/process/process_memory.h" + +#include "base/logging.h" + +namespace crashpad { + +bool ProcessMemory::ReadCStringInternal(VMAddress address, + bool has_size, + size_t size, + std::string* string) const { + string->clear(); + + char buffer[4096]; + do { + size_t read_size; + if (has_size) { + read_size = std::min(sizeof(buffer), size); + } else { + read_size = sizeof(buffer); + } + + if (!Read(address, read_size, buffer)) { + break; + } + + char* nul = static_cast(memchr(buffer, '\0', read_size)); + if (nul != nullptr) { + string->append(buffer, nul - buffer); + return true; + } + string->append(buffer, read_size); + + address += read_size; + size -= read_size; + } while (!has_size || size > 0); + + LOG(ERROR) << "unterminated string"; + return false; +} + +} // namespace crashpad diff --git a/util/process/process_memory.h b/util/process/process_memory.h index c046fc6e..5126f0c8 100644 --- a/util/process/process_memory.h +++ b/util/process/process_memory.h @@ -102,7 +102,7 @@ class ProcessMemory { virtual bool ReadCStringInternal(VMAddress address, bool has_size, size_t size, - std::string* string) const = 0; + std::string* string) const; }; } // namespace crashpad diff --git a/util/process/process_memory_fuchsia.cc b/util/process/process_memory_fuchsia.cc new file mode 100644 index 00000000..9785bd46 --- /dev/null +++ b/util/process/process_memory_fuchsia.cc @@ -0,0 +1,57 @@ +// Copyright 2018 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 "util/process/process_memory_fuchsia.h" + +#include + +#include "base/logging.h" +#include "base/fuchsia/fuchsia_logging.h" + +namespace crashpad { + +ProcessMemoryFuchsia::ProcessMemoryFuchsia() + : ProcessMemory(), process_(), initialized_() {} + +ProcessMemoryFuchsia::~ProcessMemoryFuchsia() {} + +bool ProcessMemoryFuchsia::Initialize(zx_handle_t process) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + process_ = process; + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessMemoryFuchsia::Read(VMAddress address, + size_t size, + void* buffer) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + size_t actual; + zx_status_t status = + zx_process_read_memory(process_, address, buffer, size, &actual); + + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_process_read_memory"; + return false; + } + + if (actual != size) { + LOG(ERROR) << "zx_process_read_memory: short read"; + return false; + } + + return true; +} + +} // namespace crashpad diff --git a/util/process/process_memory_fuchsia.h b/util/process/process_memory_fuchsia.h new file mode 100644 index 00000000..fa3b3318 --- /dev/null +++ b/util/process/process_memory_fuchsia.h @@ -0,0 +1,56 @@ +// Copyright 2018 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_UTIL_PROCESS_PROCESS_MEMORY_FUCHSIA_H_ +#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_FUCHSIA_H_ + +#include + +#include + +#include "base/macros.h" +#include "util/misc/address_types.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/process/process_memory.h" + +namespace crashpad { + +//! \brief Accesses the memory of another Fuchsia process. +class ProcessMemoryFuchsia final : public ProcessMemory { + public: + ProcessMemoryFuchsia(); + ~ProcessMemoryFuchsia(); + + //! \brief Initializes this object to read the memory of a process by handle. + //! + //! This method must be called successfully prior to calling any other method + //! in this class. + //! + //! \param[in] pid The handle to the target process. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool Initialize(zx_handle_t process); + + bool Read(VMAddress address, size_t size, void* buffer) const override; + + private: + zx_handle_t process_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcessMemoryFuchsia); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_FUCHSIA_H_ diff --git a/util/process/process_memory_linux.cc b/util/process/process_memory_linux.cc index f294ee34..c1e770aa 100644 --- a/util/process/process_memory_linux.cc +++ b/util/process/process_memory_linux.cc @@ -27,11 +27,12 @@ namespace crashpad { ProcessMemoryLinux::ProcessMemoryLinux() - : ProcessMemory(), mem_fd_(), pid_(-1) {} + : ProcessMemory(), mem_fd_(), pid_(-1), initialized_() {} ProcessMemoryLinux::~ProcessMemoryLinux() {} bool ProcessMemoryLinux::Initialize(pid_t pid) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); pid_ = pid; char path[32]; snprintf(path, sizeof(path), "/proc/%d/mem", pid_); @@ -40,12 +41,14 @@ bool ProcessMemoryLinux::Initialize(pid_t pid) { PLOG(ERROR) << "open"; return false; } + INITIALIZATION_STATE_SET_VALID(initialized_); return true; } bool ProcessMemoryLinux::Read(VMAddress address, size_t size, void* buffer) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); DCHECK(mem_fd_.is_valid()); char* buffer_c = static_cast(buffer); @@ -68,47 +71,4 @@ bool ProcessMemoryLinux::Read(VMAddress address, return true; } -bool ProcessMemoryLinux::ReadCStringInternal(VMAddress address, - bool has_size, - size_t size, - std::string* string) const { - DCHECK(mem_fd_.is_valid()); - - string->clear(); - - char buffer[4096]; - do { - size_t read_size; - if (has_size) { - read_size = std::min(sizeof(buffer), size); - } else { - read_size = sizeof(buffer); - } - ssize_t bytes_read; - bytes_read = - HANDLE_EINTR(pread64(mem_fd_.get(), buffer, read_size, address)); - if (bytes_read < 0) { - PLOG(ERROR) << "pread64"; - return false; - } - if (bytes_read == 0) { - break; - } - DCHECK_LE(static_cast(bytes_read), read_size); - - char* nul = static_cast(memchr(buffer, '\0', bytes_read)); - if (nul != nullptr) { - string->append(buffer, nul - buffer); - return true; - } - string->append(buffer, bytes_read); - - address += bytes_read; - size -= bytes_read; - } while (!has_size || size > 0); - - LOG(ERROR) << "unterminated string"; - return false; -} - } // namespace crashpad diff --git a/util/process/process_memory_linux.h b/util/process/process_memory_linux.h index e03e251f..7aecdfc4 100644 --- a/util/process/process_memory_linux.h +++ b/util/process/process_memory_linux.h @@ -22,6 +22,7 @@ #include "base/files/scoped_file.h" #include "base/macros.h" #include "util/misc/address_types.h" +#include "util/misc/initialization_state_dcheck.h" #include "util/process/process_memory.h" namespace crashpad { @@ -46,13 +47,9 @@ class ProcessMemoryLinux final : public ProcessMemory { bool Read(VMAddress address, size_t size, void* buffer) const override; private: - bool ReadCStringInternal(VMAddress address, - bool has_size, - size_t size, - std::string* string) const override; - base::ScopedFD mem_fd_; pid_t pid_; + InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ProcessMemoryLinux); }; diff --git a/util/process/process_memory_range_test.cc b/util/process/process_memory_range_test.cc index c4cab7e0..1c785068 100644 --- a/util/process/process_memory_range_test.cc +++ b/util/process/process_memory_range_test.cc @@ -22,7 +22,14 @@ #include "build/build_config.h" #include "gtest/gtest.h" #include "util/misc/from_pointer_cast.h" + +#if defined(OS_FUCHSIA) +#include + +#include "util/process/process_memory_fuchsia.h" +#else #include "util/process/process_memory_linux.h" +#endif namespace crashpad { namespace test { @@ -34,6 +41,11 @@ struct TestObject { } kTestObject = {"string1", "string2"}; TEST(ProcessMemoryRange, Basic) { +#if defined(OS_FUCHSIA) + ProcessMemoryFuchsia memory; + ASSERT_TRUE(memory.Initialize(zx_process_self())); + constexpr bool is_64_bit = true; +#else pid_t pid = getpid(); #if defined(ARCH_CPU_64_BITS) constexpr bool is_64_bit = true; @@ -43,6 +55,7 @@ TEST(ProcessMemoryRange, Basic) { ProcessMemoryLinux memory; ASSERT_TRUE(memory.Initialize(pid)); +#endif // OS_FUCHSIA ProcessMemoryRange range; ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); diff --git a/util/process/process_memory_test.cc b/util/process/process_memory_test.cc index 5f631237..b6987a10 100644 --- a/util/process/process_memory_test.cc +++ b/util/process/process_memory_test.cc @@ -32,6 +32,10 @@ namespace crashpad { namespace test { namespace { +// TODO(scottmg): https://crashpad.chromium.org/bug/196. Multiprocess isn't +// ported yet. +#if !defined(OS_FUCHSIA) + class TargetProcessTest : public Multiprocess { public: TargetProcessTest() : Multiprocess() {} @@ -400,6 +404,8 @@ TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedForked) { test.RunAgainstForked(); } +#endif // !defined(OS_FUCHSIA) + } // namespace } // namespace test } // namespace crashpad From 380a1e9eafa05a0bd5704f999d16f598f7dc0776 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 17 Jan 2018 10:21:49 -0800 Subject: [PATCH 108/326] Make multiprocess_exec_test_child depend on base to get OS_ defines Some doofus really wanted to shoot himself in the foot ( https://codereview.chromium.org/808493003/diff/80001/util/test/multiprocess_exec_test_child.cc ), and it worked. Depend on base to get build_config.h instead, so that OS_POSIX is also set on Fuchsia. Also, don't call getrlimit() on Fuchsia. Bug: crashpad:196 Change-Id: I102de22751fd90889bfb59b6c4a577e99357094b Reviewed-on: https://chromium-review.googlesource.com/868742 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- test/BUILD.gn | 4 +++ test/multiprocess_exec_test_child.cc | 40 +++++++++++++++------------- test/test_test.gyp | 3 +++ 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/test/BUILD.gn b/test/BUILD.gn index 2b7991e2..a937854e 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -166,6 +166,10 @@ executable("crashpad_test_test_multiprocess_exec_test_child") { sources = [ "multiprocess_exec_test_child.cc", ] + + deps = [ + "../third_party/mini_chromium:base", + ] } static_library("gmock_main") { diff --git a/test/multiprocess_exec_test_child.cc b/test/multiprocess_exec_test_child.cc index 011a37d0..f2761ea5 100644 --- a/test/multiprocess_exec_test_child.cc +++ b/test/multiprocess_exec_test_child.cc @@ -15,16 +15,12 @@ #include #include #include -#include #include #include -#if defined(__APPLE__) || defined(__linux__) -#define OS_POSIX 1 -#elif defined(_WIN32) -#define OS_WIN 1 -#endif +#include "base/logging.h" +#include "build/build_config.h" #if defined(OS_POSIX) #include @@ -35,18 +31,26 @@ int main(int argc, char* argv[]) { #if defined(OS_POSIX) + +#if defined(OS_FUCHSIA) + // getrlimit() is not implemented on Fuchsia. By construction, the child only + // receieves specific fds that it's given, but check low values as mild + // verification. + int last_fd = 1024; +#else + rlimit rlimit_nofile; + if (getrlimit(RLIMIT_NOFILE, &rlimit_nofile) != 0) { + LOG(FATAL) << "getrlimit"; + } + int last_fd = static_cast(rlimit_nofile.rlim_cur); +#endif // OS_FUCHSIA + // Make sure that there’s nothing open at any FD higher than 3. All FDs other // than stdin, stdout, and stderr should have been closed prior to or at // exec(). - rlimit rlimit_nofile; - if (getrlimit(RLIMIT_NOFILE, &rlimit_nofile) != 0) { - abort(); - } - for (int fd = STDERR_FILENO + 1; - fd < static_cast(rlimit_nofile.rlim_cur); - ++fd) { + for (int fd = STDERR_FILENO + 1; fd < last_fd; ++fd) { if (close(fd) == 0 || errno != EBADF) { - abort(); + LOG(FATAL) << "close"; } } @@ -54,14 +58,14 @@ int main(int argc, char* argv[]) { char c; ssize_t rv = read(STDIN_FILENO, &c, 1); if (rv != 1 || c != 'z') { - abort(); + LOG(FATAL) << "read"; } // Write a byte to stdout. c = 'Z'; rv = write(STDOUT_FILENO, &c, 1); if (rv != 1) { - abort(); + LOG(FATAL) << "write"; } #elif defined(OS_WIN) // TODO(scottmg): Verify that only the handles we expect to be open, are. @@ -72,7 +76,7 @@ int main(int argc, char* argv[]) { HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE); if (!ReadFile(stdin_handle, &c, 1, &bytes_read, nullptr) || bytes_read != 1 || c != 'z') { - abort(); + LOG(FATAL) << "ReadFile"; } // Write a byte to stdout. @@ -81,7 +85,7 @@ int main(int argc, char* argv[]) { if (!WriteFile( GetStdHandle(STD_OUTPUT_HANDLE), &c, 1, &bytes_written, nullptr) || bytes_written != 1) { - abort(); + LOG(FATAL) << "WriteFile"; } #endif // OS_POSIX diff --git a/test/test_test.gyp b/test/test_test.gyp index a31650da..3c6d7066 100644 --- a/test/test_test.gyp +++ b/test/test_test.gyp @@ -48,6 +48,9 @@ { 'target_name': 'crashpad_test_test_multiprocess_exec_test_child', 'type': 'executable', + 'dependencies': [ + '../third_party/mini_chromium/mini_chromium.gyp:base', + ], 'sources': [ 'multiprocess_exec_test_child.cc', ], From f62a30e977f55b8986579e3f98957761f61875c7 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 17 Jan 2018 14:03:39 -0800 Subject: [PATCH 109/326] fuchsia: Implementation of MultiprocessExec This supports multiprocess tests of the non-fork() variety. Also, improve directory finding so that the crashpad_test_test_multiprocess_exec_test_child binary can be located correctly on Fuchsia. Doc ref for launchpad: https://fuchsia.googlesource.com/zircon/+/master/system/ulib/launchpad/include/launchpad/launchpad.h#23 Also, roll mini_chromium to pick up ScopedZxHandle addition. Includes: a19ef08 Merge ScopedZxHandle from Chromium base f21c900 fuchsia: Move zircon libs dep to base, rather than global Bug: crashpad:196 Change-Id: Id01dee43f2d04e682e70c12777aff41f8dd848d6 Reviewed-on: https://chromium-review.googlesource.com/868967 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- DEPS | 2 +- test/BUILD.gn | 21 ++-- test/multiprocess.h | 14 ++- test/multiprocess_exec_fuchsia.cc | 155 ++++++++++++++++++++++++++++++ test/multiprocess_exec_win.cc | 4 - test/test_paths.cc | 8 +- util/misc/paths_fuchsia.cc | 5 +- 7 files changed, 185 insertions(+), 24 deletions(-) create mode 100644 test/multiprocess_exec_fuchsia.cc diff --git a/DEPS b/DEPS index d4bb9a65..7741afcd 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'edfe51ce818e55eae73ab3f144a360e855533888', + 'a19ef08ada53345fd9277f3d9434bc35f0e13a55', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/test/BUILD.gn b/test/BUILD.gn index a937854e..e59cc2af 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -42,13 +42,13 @@ static_library("test") { ] if (crashpad_is_posix) { - sources += [ - "multiprocess_posix.cc", - "scoped_temp_dir_posix.cc", - ] + sources += [ "scoped_temp_dir_posix.cc" ] if (!crashpad_is_fuchsia) { - sources += [ "multiprocess_exec_posix.cc" ] + sources += [ + "multiprocess_exec_posix.cc", + "multiprocess_posix.cc", + ] } } @@ -91,6 +91,11 @@ static_library("test") { ] } + if (crashpad_is_fuchsia) { + sources += [ "multiprocess_exec_fuchsia.cc" ] + libs = [ "launchpad" ] + } + public_configs = [ "..:crashpad_config" ] configs += [ "../build:crashpad_is_in_chromium" ] @@ -121,6 +126,7 @@ source_set("test_test") { sources = [ "hex_string_test.cc", "main_arguments_test.cc", + "multiprocess_exec_test.cc", "scoped_temp_dir_test.cc", "test_paths_test.cc", ] @@ -141,11 +147,6 @@ source_set("test_test") { } if (!crashpad_is_fuchsia) { - sources += [ - # TODO(scottmg): A MultiprocessExecFuchsia is probably desirable, but - # hasn't been implemented yet. - "multiprocess_exec_test.cc", - ] } deps = [ diff --git a/test/multiprocess.h b/test/multiprocess.h index b0ff9870..35d18b2f 100644 --- a/test/multiprocess.h +++ b/test/multiprocess.h @@ -36,8 +36,8 @@ struct MultiprocessInfo; //! Subclasses are expected to implement the parent and child by overriding the //! appropriate methods. //! -//! On Windows, this class is only an internal implementation detail of -//! MultiprocessExec and all tests must use that class. +//! On Windows and Fuchsia, this class is only an internal implementation +//! detail of MultiprocessExec and all tests must use that class. class Multiprocess { public: //! \brief The termination type for a child process. @@ -110,14 +110,18 @@ class Multiprocess { //! //! Subclass implementations may signal failure by raising their own fatal //! gtest assertions. - virtual void PreFork(); + virtual void PreFork() +#if defined(OS_WIN) || defined(OS_FUCHSIA) + = 0 +#endif // OS_WIN || OS_FUCHSIA + ; -#if !defined(OS_WIN) +#if !defined(OS_WIN) && !defined(OS_FUCHSIA) //! \brief Returns the child process’ process ID. //! //! This method may only be called by the parent process. pid_t ChildPID() const; -#endif // !OS_WIN +#endif // !OS_WIN && !OS_FUCHSIA //! \brief Returns the read pipe’s file handle. //! diff --git a/test/multiprocess_exec_fuchsia.cc b/test/multiprocess_exec_fuchsia.cc new file mode 100644 index 00000000..324e7eb0 --- /dev/null +++ b/test/multiprocess_exec_fuchsia.cc @@ -0,0 +1,155 @@ +// Copyright 2018 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 "test/multiprocess_exec.h" + +#include +#include +#include + +#include "base/files/scoped_file.h" +#include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/scoped_zx_handle.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { + +namespace internal { + +struct MultiprocessInfo { + MultiprocessInfo() {} + base::ScopedFD stdin_write; + base::ScopedFD stdout_read; + base::ScopedZxHandle child; +}; + +} // namespace internal + +Multiprocess::Multiprocess() + : info_(nullptr), + code_(EXIT_SUCCESS), + reason_(kTerminationNormal) { +} + +void Multiprocess::Run() { + // Set up and spawn the child process. + ASSERT_NO_FATAL_FAILURE(PreFork()); + RunChild(); + + // And then run the parent actions in this process. + RunParent(); + + // Reap the child. + zx_signals_t signals; + ASSERT_EQ( + zx_object_wait_one( + info_->child.get(), ZX_TASK_TERMINATED, ZX_TIME_INFINITE, &signals), + ZX_OK); + ASSERT_EQ(signals, ZX_TASK_TERMINATED); +} + +Multiprocess::~Multiprocess() { + delete info_; +} + +FileHandle Multiprocess::ReadPipeHandle() const { + return info_->stdout_read.get(); +} + +FileHandle Multiprocess::WritePipeHandle() const { + return info_->stdin_write.get(); +} + +void Multiprocess::CloseReadPipe() { + info_->stdout_read.reset(); +} + +void Multiprocess::CloseWritePipe() { + info_->stdin_write.reset(); +} + +void Multiprocess::RunParent() { + MultiprocessParent(); + + info_->stdout_read.reset(); + info_->stdin_write.reset(); +} + +void Multiprocess::RunChild() { + MultiprocessChild(); +} + +MultiprocessExec::MultiprocessExec() + : Multiprocess(), command_(), arguments_(), argv_() { +} + +void MultiprocessExec::SetChildCommand( + const base::FilePath& command, + const std::vector* arguments) { + command_ = command; + if (arguments) { + arguments_ = *arguments; + } else { + arguments_.clear(); + } +} + +MultiprocessExec::~MultiprocessExec() {} + +void MultiprocessExec::PreFork() { + ASSERT_FALSE(command_.empty()); + + ASSERT_TRUE(argv_.empty()); + + argv_.push_back(command_.value().c_str()); + for (const std::string& argument : arguments_) { + argv_.push_back(argument.c_str()); + } + + ASSERT_EQ(info(), nullptr); + set_info(new internal::MultiprocessInfo()); +} + +void MultiprocessExec::MultiprocessChild() { + launchpad_t* lp; + launchpad_create(zx_job_default(), command_.value().c_str(), &lp); + launchpad_load_from_file(lp, command_.value().c_str()); + launchpad_set_args(lp, argv_.size(), &argv_[0]); + + // Pass the filesystem namespace, parent environment, and default job to the + // child, but don't include any other file handles, preferring to set them + // up explicitly below. + launchpad_clone( + lp, LP_CLONE_FDIO_NAMESPACE | LP_CLONE_ENVIRON | LP_CLONE_DEFAULT_JOB); + + int stdin_parent_side; + launchpad_add_pipe(lp, &stdin_parent_side, STDIN_FILENO); + info()->stdin_write.reset(stdin_parent_side); + + int stdout_parent_side; + launchpad_add_pipe(lp, &stdout_parent_side, STDOUT_FILENO); + info()->stdout_read.reset(stdout_parent_side); + + launchpad_clone_fd(lp, STDERR_FILENO, STDERR_FILENO); + + const char* error_message; + zx_handle_t child; + zx_status_t status = launchpad_go(lp, &child, &error_message); + ZX_CHECK(status == ZX_OK, status) << "launchpad_go: " << error_message; + info()->child.reset(child); +} + +} // namespace test +} // namespace crashpad diff --git a/test/multiprocess_exec_win.cc b/test/multiprocess_exec_win.cc index f3a10c42..98fd0ac1 100644 --- a/test/multiprocess_exec_win.cc +++ b/test/multiprocess_exec_win.cc @@ -61,10 +61,6 @@ Multiprocess::~Multiprocess() { delete info_; } -void Multiprocess::PreFork() { - NOTREACHED(); -} - FileHandle Multiprocess::ReadPipeHandle() const { // This is the parent case, it's stdin in the child. return info_->pipe_c2p_read.get(); diff --git a/test/test_paths.cc b/test/test_paths.cc index b3eee3c1..f6471d08 100644 --- a/test/test_paths.cc +++ b/test/test_paths.cc @@ -170,7 +170,7 @@ base::FilePath TestPaths::BuildArtifact( base::FilePath::StringType test_name = FILE_PATH_LITERAL("crashpad_") + module + FILE_PATH_LITERAL("_test"); -#if !defined(CRASHPAD_IS_IN_CHROMIUM) +#if !defined(CRASHPAD_IS_IN_CHROMIUM) && !defined(OS_FUCHSIA) CHECK(Executable().BaseName().RemoveFinalExtension().value() == test_name); #endif // !CRASHPAD_IS_IN_CHROMIUM @@ -182,6 +182,8 @@ base::FilePath TestPaths::BuildArtifact( case FileType::kExecutable: #if defined(OS_WIN) extension = FILE_PATH_LITERAL(".exe"); +#elif defined(OS_FUCHSIA) + directory = base::FilePath(FILE_PATH_LITERAL("/pkg/bin")); #endif // OS_WIN break; @@ -191,6 +193,10 @@ base::FilePath TestPaths::BuildArtifact( #else // OS_WIN extension = FILE_PATH_LITERAL(".so"); #endif // OS_WIN + +#if defined(OS_FUCHSIA) + directory = base::FilePath(FILE_PATH_LITERAL("/pkg/lib")); +#endif break; } diff --git a/util/misc/paths_fuchsia.cc b/util/misc/paths_fuchsia.cc index 8baa0d9f..09084483 100644 --- a/util/misc/paths_fuchsia.cc +++ b/util/misc/paths_fuchsia.cc @@ -26,9 +26,8 @@ namespace crashpad { bool Paths::Executable(base::FilePath* path) { // Assume the environment has been set up following // https://fuchsia.googlesource.com/docs/+/master/namespaces.md#typical-directory-structure - // . The actual executable name is not known, but it's conceptually in this - // location. - *path = base::FilePath("/pkg/bin/unknown"); + // . + *path = base::FilePath("/pkg/bin/app"); return true; } From 0c57aafb4e93ef7a565b96271751b5d5c3c31a73 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Wed, 17 Jan 2018 16:21:36 -0800 Subject: [PATCH 110/326] Add support for libc++ on Windows. Use the "POSIX" implementation of ThrowBadAlloc() on Windows when libc++ is being used. Bug: chromium:801780 Change-Id: I230a8df9040aa73e290bb0d002996e822958a94b Reviewed-on: https://chromium-review.googlesource.com/872121 Reviewed-by: Scott Graham Commit-Queue: Peter Collingbourne --- util/stdlib/aligned_allocator.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/stdlib/aligned_allocator.cc b/util/stdlib/aligned_allocator.cc index 9aedb299..797a3ac4 100644 --- a/util/stdlib/aligned_allocator.cc +++ b/util/stdlib/aligned_allocator.cc @@ -18,7 +18,7 @@ #include "build/build_config.h" -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(_LIBCPP_STD_VER) #include #elif defined(OS_WIN) #include @@ -31,7 +31,7 @@ namespace { // library to do so. This works even if C++ exceptions are disabled, causing // program termination if uncaught. void ThrowBadAlloc() { -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(_LIBCPP_STD_VER) // This works with both libc++ and libstdc++. std::__throw_bad_alloc(); #elif defined(OS_WIN) From 939afcf56ee583b926ccc420a8ad3ae4d878c99d Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 17 Jan 2018 16:59:59 -0800 Subject: [PATCH 111/326] Add process_memory.cc when using gyp Change-Id: I835b03f1553c04ebc03e3eb1f11455f049342cc5 Reviewed-on: https://chromium-review.googlesource.com/872252 Reviewed-by: Scott Graham Commit-Queue: Joshua Peraza --- util/util.gyp | 1 + 1 file changed, 1 insertion(+) diff --git a/util/util.gyp b/util/util.gyp index 43b6e32b..2f0f6e88 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -199,6 +199,7 @@ 'posix/signals.h', 'posix/symbolic_constants_posix.cc', 'posix/symbolic_constants_posix.h', + 'process/process_memory.cc', 'process/process_memory.h', 'process/process_memory_linux.cc', 'process/process_memory_linux.h', From a459d30e9b357a84c385e2fed2746007f7cc83f2 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 17 Jan 2018 15:17:44 -0800 Subject: [PATCH 112/326] android: partially disable module tests on ARM before API 21 ProcessReader module tests use dl_iterate_phdr to check that the loader's modules appear in the ProcessReader's module vector, but this API is not provided on Android for ARM until API 21. Bug: crashpad:30 Change-Id: I7832bb5560f870671812c42345d4b59bf4416a26 Reviewed-on: https://chromium-review.googlesource.com/871972 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- snapshot/linux/process_reader_test.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/snapshot/linux/process_reader_test.cc b/snapshot/linux/process_reader_test.cc index f877bf86..a8190b39 100644 --- a/snapshot/linux/process_reader_test.cc +++ b/snapshot/linux/process_reader_test.cc @@ -43,6 +43,10 @@ #include "util/misc/from_pointer_cast.h" #include "util/synchronization/semaphore.h" +#if defined(OS_ANDROID) +#include +#endif + namespace crashpad { namespace test { namespace { @@ -441,6 +445,8 @@ TEST(ProcessReader, ChildWithSplitStack) { test.Run(); } +// Android doesn't provide dl_iterate_phdr on ARM until API 21. +#if !defined(OS_ANDROID) || !defined(ARCH_CPU_ARMEL) || __ANDROID_API__ >= 21 int ExpectFindModule(dl_phdr_info* info, size_t size, void* data) { SCOPED_TRACE( base::StringPrintf("module %s at 0x%" PRIx64 " phdrs 0x%" PRIx64, @@ -474,6 +480,7 @@ int ExpectFindModule(dl_phdr_info* info, size_t size, void* data) { EXPECT_TRUE(found); return 0; } +#endif // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21 void ExpectModulesFromSelf(const std::vector& modules) { for (const auto& module : modules) { @@ -481,11 +488,14 @@ void ExpectModulesFromSelf(const std::vector& modules) { EXPECT_NE(module.type, ModuleSnapshot::kModuleTypeUnknown); } +// Android doesn't provide dl_iterate_phdr on ARM until API 21. +#if !defined(OS_ANDROID) || !defined(ARCH_CPU_ARMEL) || __ANDROID_API__ >= 21 EXPECT_EQ(dl_iterate_phdr( ExpectFindModule, reinterpret_cast( const_cast*>(&modules))), 0); +#endif // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21 } TEST(ProcessReader, SelfModules) { From a62033d6c0ca764e799bfcff566a83e5b3b432e5 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 18 Jan 2018 10:38:34 -0800 Subject: [PATCH 113/326] Roll mini_chromium to 5fcfa43 Includes: 5fcfa43 Remove .a before alink, otherwise stale .o can hang around Bug: crashpad:196 Change-Id: Iae361c5e5fa0797be9461eecf0a9c0669e5e6522 Reviewed-on: https://chromium-review.googlesource.com/874811 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 7741afcd..994bed87 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'a19ef08ada53345fd9277f3d9434bc35f0e13a55', + '5fcfa43c1587b94132e24782579350cb8266b990', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', From e7fe120d8b209794a400f80d4669031a533ee9cd Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 18 Jan 2018 11:47:55 -0800 Subject: [PATCH 114/326] ProcessMemory: Add ReadUpTo to enable short reads ProcessMemory::ReadCStringInternal needs to be able to perform short reads. Change-Id: I2b2e1c2e6603d01235d8d2dbd15494375cd7f3f6 Reviewed-on: https://chromium-review.googlesource.com/874776 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai Reviewed-by: Scott Graham --- util/process/process_memory.cc | 33 ++++++++++++++++++++++---- util/process/process_memory.h | 18 +++++++++++++- util/process/process_memory_fuchsia.cc | 19 +++++++-------- util/process/process_memory_fuchsia.h | 4 ++-- util/process/process_memory_linux.cc | 30 ++++++++--------------- util/process/process_memory_linux.h | 4 ++-- 6 files changed, 68 insertions(+), 40 deletions(-) diff --git a/util/process/process_memory.cc b/util/process/process_memory.cc index 2d7460dd..6bc00101 100644 --- a/util/process/process_memory.cc +++ b/util/process/process_memory.cc @@ -18,6 +18,25 @@ namespace crashpad { +bool ProcessMemory::Read(VMAddress address, size_t size, void* buffer) const { + char* buffer_c = static_cast(buffer); + while (size > 0) { + ssize_t bytes_read = ReadUpTo(address, size, buffer_c); + if (bytes_read < 0) { + return false; + } + if (bytes_read == 0) { + LOG(ERROR) << "short read"; + return false; + } + DCHECK_LE(static_cast(bytes_read), size); + size -= bytes_read; + address += bytes_read; + buffer_c += bytes_read; + } + return true; +} + bool ProcessMemory::ReadCStringInternal(VMAddress address, bool has_size, size_t size, @@ -33,19 +52,23 @@ bool ProcessMemory::ReadCStringInternal(VMAddress address, read_size = sizeof(buffer); } - if (!Read(address, read_size, buffer)) { + ssize_t bytes_read = ReadUpTo(address, read_size, buffer); + if (bytes_read < 0) { + return false; + } + if (bytes_read == 0) { break; } - char* nul = static_cast(memchr(buffer, '\0', read_size)); + char* nul = static_cast(memchr(buffer, '\0', bytes_read)); if (nul != nullptr) { string->append(buffer, nul - buffer); return true; } - string->append(buffer, read_size); + string->append(buffer, bytes_read); - address += read_size; - size -= read_size; + address += bytes_read; + size -= bytes_read; } while (!has_size || size > 0); LOG(ERROR) << "unterminated string"; diff --git a/util/process/process_memory.h b/util/process/process_memory.h index 5126f0c8..34561718 100644 --- a/util/process/process_memory.h +++ b/util/process/process_memory.h @@ -40,7 +40,7 @@ class ProcessMemory { //! //! \return `true` on success, with \a buffer filled appropriately. `false` on //! failure, with a message logged. - virtual bool Read(VMAddress address, size_t size, void* buffer) const = 0; + bool Read(VMAddress address, size_t size, void* buffer) const; //! \brief Reads a `NUL`-terminated C string from the target process into a //! string in the current process. @@ -83,6 +83,22 @@ class ProcessMemory { ~ProcessMemory() = default; private: + //! \brief Copies memory from the target process into a caller-provided buffer + //! in the current process, up to a maximum number of bytes. + //! + //! \param[in] address The address, in the target process' address space, of + //! the memory region to copy. + //! \param[in] size The maximum size, in bytes, of the memory region to copy. + //! \a buffer must be at least this size. + //! \param[out] buffer The buffer into which the contents of the other + //! process' memory will be copied. + //! + //! \return the number of bytes copied, 0 if there is no more data to read, or + //! -1 on failure with a message logged. + virtual ssize_t ReadUpTo(VMAddress address, + size_t size, + void* buffer) const = 0; + //! \brief Reads a `NUL`-terminated C string from the target process into a //! string in the current process. //! diff --git a/util/process/process_memory_fuchsia.cc b/util/process/process_memory_fuchsia.cc index 9785bd46..212e1c6f 100644 --- a/util/process/process_memory_fuchsia.cc +++ b/util/process/process_memory_fuchsia.cc @@ -16,6 +16,8 @@ #include +#include + #include "base/logging.h" #include "base/fuchsia/fuchsia_logging.h" @@ -33,25 +35,22 @@ bool ProcessMemoryFuchsia::Initialize(zx_handle_t process) { return true; } -bool ProcessMemoryFuchsia::Read(VMAddress address, - size_t size, - void* buffer) const { +ssize_t ProcessMemoryFuchsia::ReadUpTo(VMAddress address, + size_t size, + void* buffer) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); + DCHECK_LE(size, size_t{std::numeric_limits::max()}); + size_t actual; zx_status_t status = zx_process_read_memory(process_, address, buffer, size, &actual); if (status != ZX_OK) { ZX_LOG(ERROR, status) << "zx_process_read_memory"; - return false; + return -1; } - if (actual != size) { - LOG(ERROR) << "zx_process_read_memory: short read"; - return false; - } - - return true; + return actual; } } // namespace crashpad diff --git a/util/process/process_memory_fuchsia.h b/util/process/process_memory_fuchsia.h index fa3b3318..ebc93e0f 100644 --- a/util/process/process_memory_fuchsia.h +++ b/util/process/process_memory_fuchsia.h @@ -42,9 +42,9 @@ class ProcessMemoryFuchsia final : public ProcessMemory { //! \return `true` on success, `false` on failure with a message logged. bool Initialize(zx_handle_t process); - bool Read(VMAddress address, size_t size, void* buffer) const override; - private: + ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override; + zx_handle_t process_; InitializationStateDcheck initialized_; diff --git a/util/process/process_memory_linux.cc b/util/process/process_memory_linux.cc index c1e770aa..1fc3c05f 100644 --- a/util/process/process_memory_linux.cc +++ b/util/process/process_memory_linux.cc @@ -20,6 +20,7 @@ #include #include +#include #include "base/logging.h" #include "base/posix/eintr_wrapper.h" @@ -45,30 +46,19 @@ bool ProcessMemoryLinux::Initialize(pid_t pid) { return true; } -bool ProcessMemoryLinux::Read(VMAddress address, - size_t size, - void* buffer) const { +ssize_t ProcessMemoryLinux::ReadUpTo(VMAddress address, + size_t size, + void* buffer) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); DCHECK(mem_fd_.is_valid()); + DCHECK_LE(size, size_t{std::numeric_limits::max()}); - char* buffer_c = static_cast(buffer); - while (size > 0) { - ssize_t bytes_read = - HANDLE_EINTR(pread64(mem_fd_.get(), buffer_c, size, address)); - if (bytes_read < 0) { - PLOG(ERROR) << "pread64"; - return false; - } - if (bytes_read == 0) { - LOG(ERROR) << "unexpected eof"; - return false; - } - DCHECK_LE(static_cast(bytes_read), size); - size -= bytes_read; - address += bytes_read; - buffer_c += bytes_read; + ssize_t bytes_read = + HANDLE_EINTR(pread64(mem_fd_.get(), buffer, size, address)); + if (bytes_read < 0) { + PLOG(ERROR) << "pread64"; } - return true; + return bytes_read; } } // namespace crashpad diff --git a/util/process/process_memory_linux.h b/util/process/process_memory_linux.h index 7aecdfc4..4fe008bb 100644 --- a/util/process/process_memory_linux.h +++ b/util/process/process_memory_linux.h @@ -44,9 +44,9 @@ class ProcessMemoryLinux final : public ProcessMemory { //! \return `true` on success, `false` on failure with a message logged. bool Initialize(pid_t pid); - bool Read(VMAddress address, size_t size, void* buffer) const override; - private: + ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override; + base::ScopedFD mem_fd_; pid_t pid_; InitializationStateDcheck initialized_; From 9f4ebc713a59358d3af7f21099b83e749b817120 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 18 Jan 2018 14:34:41 -0800 Subject: [PATCH 115/326] fuchsia: Get CrashpadInfoReader working Bug: crashpad:196 Change-Id: Ia9bcc45891fd5cf40cccc655c4b904b1610e5932 Reviewed-on: https://chromium-review.googlesource.com/875117 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- snapshot/BUILD.gn | 14 +++++-- .../crashpad_info_reader_test.cc | 37 +++++++++++++++++-- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 67291c7d..de685394 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -100,8 +100,6 @@ static_library("snapshot") { if (crashpad_is_linux || crashpad_is_android) { 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", @@ -132,6 +130,13 @@ static_library("snapshot") { ] } + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ + "crashpad_types/crashpad_info_reader.cc", + "crashpad_types/crashpad_info_reader.h", + ] + } + if (crashpad_is_win) { sources += [ "win/capture_memory_delegate_win.cc", @@ -291,7 +296,6 @@ 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", @@ -305,6 +309,10 @@ source_set("snapshot_test") { sources += [ "crashpad_info_client_options_test.cc" ] } + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ "crashpad_types/crashpad_info_reader_test.cc" ] + } + if (crashpad_is_win) { sources += [ "api/module_annotations_win_test.cc", diff --git a/snapshot/crashpad_types/crashpad_info_reader_test.cc b/snapshot/crashpad_types/crashpad_info_reader_test.cc index 23565dab..009b9e41 100644 --- a/snapshot/crashpad_types/crashpad_info_reader_test.cc +++ b/snapshot/crashpad_types/crashpad_info_reader_test.cc @@ -26,12 +26,31 @@ #include "test/multiprocess.h" #include "util/file/file_io.h" #include "util/misc/from_pointer_cast.h" + +#if defined(OS_FUCHSIA) +#include + +#include "util/process/process_memory_fuchsia.h" +#elif defined(OS_LINUX) || defined(OS_ANDROID) #include "util/process/process_memory_linux.h" +#else +#error Port. +#endif namespace crashpad { namespace test { namespace { +#if defined(OS_FUCHSIA) +using ProcessHandle = zx_handle_t; +ProcessHandle GetSelf() { return zx_process_self(); } +#elif defined(OS_POSIX) +using ProcessHandle = pid_t; +ProcessHandle GetSelf() { return getpid(); } +#else +#error Port. +#endif + constexpr TriState kCrashpadHandlerBehavior = TriState::kEnabled; constexpr TriState kSystemCrashReporterForwarding = TriState::kDisabled; constexpr TriState kGatherIndirectlyReferencedMemory = TriState::kUnset; @@ -52,9 +71,15 @@ class CrashpadInfoTest { kGatherIndirectlyReferencedMemory, kIndirectlyReferencedMemoryCap); } - void ExpectCrashpadInfo(pid_t pid, bool is_64_bit) { + void ExpectCrashpadInfo(ProcessHandle process, bool is_64_bit) { +#if defined(OS_FUCHSIA) + ProcessMemoryFuchsia memory; +#elif defined(OS_LINUX) || defined(OS_ANDROID) ProcessMemoryLinux memory; - ASSERT_TRUE(memory.Initialize(pid)); +#else +#error Port. +#endif + ASSERT_TRUE(memory.Initialize(process)); ProcessMemoryRange range; ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); @@ -95,9 +120,13 @@ TEST(CrashpadInfoReader, ReadFromSelf) { constexpr bool am_64_bit = false; #endif - test.ExpectCrashpadInfo(getpid(), am_64_bit); + test.ExpectCrashpadInfo(GetSelf(), am_64_bit); } +// TODO(scottmg): This needs to be be ported to use MultiprocessExec instead of +// relying on fork() to have the same pointers in parent and child. +#if !defined(OS_FUCHSIA) + class ReadFromChildTest : public Multiprocess { public: ReadFromChildTest() : Multiprocess(), info_test_() {} @@ -127,6 +156,8 @@ TEST(CrashpadInfoReader, ReadFromChild) { test.Run(); } +#endif // !defined(OS_FUCHSIA) + } // namespace } // namespace test } // namespace crashpad From 3a7b80995d7ab01bfc8bf33729d1719357963502 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 17 Jan 2018 10:48:49 -0800 Subject: [PATCH 116/326] fuchsia: Default crashpad_dependencies based on is_fuchsia_tree Bug: crashpad:196 Change-Id: I0ff0157287e8b84323068741e060ff3c10f9719a Reviewed-on: https://chromium-review.googlesource.com/871044 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- build/crashpad_buildconfig.gni | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build/crashpad_buildconfig.gni b/build/crashpad_buildconfig.gni index 021bd53a..562ef142 100644 --- a/build/crashpad_buildconfig.gni +++ b/build/crashpad_buildconfig.gni @@ -17,6 +17,14 @@ declare_args() { # targets to use for dependencies. Valid values are "standalone", "chromium", # and "fuchsia". crashpad_dependencies = "standalone" + + if (defined(is_fuchsia_tree) && is_fuchsia_tree) { + # Determines various flavors of build configuration, and which concrete + # targets to use for dependencies. Valid values are "standalone", + # "chromium", and "fuchsia". Defaulted to "fuchsia" because + # "is_fuchsia_tree" is set. + crashpad_dependencies = "fuchsia" + } } assert( From dea19c73741f9cad4b99a60aaf83b2716bf6c14d Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 19 Jan 2018 14:03:34 -0800 Subject: [PATCH 117/326] fuchsia: Port ElfImageReader and (some of) its tests (Still need to avoid fork()-dependence for the non-self tests.) Bug: crashpad:196 Change-Id: Ib34fe33c7ec295881c1f555995072d9ff742647f Reviewed-on: https://chromium-review.googlesource.com/876650 Reviewed-by: Joshua Peraza Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- snapshot/BUILD.gn | 24 ++--- .../crashpad_info_reader_test.cc | 30 +----- snapshot/elf/elf_image_reader.cc | 6 +- snapshot/elf/elf_image_reader_test.cc | 98 +++++++++++++++---- test/BUILD.gn | 2 + test/process_type.cc | 37 +++++++ test/process_type.h | 48 +++++++++ test/test.gyp | 2 + util/BUILD.gn | 1 + util/process/process_memory_native.h | 34 +++++++ util/util.gyp | 1 + 11 files changed, 224 insertions(+), 59 deletions(-) create mode 100644 test/process_type.cc create mode 100644 test/process_type.h create mode 100644 util/process/process_memory_native.h diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index de685394..9a27b1ee 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -102,12 +102,6 @@ static_library("snapshot") { sources += [ "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", - "elf/elf_image_reader.h", - "elf/elf_symbol_table_reader.cc", - "elf/elf_symbol_table_reader.h", "linux/cpu_context_linux.cc", "linux/cpu_context_linux.h", "linux/debug_rendezvous.cc", @@ -134,6 +128,12 @@ static_library("snapshot") { sources += [ "crashpad_types/crashpad_info_reader.cc", "crashpad_types/crashpad_info_reader.h", + "elf/elf_dynamic_array_reader.cc", + "elf/elf_dynamic_array_reader.h", + "elf/elf_image_reader.cc", + "elf/elf_image_reader.h", + "elf/elf_symbol_table_reader.cc", + "elf/elf_symbol_table_reader.h", ] } @@ -263,7 +263,7 @@ static_library("test_support") { config("snapshot_test_link") { visibility = [ ":*" ] - if (crashpad_is_linux || crashpad_is_android) { + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { # There’s no way to make the link depend on this file. “inputs” doesn’t have # the intended effect in a config. https://crbug.com/781858, # https://crbug.com/796187. @@ -297,9 +297,6 @@ source_set("snapshot_test") { if (crashpad_is_linux || crashpad_is_android) { sources += [ "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", "linux/debug_rendezvous_test.cc", "linux/exception_snapshot_linux_test.cc", "linux/process_reader_test.cc", @@ -310,7 +307,12 @@ source_set("snapshot_test") { } if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { - sources += [ "crashpad_types/crashpad_info_reader_test.cc" ] + sources += [ + "crashpad_types/crashpad_info_reader_test.cc", + "elf/elf_image_reader_test.cc", + "elf/elf_image_reader_test_note.S", + "elf/test_exported_symbols.sym", + ] } if (crashpad_is_win) { diff --git a/snapshot/crashpad_types/crashpad_info_reader_test.cc b/snapshot/crashpad_types/crashpad_info_reader_test.cc index 009b9e41..7b0629cf 100644 --- a/snapshot/crashpad_types/crashpad_info_reader_test.cc +++ b/snapshot/crashpad_types/crashpad_info_reader_test.cc @@ -24,33 +24,19 @@ #include "client/simple_string_dictionary.h" #include "gtest/gtest.h" #include "test/multiprocess.h" +#include "test/process_type.h" #include "util/file/file_io.h" #include "util/misc/from_pointer_cast.h" +#include "util/process/process_memory_native.h" #if defined(OS_FUCHSIA) #include - -#include "util/process/process_memory_fuchsia.h" -#elif defined(OS_LINUX) || defined(OS_ANDROID) -#include "util/process/process_memory_linux.h" -#else -#error Port. #endif namespace crashpad { namespace test { namespace { -#if defined(OS_FUCHSIA) -using ProcessHandle = zx_handle_t; -ProcessHandle GetSelf() { return zx_process_self(); } -#elif defined(OS_POSIX) -using ProcessHandle = pid_t; -ProcessHandle GetSelf() { return getpid(); } -#else -#error Port. -#endif - constexpr TriState kCrashpadHandlerBehavior = TriState::kEnabled; constexpr TriState kSystemCrashReporterForwarding = TriState::kDisabled; constexpr TriState kGatherIndirectlyReferencedMemory = TriState::kUnset; @@ -71,14 +57,8 @@ class CrashpadInfoTest { kGatherIndirectlyReferencedMemory, kIndirectlyReferencedMemoryCap); } - void ExpectCrashpadInfo(ProcessHandle process, bool is_64_bit) { -#if defined(OS_FUCHSIA) - ProcessMemoryFuchsia memory; -#elif defined(OS_LINUX) || defined(OS_ANDROID) - ProcessMemoryLinux memory; -#else -#error Port. -#endif + void ExpectCrashpadInfo(ProcessType process, bool is_64_bit) { + ProcessMemoryNative memory; ASSERT_TRUE(memory.Initialize(process)); ProcessMemoryRange range; @@ -120,7 +100,7 @@ TEST(CrashpadInfoReader, ReadFromSelf) { constexpr bool am_64_bit = false; #endif - test.ExpectCrashpadInfo(GetSelf(), am_64_bit); + test.ExpectCrashpadInfo(GetSelfProcess(), am_64_bit); } // TODO(scottmg): This needs to be be ported to use MultiprocessExec instead of diff --git a/snapshot/elf/elf_image_reader.cc b/snapshot/elf/elf_image_reader.cc index b7c9a5f4..7b3389c5 100644 --- a/snapshot/elf/elf_image_reader.cc +++ b/snapshot/elf/elf_image_reader.cc @@ -625,9 +625,9 @@ bool ElfImageReader::GetAddressFromDynamicArray(uint64_t tag, if (!dynamic_array_->GetValue(tag, address)) { return false; } -#if defined(OS_ANDROID) - // The GNU loader updates the dynamic array according to the load bias while - // the Android loader only updates the debug address. +#if defined(OS_ANDROID) || defined(OS_FUCHSIA) + // The GNU loader updates the dynamic array according to the load bias. + // The Android and Fuchsia loaders only update the debug address. if (tag != DT_DEBUG) { *address += GetLoadBias(); } diff --git a/snapshot/elf/elf_image_reader_test.cc b/snapshot/elf/elf_image_reader_test.cc index e9572be9..a69dfbe0 100644 --- a/snapshot/elf/elf_image_reader_test.cc +++ b/snapshot/elf/elf_image_reader_test.cc @@ -21,12 +21,29 @@ #include "build/build_config.h" #include "gtest/gtest.h" #include "test/multiprocess.h" +#include "test/process_type.h" #include "util/file/file_io.h" -#include "util/linux/auxiliary_vector.h" -#include "util/linux/memory_map.h" #include "util/misc/address_types.h" #include "util/misc/from_pointer_cast.h" -#include "util/process/process_memory_linux.h" +#include "util/process/process_memory_native.h" + +#if defined(OS_FUCHSIA) + +#include +#include + +#include "base/fuchsia/fuchsia_logging.h" + +#elif defined(OS_LINUX) || defined(OS_ANDROID) + +#include "util/linux/auxiliary_vector.h" +#include "util/linux/memory_map.h" + +#else + +#error Port. + +#endif // OS_FUCHSIA extern "C" { __attribute__((visibility("default"))) void @@ -37,15 +54,49 @@ namespace crashpad { namespace test { namespace { -void LocateExecutable(pid_t pid, bool is_64_bit, VMAddress* elf_address) { + +#if defined(OS_FUCHSIA) + +void LocateExecutable(ProcessType process, + ProcessMemory* memory, + bool is_64_bit, + VMAddress* elf_address) { + uintptr_t debug_address; + zx_status_t status = zx_object_get_property(process, + ZX_PROP_PROCESS_DEBUG_ADDR, + &debug_address, + sizeof(debug_address)); + ASSERT_EQ(status, ZX_OK) + << "zx_object_get_property: ZX_PROP_PROCESS_DEBUG_ADDR"; + + constexpr auto k_r_debug_map_offset = offsetof(r_debug, r_map); + uintptr_t map; + ASSERT_TRUE( + memory->Read(debug_address + k_r_debug_map_offset, sizeof(map), &map)) + << "read link_map"; + + constexpr auto k_link_map_addr_offset = offsetof(link_map, l_addr); + uintptr_t base; + ASSERT_TRUE(memory->Read(map + k_link_map_addr_offset, sizeof(base), &base)) + << "read base"; + + *elf_address = base; +} + +#elif defined(OS_LINUX) || defined(OS_ANDROID) + +void LocateExecutable(ProcessType process, + ProcessMemory* memory, + bool is_64_bit, + VMAddress* elf_address) { AuxiliaryVector aux; - ASSERT_TRUE(aux.Initialize(pid, is_64_bit)); + ASSERT_TRUE(aux.Initialize(process, is_64_bit)); VMAddress phdrs; ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs)); MemoryMap memory_map; - ASSERT_TRUE(memory_map.Initialize(pid)); + ASSERT_TRUE(memory_map.Initialize(process)); const MemoryMap::Mapping* phdr_mapping = memory_map.FindMapping(phdrs); ASSERT_TRUE(phdr_mapping); const MemoryMap::Mapping* exe_mapping = @@ -54,6 +105,8 @@ void LocateExecutable(pid_t pid, bool is_64_bit, VMAddress* elf_address) { *elf_address = exe_mapping->range.Base(); } +#endif // OS_FUCHSIA + void ExpectSymbol(ElfImageReader* reader, const std::string& symbol_name, VMAddress expected_symbol_address) { @@ -67,21 +120,22 @@ void ExpectSymbol(ElfImageReader* reader, reader->GetDynamicSymbol("notasymbol", &symbol_address, &symbol_size)); } -void ReadThisExecutableInTarget(pid_t pid) { +void ReadThisExecutableInTarget(ProcessType process) { #if defined(ARCH_CPU_64_BITS) constexpr bool am_64_bit = true; #else constexpr bool am_64_bit = false; #endif // ARCH_CPU_64_BITS - VMAddress elf_address; - LocateExecutable(pid, am_64_bit, &elf_address); - - ProcessMemoryLinux memory; - ASSERT_TRUE(memory.Initialize(pid)); + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); ProcessMemoryRange range; ASSERT_TRUE(range.Initialize(&memory, am_64_bit)); + VMAddress elf_address; + LocateExecutable(process, &memory, am_64_bit, &elf_address); + ASSERT_NO_FATAL_FAILURE(); + ElfImageReader reader; ASSERT_TRUE(reader.Initialize(range, elf_address)); @@ -123,7 +177,7 @@ void ReadThisExecutableInTarget(pid_t pid) { // Assumes that libc is loaded at the same address in this process as in the // target, which it is for the fork test below. -void ReadLibcInTarget(pid_t pid) { +void ReadLibcInTarget(ProcessType process) { #if defined(ARCH_CPU_64_BITS) constexpr bool am_64_bit = true; #else @@ -135,8 +189,8 @@ void ReadLibcInTarget(pid_t pid) { << dlerror(); VMAddress elf_address = FromPointerCast(info.dli_fbase); - ProcessMemoryLinux memory; - ASSERT_TRUE(memory.Initialize(pid)); + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); ProcessMemoryRange range; ASSERT_TRUE(range.Initialize(&memory, am_64_bit)); @@ -146,6 +200,11 @@ void ReadLibcInTarget(pid_t pid) { ExpectSymbol(&reader, "getpid", FromPointerCast(getpid)); } +TEST(ElfImageReader, MainExecutableSelf) { + ReadThisExecutableInTarget(GetSelfProcess()); +} + +#if !defined(OS_FUCHSIA) // TODO(scottmg): Port to MultiprocessExec. class ReadExecutableChildTest : public Multiprocess { public: ReadExecutableChildTest() : Multiprocess() {} @@ -156,19 +215,17 @@ class ReadExecutableChildTest : public Multiprocess { void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } }; -TEST(ElfImageReader, MainExecutableSelf) { - ReadThisExecutableInTarget(getpid()); -} - TEST(ElfImageReader, MainExecutableChild) { ReadExecutableChildTest test; test.Run(); } +#endif // !OS_FUCHSIA TEST(ElfImageReader, OneModuleSelf) { - ReadLibcInTarget(getpid()); + ReadLibcInTarget(GetSelfProcess()); } +#if !defined(OS_FUCHSIA) // TODO(scottmg): Port to MultiprocessExec. class ReadLibcChildTest : public Multiprocess { public: ReadLibcChildTest() : Multiprocess() {} @@ -183,6 +240,7 @@ TEST(ElfImageReader, OneModuleChild) { ReadLibcChildTest test; test.Run(); } +#endif // !OS_FUCHSIA } // namespace } // namespace test diff --git a/test/BUILD.gn b/test/BUILD.gn index e59cc2af..4b1eff24 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -33,6 +33,8 @@ static_library("test") { "main_arguments.h", "multiprocess.h", "multiprocess_exec.h", + "process_type.cc", + "process_type.h", "scoped_module_handle.cc", "scoped_module_handle.h", "scoped_temp_dir.cc", diff --git a/test/process_type.cc b/test/process_type.cc new file mode 100644 index 00000000..0e3c9e6d --- /dev/null +++ b/test/process_type.cc @@ -0,0 +1,37 @@ +// Copyright 2018 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 "test/process_type.h" + +#if defined(OS_FUCHSIA) +#include +#elif defined(OS_POSIX) +#include +#endif + +namespace crashpad { +namespace test { + +ProcessType GetSelfProcess() { +#if defined(OS_FUCHSIA) + return zx_process_self(); +#elif defined(OS_POSIX) + return getpid(); +#elif defined(OS_WIN) + return GetCurrentProcess(); +#endif +} + +} // namespace test +} // namespace crashpad diff --git a/test/process_type.h b/test/process_type.h new file mode 100644 index 00000000..a25b1222 --- /dev/null +++ b/test/process_type.h @@ -0,0 +1,48 @@ +// Copyright 2018 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_TEST_PROCESS_TYPE_H_ +#define CRASHPAD_TEST_PROCESS_TYPE_H_ + +#include "build/build_config.h" + +#if defined(OS_FUCHSIA) +#include +#elif defined(OS_POSIX) +#include +#elif defined(OS_WIN) +#include +#endif + +namespace crashpad { +namespace test { + +#if defined(OS_FUCHSIA) +using ProcessType = zx_handle_t; +#elif defined(OS_POSIX) || DOXYGEN +//! \brief Alias for platform-specific type to represent a process. +using ProcessType = pid_t; +#elif defined(OS_WIN) +using ProcessType = HANDLE; +#else +#error Port. +#endif + +//! \brief Get a ProcessType representing the current process. +ProcessType GetSelfProcess(); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_PROCESS_TYPE_H_ diff --git a/test/test.gyp b/test/test.gyp index ad88e036..699d984f 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -62,6 +62,8 @@ 'multiprocess_exec_posix.cc', 'multiprocess_exec_win.cc', 'multiprocess_posix.cc', + 'process_type.cc', + 'process_type.h', 'scoped_module_handle.cc', 'scoped_module_handle.h', 'scoped_temp_dir.cc', diff --git a/util/BUILD.gn b/util/BUILD.gn index fb6d71aa..13a5da80 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -283,6 +283,7 @@ static_library("util") { sources += [ "process/process_memory.cc", "process/process_memory.h", + "process/process_memory_native.h", # TODO: Port to all platforms. "process/process_memory_range.cc", diff --git a/util/process/process_memory_native.h b/util/process/process_memory_native.h new file mode 100644 index 00000000..19fa805a --- /dev/null +++ b/util/process/process_memory_native.h @@ -0,0 +1,34 @@ +// Copyright 2018 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 "build/build_config.h" + +#if defined(OS_FUCHSIA) +#include "util/process/process_memory_fuchsia.h" +#elif defined(OS_LINUX) || defined(OS_ANDROID) +#include "util/process/process_memory_linux.h" +#endif + +namespace crashpad { + +#if defined(OS_FUCHSIA) || DOXYGEN +//! \brief Alias for platform-specific native implementation of ProcessMemory. +using ProcessMemoryNative = ProcessMemoryFuchsia; +#elif defined(OS_LINUX) || defined(OS_ANDROID) +using ProcessMemoryNative = ProcessMemoryLinux; +#else +#error Port. +#endif + +} // namespace crashpad diff --git a/util/util.gyp b/util/util.gyp index 2f0f6e88..0000ee2d 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -203,6 +203,7 @@ 'process/process_memory.h', 'process/process_memory_linux.cc', 'process/process_memory_linux.h', + 'process/process_memory_native.h', 'process/process_memory_range.cc', 'process/process_memory_range.h', 'stdlib/aligned_allocator.cc', From c9b41a4d152d9dbae031e781cf5a9ad6e829b593 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 23 Jan 2018 10:33:55 -0800 Subject: [PATCH 118/326] fuchsia: Fix removal of /tmp dir on test run, and location of .so files 1. .so files should (I believe) be in /pkg/lib according to https://fuchsia.googlesource.com/docs/+/master/namespaces.md#typical-directory-structure (None of the tests that load .so actually work currently, so it doesn't make anything better or worse to fix this.) 2. The test directory structure looks like: - /tmp//pkg/... - /tmp//tmp/... Previously /tmp//pkg/ and /tmp//tmp/ were being removed, but /tmp// wasn't so there was a bunch of empty directory garbage being left in /tmp. Clean up from the root instead. Bug: crashpad:196 Change-Id: I4e82198721f329d597e14a89da8bc77fcc8647c6 Reviewed-on: https://chromium-review.googlesource.com/880884 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- build/run_tests.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/build/run_tests.py b/build/run_tests.py index fe5d6262..19833a8e 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -368,8 +368,9 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line): try: unique_id = uuid.uuid4().hex - tmp_root = '/tmp/%s_%s/tmp' % (test, unique_id) - staging_root = '/tmp/%s_%s/pkg' % (test, unique_id) + test_root = '/tmp/%s_%s' % (test, unique_id) + tmp_root = test_root + '/tmp' + staging_root = test_root + '/pkg' # Make a staging directory tree on the target. directories_to_create = [tmp_root, '%s/bin' % staging_root, @@ -378,12 +379,15 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line): def netcp(local_path): """Uses `netcp` to copy a file or directory to the device. Files located - inside the build dir are stored to /pkg/bin, otherwise to /pkg/assets. + inside the build dir are stored to /pkg/bin, or /pkg/lib (if .so), + otherwise to /pkg/assets. """ in_binary_dir = local_path.startswith(binary_dir + '/') if in_binary_dir: - target_path = os.path.join( - staging_root, 'bin', local_path[len(binary_dir)+1:]) + is_so = local_path.endswith('.so') + target_path = os.path.join(staging_root, + 'lib' if is_so else 'bin', + local_path[len(binary_dir)+1:]) else: target_path = os.path.join(staging_root, 'assets', local_path) netcp_path = os.path.join(sdk_root, 'tools', 'netcp') @@ -412,7 +416,7 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line): if not success: raise subprocess.CalledProcessError(1, test) finally: - netruncmd(['rm', '-rf', tmp_root, staging_root]) + netruncmd(['rm', '-rf', test_root]) # This script is primarily used from the waterfall so that the list of tests From 24f07f7c432831881b4a6f6bcb73143dd20e038a Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 23 Jan 2018 14:14:06 -0800 Subject: [PATCH 119/326] linux: Enable ARM family exception and thread snapshots ARM references: http://elixir.free-electrons.com/linux/latest/source/arch/arm/include/asm/ucontext.h http://elixir.free-electrons.com/linux/latest/source/arch/arm/kernel/signal.c#L185 ARM64 references: http://elixir.free-electrons.com/linux/latest/source/arch/arm64/include/uapi/asm/sigcontext.h http://elixir.free-electrons.com/linux/latest/source/arch/arm64/kernel/signal.c#L371 Bug: crashpad:30 Change-Id: I53f235b5826607db260bd1e43a819a93284843f5 Reviewed-on: https://chromium-review.googlesource.com/865435 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- compat/linux/signal.h | 33 ++++ snapshot/cpu_architecture.h | 6 + snapshot/cpu_context.cc | 4 + snapshot/cpu_context.h | 50 +++++ snapshot/linux/cpu_context_linux.cc | 89 ++++++++- snapshot/linux/cpu_context_linux.h | 67 ++++++- snapshot/linux/exception_snapshot_linux.cc | 162 ++++++++++++++++- snapshot/linux/exception_snapshot_linux.h | 7 +- .../linux/exception_snapshot_linux_test.cc | 171 ++++++++++++++++++ snapshot/linux/signal_context.h | 157 ++++++++++++---- snapshot/linux/system_snapshot_linux.cc | 21 +++ snapshot/linux/thread_snapshot_linux.cc | 14 ++ snapshot/linux/thread_snapshot_linux.h | 7 +- util/linux/thread_info.h | 2 +- util/linux/traits.h | 2 + 15 files changed, 750 insertions(+), 42 deletions(-) diff --git a/compat/linux/signal.h b/compat/linux/signal.h index 62b9c086..4e1cdc1f 100644 --- a/compat/linux/signal.h +++ b/compat/linux/signal.h @@ -18,10 +18,43 @@ #include_next // Missing from glibc and bionic-x86_64 + #if defined(__x86_64__) || defined(__i386__) #if !defined(X86_FXSR_MAGIC) #define X86_FXSR_MAGIC 0x0000 #endif #endif // __x86_64__ || __i386__ +#if defined(__aarch64__) || defined(__arm__) + +#if !defined(FPSIMD_MAGIC) +#define FPSIMD_MAGIC 0x46508001 +#endif + +#if !defined(ESR_MAGIC) +#define ESR_MAGIC 0x45535201 +#endif + +#if !defined(EXTRA_MAGIC) +#define EXTRA_MAGIC 0x45585401 +#endif + +#if !defined(VFP_MAGIC) +#define VFP_MAGIC 0x56465001 +#endif + +#if !defined(CRUNCH_MAGIC) +#define CRUNCH_MAGIC 0x5065cf03 +#endif + +#if !defined(DUMMY_MAGIC) +#define DUMMY_MAGIC 0xb0d9ed01 +#endif + +#if !defined(IWMMXT_MAGIC) +#define IWMMXT_MAGIC 0x12ef842a +#endif + +#endif // __aarch64__ || __arm__ + #endif // CRASHPAD_COMPAT_LINUX_SIGNAL_H_ diff --git a/snapshot/cpu_architecture.h b/snapshot/cpu_architecture.h index 208e98f2..6116d4a1 100644 --- a/snapshot/cpu_architecture.h +++ b/snapshot/cpu_architecture.h @@ -32,6 +32,12 @@ enum CPUArchitecture { //! \brief x86_64. kCPUArchitectureX86_64, + + //! \brief 32-bit ARM. + kCPUArchitectureARM, + + //! \brief 64-bit ARM. + kCPUArchitectureARM64 }; } // namespace crashpad diff --git a/snapshot/cpu_context.cc b/snapshot/cpu_context.cc index 5c964480..8d83e638 100644 --- a/snapshot/cpu_context.cc +++ b/snapshot/cpu_context.cc @@ -164,6 +164,10 @@ uint64_t CPUContext::InstructionPointer() const { return x86->eip; case kCPUArchitectureX86_64: return x86_64->rip; + case kCPUArchitectureARM: + return arm->pc; + case kCPUArchitectureARM64: + return arm64->pc; default: NOTREACHED(); return ~0ull; diff --git a/snapshot/cpu_context.h b/snapshot/cpu_context.h index ba3ac181..3160b0eb 100644 --- a/snapshot/cpu_context.h +++ b/snapshot/cpu_context.h @@ -18,6 +18,7 @@ #include #include "snapshot/cpu_architecture.h" +#include "util/numeric/int128.h" namespace crashpad { @@ -258,6 +259,53 @@ struct CPUContextX86_64 { uint64_t dr7; }; +//! \brief A context structure carrying ARM CPU state. +struct CPUContextARM { + uint32_t regs[11]; + uint32_t fp; // r11 + uint32_t ip; // r12 + uint32_t sp; // r13 + uint32_t lr; // r14 + uint32_t pc; // r15 + uint32_t cpsr; + + struct { + struct fp_reg { + uint32_t sign1 : 1; + uint32_t unused : 15; + uint32_t sign2 : 1; + uint32_t exponent : 14; + uint32_t j : 1; + uint32_t mantissa1 : 31; + uint32_t mantisss0 : 32; + } fpregs[8]; + uint32_t fpsr : 32; + uint32_t fpcr : 32; + uint8_t type[8]; + uint32_t init_flag; + } fpa_regs; + + struct { + uint64_t vfp[32]; + uint32_t fpscr; + } vfp_regs; + + bool have_fpa_regs; + bool have_vfp_regs; +}; + +//! \brief A context structure carrying ARM64 CPU state. +struct CPUContextARM64 { + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; + + uint128_struct fpsimd[32]; + uint32_t fpsr; + uint32_t fpcr; +}; + //! \brief A context structure capable of carrying the context of any supported //! CPU architecture. struct CPUContext { @@ -274,6 +322,8 @@ struct CPUContext { union { CPUContextX86* x86; CPUContextX86_64* x86_64; + CPUContextARM* arm; + CPUContextARM64* arm64; }; }; diff --git a/snapshot/linux/cpu_context_linux.cc b/snapshot/linux/cpu_context_linux.cc index 26c65be1..1a6589f8 100644 --- a/snapshot/linux/cpu_context_linux.cc +++ b/snapshot/linux/cpu_context_linux.cc @@ -151,9 +151,92 @@ void InitializeCPUContextX86_64(const SignalThreadContext64& thread_context, context->dr6 = 0; context->dr7 = 0; } -#else -#error Port. -#endif // ARCH_CPU_X86_FAMILY || DOXYGEN + +#elif defined(ARCH_CPU_ARM_FAMILY) + +void InitializeCPUContextARM(const ThreadContext::t32_t& thread_context, + const FloatContext::f32_t& float_context, + CPUContextARM* context) { + static_assert(sizeof(context->regs) == sizeof(thread_context.regs), + "registers size mismatch"); + memcpy(&context->regs, &thread_context.regs, sizeof(context->regs)); + context->fp = thread_context.fp; + context->ip = thread_context.ip; + context->sp = thread_context.sp; + context->lr = thread_context.lr; + context->pc = thread_context.pc; + context->cpsr = thread_context.cpsr; + + static_assert(sizeof(context->vfp_regs) == sizeof(float_context.vfp), + "vfp size mismatch"); + context->have_vfp_regs = float_context.have_vfp; + if (float_context.have_vfp) { + memcpy(&context->vfp_regs, &float_context.vfp, sizeof(context->vfp_regs)); + } + + static_assert(sizeof(context->fpa_regs) == sizeof(float_context.fpregs), + "fpregs size mismatch"); + context->have_fpa_regs = float_context.have_fpregs; + if (float_context.have_fpregs) { + memcpy( + &context->fpa_regs, &float_context.fpregs, sizeof(context->fpa_regs)); + } +} + +void InitializeCPUContextARM_NoFloatingPoint( + const SignalThreadContext32& thread_context, + CPUContextARM* context) { + static_assert(sizeof(context->regs) == sizeof(thread_context.regs), + "registers size mismatch"); + memcpy(&context->regs, &thread_context.regs, sizeof(context->regs)); + context->fp = thread_context.fp; + context->ip = thread_context.ip; + context->sp = thread_context.sp; + context->lr = thread_context.lr; + context->pc = thread_context.pc; + context->cpsr = thread_context.cpsr; +} + +void InitializeCPUContextARM64(const ThreadContext::t64_t& thread_context, + const FloatContext::f64_t& float_context, + CPUContextARM64* context) { + InitializeCPUContextARM64_NoFloatingPoint(thread_context, context); + + static_assert(sizeof(context->fpsimd) == sizeof(float_context.vregs), + "fpsimd context size mismatch"); + memcpy(context->fpsimd, float_context.vregs, sizeof(context->fpsimd)); + context->fpsr = float_context.fpsr; + context->fpcr = float_context.fpcr; +} + +void InitializeCPUContextARM64_NoFloatingPoint( + const ThreadContext::t64_t& thread_context, + CPUContextARM64* context) { + static_assert(sizeof(context->regs) == sizeof(thread_context.regs), + "gpr context size mismtach"); + memcpy(context->regs, thread_context.regs, sizeof(context->regs)); + context->sp = thread_context.sp; + context->pc = thread_context.pc; + context->pstate = thread_context.pstate; +} + +void InitializeCPUContextARM64_OnlyFPSIMD( + const SignalFPSIMDContext& float_context, + CPUContextARM64* context) { + static_assert(sizeof(context->fpsimd) == sizeof(float_context.vregs), + "fpsimd context size mismatch"); + memcpy(context->fpsimd, float_context.vregs, sizeof(context->fpsimd)); + context->fpsr = float_context.fpsr; + context->fpcr = float_context.fpcr; +} + +void InitializeCPUContextARM64_ClearFPSIMD(CPUContextARM64* context) { + memset(context->fpsimd, 0, sizeof(context->fpsimd)); + context->fpsr = 0; + context->fpcr = 0; +} + +#endif // ARCH_CPU_X86_FAMILY } // namespace internal } // namespace crashpad diff --git a/snapshot/linux/cpu_context_linux.h b/snapshot/linux/cpu_context_linux.h index 092762c2..30bb205d 100644 --- a/snapshot/linux/cpu_context_linux.h +++ b/snapshot/linux/cpu_context_linux.h @@ -68,10 +68,73 @@ void InitializeCPUContextX86_64(const SignalThreadContext64& thread_context, const SignalFloatContext64& float_context, CPUContextX86_64* context); //! \} -#else -#error Port. // TODO(jperaza): ARM + #endif // ARCH_CPU_X86_FAMILY || DOXYGEN +#if defined(ARCH_CPU_ARM_FAMILY) || DOXYGEN + +//! \brief Initializes a CPUContextARM structure from native context structures +//! on Linux. +//! +//! \param[in] thread_context The native thread context. +//! \param[in] float_context The native float context. +//! \param[out] context The CPUContextARM structure to initialize. +void InitializeCPUContextARM(const ThreadContext::t32_t& thread_context, + const FloatContext::f32_t& float_context, + CPUContextARM* context); + +//! \brief Initializes GPR state in a CPUContextARM from a native signal context +//! structure on Linux. +//! +//! Floating point state is not initialized. +//! +//! \param[in] thread_context The native thread context. +//! \param[out] context The CPUContextARM structure to initialize. +void InitializeCPUContextARM_NoFloatingPoint( + const SignalThreadContext32& thread_context, + CPUContextARM* context); + +//! \brief Initializes a CPUContextARM64 structure from native context +//! structures on Linux. +//! +//! \param[in] thread_context The native thread context. +//! \param[in] float_context The native float context. +//! \param[out] context The CPUContextARM64 structure to initialize. +void InitializeCPUContextARM64(const ThreadContext::t64_t& thread_context, + const FloatContext::f64_t& float_context, + CPUContextARM64* context); + +//! \brief Initializes GPR state in a CPUContextARM64 from a native context +//! structure on Linux. +//! +//! Floating point state is not initialized. +//! +//! \param[in] thread_context The native thread context. +//! \param[out] context The CPUContextARM64 structure to initialize. +void InitializeCPUContextARM64_NoFloatingPoint( + const ThreadContext::t64_t& thread_context, + CPUContextARM64* context); + +//! \brief Initializes FPSIMD state in a CPUContextARM64 from a native fpsimd +//! signal context structure on Linux. +//! +//! General purpose registers are not initialized. +//! +//! \param[in] thread_context The native fpsimd context. +//! \param[out] context The CPUContextARM64 structure to initialize. +void InitializeCPUContextARM64_OnlyFPSIMD( + const SignalFPSIMDContext& float_context, + CPUContextARM64* context); + +//! \brief Initializes FPSIMD state in a CPUContextARM64 to zero. +//! +//! General purpose registers are not initialized. +//! +//! \param[out] context The CPUContextARM64 structure to initialize. +void InitializeCPUContextARM64_ClearFPSIMD(CPUContextARM64* context); + +#endif // ARCH_CPU_ARM_FAMILY || DOXYGEN + } // namespace internal } // namespace crashpad diff --git a/snapshot/linux/exception_snapshot_linux.cc b/snapshot/linux/exception_snapshot_linux.cc index 1fcbced4..498b1f79 100644 --- a/snapshot/linux/exception_snapshot_linux.cc +++ b/snapshot/linux/exception_snapshot_linux.cc @@ -67,7 +67,10 @@ bool ExceptionSnapshotLinux::ReadContext( context_.x86); } else { - DCHECK_EQ(ucontext.fprs.magic, 0xffff); + if (ucontext.fprs.magic != 0xffff) { + LOG(ERROR) << "unexpected magic 0x" << std::hex << ucontext.fprs.magic; + return false; + } InitializeCPUContextX86( ucontext.mcontext.gprs, ucontext.fprs, context_.x86); } @@ -91,6 +94,163 @@ bool ExceptionSnapshotLinux::ReadContext( ucontext.mcontext.gprs, ucontext.fprs, context_.x86_64); return true; } + +#elif defined(ARCH_CPU_ARM_FAMILY) + +template <> +bool ExceptionSnapshotLinux::ReadContext( + ProcessReader* reader, + LinuxVMAddress context_address) { + context_.architecture = kCPUArchitectureARM; + context_.arm = &context_union_.arm; + + CPUContextARM* dest_context = context_.arm; + ProcessMemory* memory = reader->Memory(); + + LinuxVMAddress gprs_address = + context_address + offsetof(UContext, mcontext32) + + offsetof(MContext32, gprs); + + SignalThreadContext32 thread_context; + if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) { + LOG(ERROR) << "Couldn't read gprs"; + return false; + } + InitializeCPUContextARM_NoFloatingPoint(thread_context, dest_context); + + dest_context->have_fpa_regs = false; + + LinuxVMAddress reserved_address = + context_address + offsetof(UContext, reserved); + if ((reserved_address & 7) != 0) { + LOG(ERROR) << "invalid alignment 0x" << std::hex << reserved_address; + return false; + } + + constexpr VMSize kMaxContextSpace = 1024; + + ProcessMemoryRange range; + if (!range.Initialize(memory, false, reserved_address, kMaxContextSpace)) { + return false; + } + + dest_context->have_vfp_regs = false; + do { + CoprocessorContextHead head; + if (!range.Read(reserved_address, sizeof(head), &head)) { + LOG(ERROR) << "missing context terminator"; + return false; + } + reserved_address += sizeof(head); + + switch (head.magic) { + case VFP_MAGIC: + if (head.size != sizeof(SignalVFPContext) + sizeof(head)) { + LOG(ERROR) << "unexpected vfp context size " << head.size; + return false; + } + static_assert( + sizeof(SignalVFPContext::vfp) == sizeof(dest_context->vfp_regs), + "vfp context size mismatch"); + if (!range.Read(reserved_address + offsetof(SignalVFPContext, vfp), + sizeof(dest_context->vfp_regs), + &dest_context->vfp_regs)) { + LOG(ERROR) << "Couldn't read vfp"; + return false; + } + dest_context->have_vfp_regs = true; + return true; + + case CRUNCH_MAGIC: + case IWMMXT_MAGIC: + case DUMMY_MAGIC: + reserved_address += head.size - sizeof(head); + continue; + + case 0: + return true; + + default: + LOG(ERROR) << "invalid magic number 0x" << std::hex << head.magic; + return false; + } + } while (true); +} + +template <> +bool ExceptionSnapshotLinux::ReadContext( + ProcessReader* reader, + LinuxVMAddress context_address) { + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_union_.arm64; + + CPUContextARM64* dest_context = context_.arm64; + ProcessMemory* memory = reader->Memory(); + + LinuxVMAddress gprs_address = + context_address + offsetof(UContext, mcontext64) + + offsetof(MContext64, gprs); + + ThreadContext::t64_t thread_context; + if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) { + LOG(ERROR) << "Couldn't read gprs"; + return false; + } + InitializeCPUContextARM64_NoFloatingPoint(thread_context, dest_context); + + LinuxVMAddress reserved_address = + context_address + offsetof(UContext, reserved); + if ((reserved_address & 15) != 0) { + LOG(ERROR) << "invalid alignment 0x" << std::hex << reserved_address; + return false; + } + + constexpr VMSize kMaxContextSpace = 4096; + + ProcessMemoryRange range; + if (!range.Initialize(memory, true, reserved_address, kMaxContextSpace)) { + return false; + } + + do { + CoprocessorContextHead head; + if (!range.Read(reserved_address, sizeof(head), &head)) { + LOG(ERROR) << "missing context terminator"; + return false; + } + reserved_address += sizeof(head); + + switch (head.magic) { + case FPSIMD_MAGIC: + if (head.size != sizeof(SignalFPSIMDContext) + sizeof(head)) { + LOG(ERROR) << "unexpected fpsimd context size " << head.size; + return false; + } + SignalFPSIMDContext fpsimd; + if (!range.Read(reserved_address, sizeof(fpsimd), &fpsimd)) { + LOG(ERROR) << "Couldn't read fpsimd " << head.size; + return false; + } + InitializeCPUContextARM64_OnlyFPSIMD(fpsimd, dest_context); + return true; + + case ESR_MAGIC: + case EXTRA_MAGIC: + reserved_address += head.size - sizeof(head); + continue; + + case 0: + LOG(WARNING) << "fpsimd not found"; + InitializeCPUContextARM64_ClearFPSIMD(dest_context); + return true; + + default: + LOG(ERROR) << "invalid magic number 0x" << std::hex << head.magic; + return false; + } + } while (true); +} + #endif // ARCH_CPU_X86_FAMILY bool ExceptionSnapshotLinux::Initialize(ProcessReader* process_reader, diff --git a/snapshot/linux/exception_snapshot_linux.h b/snapshot/linux/exception_snapshot_linux.h index a744356d..73949668 100644 --- a/snapshot/linux/exception_snapshot_linux.h +++ b/snapshot/linux/exception_snapshot_linux.h @@ -73,12 +73,15 @@ class ExceptionSnapshotLinux final : public ExceptionSnapshot { template bool ReadContext(ProcessReader* reader, LinuxVMAddress context_address); -#if defined(ARCH_CPU_X86_FAMILY) union { +#if defined(ARCH_CPU_X86_FAMILY) CPUContextX86 x86; CPUContextX86_64 x86_64; - } context_union_; +#elif defined(ARCH_CPU_ARM_FAMILY) + CPUContextARM arm; + CPUContextARM64 arm64; #endif + } context_union_; CPUContext context_; std::vector codes_; uint64_t thread_id_; diff --git a/snapshot/linux/exception_snapshot_linux_test.cc b/snapshot/linux/exception_snapshot_linux_test.cc index bbccf7d8..24f0ef52 100644 --- a/snapshot/linux/exception_snapshot_linux_test.cc +++ b/snapshot/linux/exception_snapshot_linux_test.cc @@ -26,6 +26,7 @@ #include "gtest/gtest.h" #include "snapshot/cpu_architecture.h" #include "snapshot/linux/process_reader.h" +#include "snapshot/linux/signal_context.h" #include "sys/syscall.h" #include "test/errors.h" #include "test/linux/fake_ptrace_connection.h" @@ -92,6 +93,176 @@ void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { reinterpret_cast(&expected.__fpregs_mem)[byte_offset]); } } +#elif defined(ARCH_CPU_ARMEL) +// A native ucontext_t on ARM doesn't have enough regspace (yet) to hold all of +// the different possible coprocessor contexts at once. However, the ABI allows +// it and the native regspace may be expanded in the future. Append some extra +// space so this is testable now. +struct NativeCPUContext { + ucontext_t ucontext; + char extra[1024]; +}; + +struct CrunchContext { + uint32_t mvdx[16][2]; + uint32_t mvax[4][3]; + uint32_t dspsc[2]; +}; + +struct IWMMXTContext { + uint32_t save[38]; +}; + +struct TestCoprocessorContext { + struct { + internal::CoprocessorContextHead head; + CrunchContext context; + } crunch; + struct { + internal::CoprocessorContextHead head; + IWMMXTContext context; + } iwmmxt; + struct { + internal::CoprocessorContextHead head; + IWMMXTContext context; + } dummy; + struct { + internal::CoprocessorContextHead head; + internal::SignalVFPContext context; + } vfp; + internal::CoprocessorContextHead terminator; +}; + +void InitializeContext(NativeCPUContext* context) { + memset(context, 'x', sizeof(*context)); + + for (int index = 0; index < (&context->ucontext.uc_mcontext.fault_address - + &context->ucontext.uc_mcontext.arm_r0); + ++index) { + (&context->ucontext.uc_mcontext.arm_r0)[index] = index; + } + + static_assert( + sizeof(TestCoprocessorContext) <= + sizeof(context->ucontext.uc_regspace) + sizeof(context->extra), + "Insufficient context space"); + auto test_context = + reinterpret_cast(context->ucontext.uc_regspace); + + test_context->crunch.head.magic = CRUNCH_MAGIC; + test_context->crunch.head.size = sizeof(test_context->crunch); + memset( + &test_context->crunch.context, 'c', sizeof(test_context->crunch.context)); + + test_context->iwmmxt.head.magic = IWMMXT_MAGIC; + test_context->iwmmxt.head.size = sizeof(test_context->iwmmxt); + memset( + &test_context->iwmmxt.context, 'i', sizeof(test_context->iwmmxt.context)); + + test_context->dummy.head.magic = DUMMY_MAGIC; + test_context->dummy.head.size = sizeof(test_context->dummy); + memset( + &test_context->dummy.context, 'd', sizeof(test_context->dummy.context)); + + test_context->vfp.head.magic = VFP_MAGIC; + test_context->vfp.head.size = sizeof(test_context->vfp); + memset(&test_context->vfp.context, 'v', sizeof(test_context->vfp.context)); + for (size_t reg = 0; reg < arraysize(test_context->vfp.context.vfp.fpregs); + ++reg) { + test_context->vfp.context.vfp.fpregs[reg] = reg; + } + test_context->vfp.context.vfp.fpscr = 42; + + test_context->terminator.magic = 0; + test_context->terminator.size = 0; +} + +void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { + EXPECT_EQ(actual.architecture, kCPUArchitectureARM); + + EXPECT_EQ(memcmp(actual.arm->regs, + &expected.ucontext.uc_mcontext.arm_r0, + sizeof(actual.arm->regs)), + 0); + EXPECT_EQ(actual.arm->fp, expected.ucontext.uc_mcontext.arm_fp); + EXPECT_EQ(actual.arm->ip, expected.ucontext.uc_mcontext.arm_ip); + EXPECT_EQ(actual.arm->sp, expected.ucontext.uc_mcontext.arm_sp); + EXPECT_EQ(actual.arm->lr, expected.ucontext.uc_mcontext.arm_lr); + EXPECT_EQ(actual.arm->pc, expected.ucontext.uc_mcontext.arm_pc); + EXPECT_EQ(actual.arm->cpsr, expected.ucontext.uc_mcontext.arm_cpsr); + + EXPECT_FALSE(actual.arm->have_fpa_regs); + + EXPECT_TRUE(actual.arm->have_vfp_regs); + + auto test_context = reinterpret_cast( + expected.ucontext.uc_regspace); + + EXPECT_EQ(memcmp(actual.arm->vfp_regs.vfp, + &test_context->vfp.context.vfp, + sizeof(actual.arm->vfp_regs.vfp)), + 0); +} +#elif defined(ARCH_CPU_ARM64) +using NativeCPUContext = ucontext_t; + +struct TestCoprocessorContext { + esr_context esr; + fpsimd_context fpsimd; + _aarch64_ctx terminator; +}; + +void InitializeContext(NativeCPUContext* context) { + memset(context, 'x', sizeof(*context)); + + for (size_t index = 0; index < arraysize(context->uc_mcontext.regs); + ++index) { + context->uc_mcontext.regs[index] = index; + } + context->uc_mcontext.sp = 1; + context->uc_mcontext.pc = 2; + context->uc_mcontext.pstate = 3; + + auto test_context = reinterpret_cast( + context->uc_mcontext.__reserved); + + test_context->esr.head.magic = ESR_MAGIC; + test_context->esr.head.size = sizeof(test_context->esr); + memset(&test_context->esr.esr, 'e', sizeof(test_context->esr.esr)); + + test_context->fpsimd.head.magic = FPSIMD_MAGIC; + test_context->fpsimd.head.size = sizeof(test_context->fpsimd); + test_context->fpsimd.fpsr = 1; + test_context->fpsimd.fpcr = 2; + for (size_t reg = 0; reg < arraysize(test_context->fpsimd.vregs); ++reg) { + test_context->fpsimd.vregs[reg] = reg; + } + + test_context->terminator.magic = 0; + test_context->terminator.size = 0; +} + +void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { + EXPECT_EQ(actual.architecture, kCPUArchitectureARM64); + + EXPECT_EQ(memcmp(actual.arm64->regs, + expected.uc_mcontext.regs, + sizeof(actual.arm64->regs)), + 0); + EXPECT_EQ(actual.arm64->sp, expected.uc_mcontext.sp); + EXPECT_EQ(actual.arm64->pc, expected.uc_mcontext.pc); + EXPECT_EQ(actual.arm64->pstate, expected.uc_mcontext.pstate); + + auto test_context = reinterpret_cast( + expected.uc_mcontext.__reserved); + + EXPECT_EQ(actual.arm64->fpsr, test_context->fpsimd.fpsr); + EXPECT_EQ(actual.arm64->fpcr, test_context->fpsimd.fpcr); + EXPECT_EQ(memcmp(actual.arm64->fpsimd, + &test_context->fpsimd.vregs, + sizeof(actual.arm64->fpsimd)), + 0); +} #else #error Port. #endif diff --git a/snapshot/linux/signal_context.h b/snapshot/linux/signal_context.h index d8d5f69d..0476fdfd 100644 --- a/snapshot/linux/signal_context.h +++ b/snapshot/linux/signal_context.h @@ -12,13 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_LINUX_H_ -#define CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_LINUX_H_ +#ifndef CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_ +#define CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_ +#include #include #include +#include + #include "build/build_config.h" +#include "util/linux/thread_info.h" #include "util/linux/traits.h" namespace crashpad { @@ -85,6 +89,35 @@ struct Siginfo { }; }; +template +struct SignalStack { + typename Traits::Address stack_pointer; + uint32_t flags; + typename Traits::UInteger32_64Only padding; + typename Traits::Size size; +}; + +template +struct Sigset {}; + +template +struct Sigset< + Traits, + typename std::enable_if::value>::type> { + uint64_t val; +}; + +template +struct Sigset< + Traits, + typename std::enable_if::value>::type> { +#if defined(OS_ANDROID) + uint64_t val; +#else + typename Traits::ULong val[16]; +#endif // OS_ANDROID +}; + #if defined(ARCH_CPU_X86_FAMILY) struct SignalThreadContext32 { @@ -166,34 +199,6 @@ struct MContext { typename Traits::ULong_64Only reserved[8]; }; -template -struct SignalStack { - typename Traits::Address stack_pointer; - uint32_t flags; - typename Traits::UInteger32_64Only padding; - typename Traits::Size size; -}; - -template -struct Sigset {}; - -template <> -struct Sigset { - uint64_t val; -}; - -#if defined(OS_ANDROID) -template <> -struct Sigset { - uint64_t val; -}; -#else -template <> -struct Sigset { - ContextTraits64::ULong val[16]; -}; -#endif // OS_ANDROID - template struct UContext { typename Traits::ULong flags; @@ -204,6 +209,96 @@ struct UContext { typename Traits::FloatContext fprs; }; +#elif defined(ARCH_CPU_ARM_FAMILY) + +struct CoprocessorContextHead { + uint32_t magic; + uint32_t size; +}; + +struct SignalFPSIMDContext { + uint32_t fpsr; + uint32_t fpcr; + uint128_struct vregs[32]; +}; + +struct SignalVFPContext { + FloatContext::f32_t::vfp_t vfp; + struct vfp_exc { + uint32_t fpexc; + uint32_t fpinst; + uint32_t fpinst2; + } vfp_exc; + uint32_t padding; +}; + +struct SignalThreadContext32 { + uint32_t regs[11]; + uint32_t fp; + uint32_t ip; + uint32_t sp; + uint32_t lr; + uint32_t pc; + uint32_t cpsr; +}; + +using SignalThreadContext64 = ThreadContext::t64_t; + +struct MContext32 { + uint32_t trap_no; + uint32_t error_code; + uint32_t oldmask; + SignalThreadContext32 gprs; + uint32_t fault_address; +}; + +struct MContext64 { + uint64_t fault_address; + SignalThreadContext64 gprs; +}; + +struct ContextTraits32 : public Traits32 { + using MContext32 = MContext32; + using MContext64 = Nothing; +}; + +struct ContextTraits64 : public Traits64 { + using MContext32 = Nothing; + using MContext64 = MContext64; +}; + +template +struct UContext { + typename Traits::ULong flags; + typename Traits::Address link; + SignalStack stack; + typename Traits::MContext32 mcontext32; + Sigset sigmask; + char padding[128 - sizeof(sigmask)]; + typename Traits::Char_64Only padding2[8]; + typename Traits::MContext64 mcontext64; + typename Traits::Char_64Only padding3[8]; + char reserved[0]; +}; + +#if defined(ARCH_CPU_ARMEL) +static_assert(offsetof(UContext, mcontext32) == + offsetof(ucontext_t, uc_mcontext), + "context offset mismatch"); +static_assert(offsetof(UContext, reserved) == + offsetof(ucontext_t, uc_regspace), + "regspace offset mismatch"); + +#elif defined(ARCH_CPU_ARM64) +static_assert(offsetof(UContext, mcontext64) == + offsetof(ucontext_t, uc_mcontext), + "context offset mismtach"); +static_assert(offsetof(UContext, reserved) == + offsetof(ucontext_t, uc_mcontext) + + offsetof(mcontext_t, __reserved), + "reserved space offset mismtach"); +#endif + #else #error Port. #endif // ARCH_CPU_X86_FAMILY @@ -213,4 +308,4 @@ struct UContext { } // namespace internal } // namespace crashpad -#endif // CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_LINUX_H_ +#endif // CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_ diff --git a/snapshot/linux/system_snapshot_linux.cc b/snapshot/linux/system_snapshot_linux.cc index f3ed99be..c9c64383 100644 --- a/snapshot/linux/system_snapshot_linux.cc +++ b/snapshot/linux/system_snapshot_linux.cc @@ -197,6 +197,9 @@ CPUArchitecture SystemSnapshotLinux::GetCPUArchitecture() const { #if defined(ARCH_CPU_X86_FAMILY) return process_reader_->Is64Bit() ? kCPUArchitectureX86_64 : kCPUArchitectureX86; +#elif defined(ARCH_CPU_ARM_FAMILY) + return process_reader_->Is64Bit() ? kCPUArchitectureARM64 + : kCPUArchitectureARM; #else #error port to your architecture #endif @@ -206,6 +209,9 @@ uint32_t SystemSnapshotLinux::CPURevision() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); #if defined(ARCH_CPU_X86_FAMILY) return cpuid_.Revision(); +#elif defined(ARCH_CPU_ARM_FAMILY) + // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30 + return 0; #else #error port to your architecture #endif @@ -220,6 +226,9 @@ std::string SystemSnapshotLinux::CPUVendor() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); #if defined(ARCH_CPU_X86_FAMILY) return cpuid_.Vendor(); +#elif defined(ARCH_CPU_ARM_FAMILY) + // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30 + return std::string(); #else #error port to your architecture #endif @@ -264,7 +273,12 @@ uint64_t SystemSnapshotLinux::CPUX86Features() const { uint64_t SystemSnapshotLinux::CPUX86ExtendedFeatures() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_FAMILY) return cpuid_.ExtendedFeatures(); +#else + NOTREACHED(); + return 0; +#endif } uint32_t SystemSnapshotLinux::CPUX86Leaf7Features() const { @@ -340,7 +354,14 @@ std::string SystemSnapshotLinux::MachineDescription() const { bool SystemSnapshotLinux::NXEnabled() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_FAMILY) return cpuid_.NXEnabled(); +#elif defined(ARCH_CPU_ARM_FAMILY) + // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30 + return false; +#else +#error Port. +#endif // ARCH_CPU_X86_FAMILY } void SystemSnapshotLinux::TimeZone(DaylightSavingTimeStatus* dst_status, diff --git a/snapshot/linux/thread_snapshot_linux.cc b/snapshot/linux/thread_snapshot_linux.cc index dcfe5c40..f465a59c 100644 --- a/snapshot/linux/thread_snapshot_linux.cc +++ b/snapshot/linux/thread_snapshot_linux.cc @@ -56,6 +56,20 @@ bool ThreadSnapshotLinux::Initialize( thread.thread_info.float_context.f32, context_.x86); } +#elif defined(ARCH_CPU_ARM_FAMILY) + if (process_reader->Is64Bit()) { + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_union_.arm64; + InitializeCPUContextARM64(thread.thread_info.thread_context.t64, + thread.thread_info.float_context.f64, + context_.arm64); + } else { + context_.architecture = kCPUArchitectureARM; + context_.arm = &context_union_.arm; + InitializeCPUContextARM(thread.thread_info.thread_context.t32, + thread.thread_info.float_context.f32, + context_.arm); + } #else #error Port. #endif diff --git a/snapshot/linux/thread_snapshot_linux.h b/snapshot/linux/thread_snapshot_linux.h index e0ce7adb..1ba291dc 100644 --- a/snapshot/linux/thread_snapshot_linux.h +++ b/snapshot/linux/thread_snapshot_linux.h @@ -58,14 +58,17 @@ class ThreadSnapshotLinux final : public ThreadSnapshot { std::vector ExtraMemory() const override; private: -#if defined(ARCH_CPU_X86_FAMILY) union { +#if defined(ARCH_CPU_X86_FAMILY) CPUContextX86 x86; CPUContextX86_64 x86_64; - } context_union_; +#elif defined(ARCH_CPU_ARM_FAMILY) + CPUContextARM arm; + CPUContextARM64 arm64; #else #error Port. #endif // ARCH_CPU_X86_FAMILY + } context_union_; CPUContext context_; MemorySnapshotLinux stack_; LinuxVMAddress thread_specific_data_address_; diff --git a/util/linux/thread_info.h b/util/linux/thread_info.h index 4208be67..94424dd4 100644 --- a/util/linux/thread_info.h +++ b/util/linux/thread_info.h @@ -176,7 +176,7 @@ union FloatContext { } fpregs; // Reflects user_vfp in sys/user.h. - struct vfp { + struct vfp_t { uint64_t fpregs[32]; uint32_t fpscr; } vfp; diff --git a/util/linux/traits.h b/util/linux/traits.h index ca957681..dc5463d8 100644 --- a/util/linux/traits.h +++ b/util/linux/traits.h @@ -26,6 +26,7 @@ struct Traits32 { using ULong = uint32_t; using Clock = Long; using Size = uint32_t; + using Char_64Only = Nothing; using ULong_32Only = ULong; using ULong_64Only = Nothing; using UInteger32_64Only = Nothing; @@ -38,6 +39,7 @@ struct Traits64 { using ULong = uint64_t; using Clock = Long; using Size = uint64_t; + using Char_64Only = char; using ULong_32Only = Nothing; using ULong_64Only = ULong; using UInteger32_64Only = uint32_t; From c56e8549846d4818c027f0cb40916e1619585641 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 24 Jan 2018 10:12:00 -0800 Subject: [PATCH 120/326] Fix Doxygen errors Change-Id: I571d322e75afd33a679c488694db2e7bad3285ea Reviewed-on: https://chromium-review.googlesource.com/883903 Reviewed-by: Scott Graham Commit-Queue: Joshua Peraza --- snapshot/linux/cpu_context_linux.h | 2 +- util/process/process_memory_fuchsia.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/snapshot/linux/cpu_context_linux.h b/snapshot/linux/cpu_context_linux.h index 30bb205d..1ec4a1b4 100644 --- a/snapshot/linux/cpu_context_linux.h +++ b/snapshot/linux/cpu_context_linux.h @@ -120,7 +120,7 @@ void InitializeCPUContextARM64_NoFloatingPoint( //! //! General purpose registers are not initialized. //! -//! \param[in] thread_context The native fpsimd context. +//! \param[in] float_context The native fpsimd context. //! \param[out] context The CPUContextARM64 structure to initialize. void InitializeCPUContextARM64_OnlyFPSIMD( const SignalFPSIMDContext& float_context, diff --git a/util/process/process_memory_fuchsia.h b/util/process/process_memory_fuchsia.h index ebc93e0f..e6cdd3e8 100644 --- a/util/process/process_memory_fuchsia.h +++ b/util/process/process_memory_fuchsia.h @@ -37,7 +37,7 @@ class ProcessMemoryFuchsia final : public ProcessMemory { //! This method must be called successfully prior to calling any other method //! in this class. //! - //! \param[in] pid The handle to the target process. + //! \param[in] process The handle to the target process. //! //! \return `true` on success, `false` on failure with a message logged. bool Initialize(zx_handle_t process); From 7e7193d5f10328fd57db8d988d4bc9f33bdb6bee Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 24 Jan 2018 10:41:24 -0800 Subject: [PATCH 121/326] fuchsia: Update expected mini_chromium location for in-Fuchsia build The initial plan was to have crashpad and mini_chromium be siblings in the Fuchsia tree, but the Fuchsia team sensibly preferred to nest mini_chromium inside Crashpad https://fuchsia-review.googlesource.com/c/garnet/+/115120, so update the expected location to match. Conveniently this is where mini_chromium lives in a normal standalone build too. Bug: crashpad:196 Change-Id: Iedab0dd557fa248c6419380b676b71427279bba1 Reviewed-on: https://chromium-review.googlesource.com/883569 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- build/crashpad_buildconfig.gni | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/build/crashpad_buildconfig.gni b/build/crashpad_buildconfig.gni index 562ef142..e9ac5713 100644 --- a/build/crashpad_buildconfig.gni +++ b/build/crashpad_buildconfig.gni @@ -46,15 +46,10 @@ if (crashpad_is_in_chromium) { crashpad_is_clang = is_clang } else { - # Using mini_chromium, but in different locations depending on whether in - # Fuchsia, or standalone. - if (crashpad_is_in_fuchsia) { - import("//third_party/mini_chromium/build/compiler.gni") - import("//third_party/mini_chromium/build/platform.gni") - } else { - import("../third_party/mini_chromium/mini_chromium/build/compiler.gni") - import("../third_party/mini_chromium/mini_chromium/build/platform.gni") - } + # Both standalone and in Fuchsia tree use mini_chromium, and it's mapped into + # the same location in both cases. + import("../third_party/mini_chromium/mini_chromium/build/compiler.gni") + import("../third_party/mini_chromium/mini_chromium/build/platform.gni") crashpad_is_mac = mini_chromium_is_mac crashpad_is_win = mini_chromium_is_win crashpad_is_linux = mini_chromium_is_linux From 9d7a5b10eca07cde757eaee06e6b6e4346f9799c Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 24 Jan 2018 12:02:59 -0800 Subject: [PATCH 122/326] gn: Fix relative path to base for in-Fuchsia build Change from an absolute path to a relative one so that the location of Crashpad and mini_chromium don't matter, as long as they're in the same relative locations. Bug: crashpad:196 Change-Id: I20380b02f211ca0ac04cffaab7d7510d2c8f35ea Reviewed-on: https://chromium-review.googlesource.com/884370 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- third_party/mini_chromium/BUILD.gn | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/third_party/mini_chromium/BUILD.gn b/third_party/mini_chromium/BUILD.gn index 9ab81780..e11d0111 100644 --- a/third_party/mini_chromium/BUILD.gn +++ b/third_party/mini_chromium/BUILD.gn @@ -19,13 +19,9 @@ group("base") { public_deps = [ "//base", ] - } else if (crashpad_is_in_fuchsia) { + } else if (crashpad_is_standalone || crashpad_is_in_fuchsia) { public_deps = [ - "//third_party/mini_chromium/base", - ] - } else if (crashpad_is_standalone) { - public_deps = [ - "//third_party/mini_chromium/mini_chromium/base", + "mini_chromium/base", ] } } From 26cd6138af864fd03672e019a722ea09f1aacd41 Mon Sep 17 00:00:00 2001 From: Mostyn Bramley-Moore Date: Wed, 24 Jan 2018 23:25:43 +0100 Subject: [PATCH 123/326] [jumbo] add some missing include guards Change-Id: I062c853d65c3e89a61920d790d9bc5c993b46fcd Reviewed-on: https://chromium-review.googlesource.com/884581 Commit-Queue: Scott Graham Reviewed-by: Scott Graham --- AUTHORS | 1 + snapshot/x86/cpuid_reader.h | 5 +++++ util/win/nt_internals.h | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/AUTHORS b/AUTHORS index b1e4ddf9..eb3534a6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -8,3 +8,4 @@ Google Inc. Opera Software ASA +Vewd Software AS diff --git a/snapshot/x86/cpuid_reader.h b/snapshot/x86/cpuid_reader.h index 0fd02cfd..b6782afb 100644 --- a/snapshot/x86/cpuid_reader.h +++ b/snapshot/x86/cpuid_reader.h @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#ifndef CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_ +#define CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_ + #include #include @@ -61,3 +64,5 @@ class CpuidReader { } // namespace internal } // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_ diff --git a/util/win/nt_internals.h b/util/win/nt_internals.h index a2a88b77..dad04056 100644 --- a/util/win/nt_internals.h +++ b/util/win/nt_internals.h @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#ifndef CRASHPAD_UTIL_WIN_NT_INTERNALS_H_ +#define CRASHPAD_UTIL_WIN_NT_INTERNALS_H_ + #include #include @@ -92,3 +95,5 @@ void RtlGetUnloadEventTraceEx(ULONG** element_size, void** event_trace); } // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_NT_INTERNALS_H_ From 48abd4a60fdf2d640e48bc436a15a5887f31a209 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 25 Jan 2018 14:47:28 -0800 Subject: [PATCH 124/326] Add CRASHPAD_CHILD_TEST_MAIN() helper for multiprocess tests Extends MultiprocessExec to support running functions registered via CRASHPAD_CHILD_TEST_MAIN() as the main of a new child process. Additionally, implements Fuchsia exit code checking, and adds a CRASHPAD_CHILD_TEST_MAIN()-based test for that. Bug: crashpad:196, crashpad:215 Change-Id: I49ce3f4d95a3b9823813e6df5a602cee2583bcf8 Reviewed-on: https://chromium-review.googlesource.com/879563 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- build/run_tests.py | 3 +- test/BUILD.gn | 1 + test/gtest_main.cc | 24 ++++++++++ test/multiprocess.h | 2 + test/multiprocess_exec.cc | 74 +++++++++++++++++++++++++++++++ test/multiprocess_exec.h | 65 +++++++++++++++++++++++++++ test/multiprocess_exec_fuchsia.cc | 28 +++++++++++- test/multiprocess_exec_test.cc | 42 ++++++++++++++++++ test/multiprocess_exec_win.cc | 8 ++++ test/test.gyp | 1 + 10 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 test/multiprocess_exec.cc diff --git a/build/run_tests.py b/build/run_tests.py index 19833a8e..43e49f2c 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -407,7 +407,8 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line): done_message = 'TERMINATED: ' + unique_id namespace_command = [ - 'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root, '--', + 'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root, + '--replace-child-argv0=/pkg/bin/' + test, '--', staging_root + '/bin/' + test] + extra_command_line netruncmd(namespace_command, ['echo', done_message]) diff --git a/test/BUILD.gn b/test/BUILD.gn index 4b1eff24..f7541749 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -32,6 +32,7 @@ static_library("test") { "main_arguments.cc", "main_arguments.h", "multiprocess.h", + "multiprocess_exec.cc", "multiprocess_exec.h", "process_type.cc", "process_type.h", diff --git a/test/gtest_main.cc b/test/gtest_main.cc index 5a3d7996..ebdbeb9d 100644 --- a/test/gtest_main.cc +++ b/test/gtest_main.cc @@ -16,6 +16,7 @@ #include "gtest/gtest.h" #include "test/gtest_disabled.h" #include "test/main_arguments.h" +#include "test/multiprocess_exec.h" #if defined(CRASHPAD_TEST_LAUNCHER_GMOCK) #include "gmock/gmock.h" @@ -31,11 +32,34 @@ #include "base/test/test_suite.h" #endif // CRASHPAD_IS_IN_CHROMIUM +namespace { + +bool GetChildTestFunctionName(std::string* child_func_name) { + constexpr size_t arg_length = + sizeof(crashpad::test::internal::kChildTestFunction) - 1; + for (const auto& it : crashpad::test::GetMainArguments()) { + if (it.compare( + 0, arg_length, crashpad::test::internal::kChildTestFunction) == 0) { + *child_func_name = it.substr(arg_length); + return true; + } + } + return false; +} + +} // namespace + int main(int argc, char* argv[]) { crashpad::test::InitializeMainArguments(argc, argv); testing::AddGlobalTestEnvironment( crashpad::test::DisabledTestGtestEnvironment::Get()); + std::string child_func_name; + if (GetChildTestFunctionName(&child_func_name)) { + return crashpad::test::internal::CheckedInvokeMultiprocessChild( + child_func_name); + } + #if defined(CRASHPAD_IS_IN_CHROMIUM) #if defined(OS_WIN) diff --git a/test/multiprocess.h b/test/multiprocess.h index 35d18b2f..1d3ee9b2 100644 --- a/test/multiprocess.h +++ b/test/multiprocess.h @@ -49,11 +49,13 @@ class Multiprocess { //! that call `exit()` or `_exit()`. kTerminationNormal = false, +#if !defined(OS_FUCHSIA) // There are no signals on Fuchsia. //! \brief The child terminated by signal. //! //! Signal termination happens as a result of a crash, a call to `abort()`, //! assertion failure (including gtest assertions), etc. kTerminationSignal, +#endif // !defined(OS_FUCHSIA) }; Multiprocess(); diff --git a/test/multiprocess_exec.cc b/test/multiprocess_exec.cc new file mode 100644 index 00000000..fb332417 --- /dev/null +++ b/test/multiprocess_exec.cc @@ -0,0 +1,74 @@ +// Copyright 2018 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 "test/multiprocess_exec.h" + +#include + +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "test/main_arguments.h" +#include "util/stdlib/map_insert.h" +#include "test/test_paths.h" + +namespace crashpad { +namespace test { + +namespace internal { + +namespace { + +std::map* GetMultiprocessFunctionMap() { + static auto* map = new std::map(); + return map; +} + +} // namespace + +AppendMultiprocessTest::AppendMultiprocessTest(const std::string& test_name, + int (*main_function_pointer)()) { + CHECK(MapInsertOrReplace( + GetMultiprocessFunctionMap(), test_name, main_function_pointer, nullptr)) + << test_name << " already registered"; +} + +int CheckedInvokeMultiprocessChild(const std::string& test_name) { + const auto* functions = internal::GetMultiprocessFunctionMap(); + auto it = functions->find(test_name); + CHECK(it != functions->end()) + << "child main " << test_name << " not registered"; + return (*it->second)(); +} + +} // namespace internal + +void MultiprocessExec::SetChildTestMainFunction( + const std::string& function_name) { + std::vector rest(GetMainArguments().begin() + 1, + GetMainArguments().end()); + rest.push_back(internal::kChildTestFunction + function_name); + +#if defined(OS_WIN) + // Instead of using argv[0] on Windows, use the actual binary name. This is + // necessary because if originally the test isn't run with ".exe" on the + // command line, then argv[0] also won't include ".exe". This argument is used + // as the lpApplicationName argument to CreateProcess(), and so will fail. + SetChildCommand(TestPaths::Executable(), &rest); +#else + SetChildCommand(base::FilePath(GetMainArguments()[0]), &rest); +#endif +} + +} // namespace test +} // namespace crashpad diff --git a/test/multiprocess_exec.h b/test/multiprocess_exec.h index 258a3f8e..58b6808e 100644 --- a/test/multiprocess_exec.h +++ b/test/multiprocess_exec.h @@ -23,9 +23,57 @@ #include "build/build_config.h" #include "test/multiprocess.h" +//! \file + namespace crashpad { namespace test { +namespace internal { + +//! \brief Command line argument used to indicate that a child test function +//! should be run. +constexpr char kChildTestFunction[] = "--child-test-function="; + + +//! \brief Helper class used by CRASHPAD_CHILD_TEST_MAIN() to insert a child +//! function into the global mapping. +class AppendMultiprocessTest { + public: + AppendMultiprocessTest(const std::string& test_name, + int (*main_function_pointer)()); +}; + +//! \brief Used to run a child test function by name, registered by +//! CRASHPAD_CHILD_TEST_MAIN(). +//! +//! \return The exit code of the child process after running the function named +//! by \a test_name. Aborts with a CHECK() if \a test_name wasn't +//! registered. +int CheckedInvokeMultiprocessChild(const std::string& test_name); + +} // namespace internal + +//! \brief Registers a function that can be invoked as a child process by +//! MultiprocessExec. +//! +//! Used as: +//! +//! \code +//! CRASHPAD_CHILD_TEST_MAIN(MyChildTestBody) { +//! ... child body ... +//! } +//! \endcode +//! +//! In the main (parent) test body, this function can be run in a child process +//! via MultiprocessExec::SetChildTestMainFunction(). +#define CRASHPAD_CHILD_TEST_MAIN(test_main) \ + int test_main(); \ + namespace { \ + ::crashpad::test::internal::AppendMultiprocessTest \ + AddMultiprocessTest##_##test_main(#test_main, (test_main)); \ + } /* namespace */ \ + int test_main() + //! \brief Manages an `exec()`-based multiprocess test. //! //! These tests are based on `fork()` and `exec()`. The parent process is able @@ -44,14 +92,31 @@ class MultiprocessExec : public Multiprocess { //! //! This method must be called before the test can be Run(). //! + //! This method is useful when a custom executable is required for the child + //! binary, however, SetChildTestMainFunction() should generally be preferred. + //! //! \param[in] command The executable’s pathname. //! \param[in] arguments The command-line arguments to pass to the child //! process in its `argv[]` vector. This vector must begin at `argv[1]`, //! as \a command is implicitly used as `argv[0]`. This argument may be //! `nullptr` if no command-line arguments are to be passed. + //! + //! \sa SetChildTestMainFunction void SetChildCommand(const base::FilePath& command, const std::vector* arguments); + //! \brief Calls SetChildCommand() to run a child test main function + //! registered with CRASHPAD_CHILD_TEST_MAIN(). + //! + //! This uses the same launch mechanism as SetChildCommand(), but coordinates + //! with test/gtest_main.cc to allow for simple registration of a child + //! processes' entry point via the helper macro, rather than needing to + //! create a separate build target. + //! + //! \param[in] function_name The name of the function as passed to + //! CRASHPAD_CHILD_TEST_MAIN(). + void SetChildTestMainFunction(const std::string& function_name); + protected: ~MultiprocessExec(); diff --git a/test/multiprocess_exec_fuchsia.cc b/test/multiprocess_exec_fuchsia.cc index 324e7eb0..2f788028 100644 --- a/test/multiprocess_exec_fuchsia.cc +++ b/test/multiprocess_exec_fuchsia.cc @@ -51,13 +51,39 @@ void Multiprocess::Run() { // And then run the parent actions in this process. RunParent(); - // Reap the child. + // Wait until the child exits. zx_signals_t signals; ASSERT_EQ( zx_object_wait_one( info_->child.get(), ZX_TASK_TERMINATED, ZX_TIME_INFINITE, &signals), ZX_OK); ASSERT_EQ(signals, ZX_TASK_TERMINATED); + + // Get the child's exit code. + zx_info_process_t proc_info; + zx_status_t status = zx_object_get_info(info_->child.get(), + ZX_INFO_PROCESS, + &proc_info, + sizeof(proc_info), + nullptr, + nullptr); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_object_get_info"; + ADD_FAILURE() << "Unable to get exit code of child"; + } else { + if (code_ != proc_info.return_code) { + ADD_FAILURE() << "Child exited with code " << proc_info.return_code + << ", expected exit with code " << code_; + } + } +} + +void Multiprocess::SetExpectedChildTermination(TerminationReason reason, + int code) { + EXPECT_EQ(info_, nullptr) + << "SetExpectedChildTermination() must be called before Run()"; + reason_ = reason; + code_ = code; } Multiprocess::~Multiprocess() { diff --git a/test/multiprocess_exec_test.cc b/test/multiprocess_exec_test.cc index b8a5bc19..0104b0f3 100644 --- a/test/multiprocess_exec_test.cc +++ b/test/multiprocess_exec_test.cc @@ -14,6 +14,7 @@ #include "test/multiprocess_exec.h" +#include "base/logging.h" #include "base/macros.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" @@ -56,6 +57,47 @@ TEST(MultiprocessExec, MultiprocessExec) { multiprocess_exec.Run(); } + +CRASHPAD_CHILD_TEST_MAIN(SimpleMultiprocess) { + char c; + CheckedReadFileExactly(StdioFileHandle(StdioStream::kStandardInput), &c, 1); + LOG_IF(FATAL, c != 'z'); + + c = 'Z'; + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), &c, 1); + return 0; +} + +TEST(MultiprocessExec, MultiprocessExecSimpleChild) { + TestMultiprocessExec exec; + exec.SetChildTestMainFunction("SimpleMultiprocess"); + exec.Run(); +}; + + +CRASHPAD_CHILD_TEST_MAIN(SimpleMultiprocessReturnsNonZero) { + return 123; +} + +class TestMultiprocessExecEmpty final : public MultiprocessExec { + public: + TestMultiprocessExecEmpty() = default; + ~TestMultiprocessExecEmpty() = default; + + private: + void MultiprocessParent() override {} + + DISALLOW_COPY_AND_ASSIGN(TestMultiprocessExecEmpty); +}; + +TEST(MultiprocessExec, MultiprocessExecSimpleChildReturnsNonZero) { + TestMultiprocessExecEmpty exec; + exec.SetChildTestMainFunction("SimpleMultiprocessReturnsNonZero"); + exec.SetExpectedChildTermination( + Multiprocess::TerminationReason::kTerminationNormal, 123); + exec.Run(); +}; + } // namespace } // namespace test } // namespace crashpad diff --git a/test/multiprocess_exec_win.cc b/test/multiprocess_exec_win.cc index 98fd0ac1..f5190c97 100644 --- a/test/multiprocess_exec_win.cc +++ b/test/multiprocess_exec_win.cc @@ -57,6 +57,14 @@ void Multiprocess::Run() { CloseHandle(info_->process_info.hProcess); } +void Multiprocess::SetExpectedChildTermination(TerminationReason reason, + int code) { + EXPECT_EQ(info_, nullptr) + << "SetExpectedChildTermination() must be called before Run()"; + reason_ = reason; + code_ = code; +} + Multiprocess::~Multiprocess() { delete info_; } diff --git a/test/test.gyp b/test/test.gyp index 699d984f..a3721efd 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -58,6 +58,7 @@ 'main_arguments.cc', 'main_arguments.h', 'multiprocess.h', + 'multiprocess_exec.cc', 'multiprocess_exec.h', 'multiprocess_exec_posix.cc', 'multiprocess_exec_win.cc', From 11589d9b32552fc78ead584546baa2056b3492b1 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 25 Jan 2018 13:37:12 -0800 Subject: [PATCH 125/326] Rework ElfImageReader.MainExecutableChild to not rely on fork() Switches from test::Multiprocess to test::MultiprocessExec for ElfImageReader.MainExecutableChild. Uses the new child process launching, and passes the expected symbol address from the child to the parent, rather than assuming the value will be the same in both processes. And, enables the test on Fuchsia since it now works. Bug: crashpad:196, crashpad:215 Change-Id: I3b43407b6584275d61bedc9c13d1625b950fc23b Reviewed-on: https://chromium-review.googlesource.com/884993 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- snapshot/elf/elf_image_reader_test.cc | 47 ++++++++++++++++++++------- test/multiprocess_exec.h | 8 +++++ test/multiprocess_exec_fuchsia.cc | 4 +++ test/multiprocess_exec_posix.cc | 4 +++ test/multiprocess_exec_win.cc | 4 +++ 5 files changed, 55 insertions(+), 12 deletions(-) diff --git a/snapshot/elf/elf_image_reader_test.cc b/snapshot/elf/elf_image_reader_test.cc index a69dfbe0..715b3b25 100644 --- a/snapshot/elf/elf_image_reader_test.cc +++ b/snapshot/elf/elf_image_reader_test.cc @@ -21,6 +21,7 @@ #include "build/build_config.h" #include "gtest/gtest.h" #include "test/multiprocess.h" +#include "test/multiprocess_exec.h" #include "test/process_type.h" #include "util/file/file_io.h" #include "util/misc/address_types.h" @@ -68,6 +69,8 @@ void LocateExecutable(ProcessType process, sizeof(debug_address)); ASSERT_EQ(status, ZX_OK) << "zx_object_get_property: ZX_PROP_PROCESS_DEBUG_ADDR"; + // Can be 0 if requested before the loader has loaded anything. + EXPECT_NE(debug_address, 0u); constexpr auto k_r_debug_map_offset = offsetof(r_debug, r_map); uintptr_t map; @@ -120,7 +123,8 @@ void ExpectSymbol(ElfImageReader* reader, reader->GetDynamicSymbol("notasymbol", &symbol_address, &symbol_size)); } -void ReadThisExecutableInTarget(ProcessType process) { +void ReadThisExecutableInTarget(ProcessType process, + VMAddress exported_symbol_address) { #if defined(ARCH_CPU_64_BITS) constexpr bool am_64_bit = true; #else @@ -139,9 +143,8 @@ void ReadThisExecutableInTarget(ProcessType process) { ElfImageReader reader; ASSERT_TRUE(reader.Initialize(range, elf_address)); - ExpectSymbol(&reader, - "ElfImageReaderTestExportedSymbol", - FromPointerCast(ElfImageReaderTestExportedSymbol)); + ExpectSymbol( + &reader, "ElfImageReaderTestExportedSymbol", exported_symbol_address); ElfImageReader::NoteReader::Result result; std::string note_name; @@ -201,25 +204,45 @@ void ReadLibcInTarget(ProcessType process) { } TEST(ElfImageReader, MainExecutableSelf) { - ReadThisExecutableInTarget(GetSelfProcess()); + ReadThisExecutableInTarget( + GetSelfProcess(), + FromPointerCast(ElfImageReaderTestExportedSymbol)); } -#if !defined(OS_FUCHSIA) // TODO(scottmg): Port to MultiprocessExec. -class ReadExecutableChildTest : public Multiprocess { +CRASHPAD_CHILD_TEST_MAIN(ReadExecutableChild) { + VMAddress exported_symbol_address = + FromPointerCast(ElfImageReaderTestExportedSymbol); + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + &exported_symbol_address, + sizeof(exported_symbol_address)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadExecutableChildTest : public MultiprocessExec { public: - ReadExecutableChildTest() : Multiprocess() {} - ~ReadExecutableChildTest() {} + ReadExecutableChildTest() : MultiprocessExec() {} private: - void MultiprocessParent() { ReadThisExecutableInTarget(ChildPID()); } - void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } + void MultiprocessParent() { + // This read serves two purposes -- on Fuchsia, the loader may have not + // filled in debug address as soon as the child process handle is valid, so + // this causes a wait at least until the main() of the child, at which point + // it will always be valid. Secondarily, the address of the symbol to be + // looked up needs to be communicated. + VMAddress exported_symbol_address; + CheckedReadFileExactly(ReadPipeHandle(), + &exported_symbol_address, + sizeof(exported_symbol_address)); + ReadThisExecutableInTarget(ChildProcess(), exported_symbol_address); + } }; TEST(ElfImageReader, MainExecutableChild) { ReadExecutableChildTest test; + test.SetChildTestMainFunction("ReadExecutableChild"); test.Run(); } -#endif // !OS_FUCHSIA TEST(ElfImageReader, OneModuleSelf) { ReadLibcInTarget(GetSelfProcess()); diff --git a/test/multiprocess_exec.h b/test/multiprocess_exec.h index 58b6808e..84335b28 100644 --- a/test/multiprocess_exec.h +++ b/test/multiprocess_exec.h @@ -22,6 +22,7 @@ #include "base/macros.h" #include "build/build_config.h" #include "test/multiprocess.h" +#include "test/process_type.h" //! \file @@ -117,6 +118,13 @@ class MultiprocessExec : public Multiprocess { //! CRASHPAD_CHILD_TEST_MAIN(). void SetChildTestMainFunction(const std::string& function_name); + //! \brief Returns a ProcessType representing the child process. + //! + //! This method is only valid during the body of MultiprocessParent(). + //! + //! \return A platform-specific type representing the child process. + ProcessType ChildProcess(); + protected: ~MultiprocessExec(); diff --git a/test/multiprocess_exec_fuchsia.cc b/test/multiprocess_exec_fuchsia.cc index 2f788028..e6306080 100644 --- a/test/multiprocess_exec_fuchsia.cc +++ b/test/multiprocess_exec_fuchsia.cc @@ -177,5 +177,9 @@ void MultiprocessExec::MultiprocessChild() { info()->child.reset(child); } +ProcessType MultiprocessExec::ChildProcess() { + return info()->child.get(); +} + } // namespace test } // namespace crashpad diff --git a/test/multiprocess_exec_posix.cc b/test/multiprocess_exec_posix.cc index 528e8c77..c91b7f73 100644 --- a/test/multiprocess_exec_posix.cc +++ b/test/multiprocess_exec_posix.cc @@ -148,5 +148,9 @@ void MultiprocessExec::MultiprocessChild() { FAIL() << ErrnoMessage("execv") << ": " << argv_[0]; } +ProcessType MultiprocessExec::ChildProcess() { + return ChildPID(); +} + } // namespace test } // namespace crashpad diff --git a/test/multiprocess_exec_win.cc b/test/multiprocess_exec_win.cc index f5190c97..38978186 100644 --- a/test/multiprocess_exec_win.cc +++ b/test/multiprocess_exec_win.cc @@ -170,5 +170,9 @@ void MultiprocessExec::MultiprocessChild() { &info()->process_info)); } +ProcessType MultiprocessExec::ChildProcess() { + return info()->process_info.hProcess; +} + } // namespace test } // namespace crashpad From c04352a2e63c24e8e1ec53b69d7077bf8b0dd08b Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 25 Jan 2018 13:37:37 -0800 Subject: [PATCH 126/326] Rework ElfImageReader.OneModuleChild to not rely on fork() Switches from test::Multiprocess to test::MultiprocessExec for ElfImageReader.OneModuleChild. Uses the new child process launching, and passes the address of libc and the address of getpid from the child to parent, rather than assuming the values will be the same in both processes. And, enables the test on Fuchsia since it now works. Bug: crashpad:196, crashpad:215 Change-Id: I3650c16c4fccfe9c1e4147192fdc88b997460060 Reviewed-on: https://chromium-review.googlesource.com/887373 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- snapshot/elf/elf_image_reader_test.cc | 57 +++++++++++++++++++-------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/snapshot/elf/elf_image_reader_test.cc b/snapshot/elf/elf_image_reader_test.cc index 715b3b25..32b8cfd8 100644 --- a/snapshot/elf/elf_image_reader_test.cc +++ b/snapshot/elf/elf_image_reader_test.cc @@ -20,7 +20,6 @@ #include "base/logging.h" #include "build/build_config.h" #include "gtest/gtest.h" -#include "test/multiprocess.h" #include "test/multiprocess_exec.h" #include "test/process_type.h" #include "util/file/file_io.h" @@ -178,20 +177,15 @@ void ReadThisExecutableInTarget(ProcessType process, ElfImageReader::NoteReader::Result::kNoMoreNotes); } -// Assumes that libc is loaded at the same address in this process as in the -// target, which it is for the fork test below. -void ReadLibcInTarget(ProcessType process) { +void ReadLibcInTarget(ProcessType process, + VMAddress elf_address, + VMAddress getpid_address) { #if defined(ARCH_CPU_64_BITS) constexpr bool am_64_bit = true; #else constexpr bool am_64_bit = false; #endif // ARCH_CPU_64_BITS - Dl_info info; - ASSERT_TRUE(dladdr(reinterpret_cast(getpid), &info)) << "dladdr:" - << dlerror(); - VMAddress elf_address = FromPointerCast(info.dli_fbase); - ProcessMemoryNative memory; ASSERT_TRUE(memory.Initialize(process)); ProcessMemoryRange range; @@ -200,7 +194,7 @@ void ReadLibcInTarget(ProcessType process) { ElfImageReader reader; ASSERT_TRUE(reader.Initialize(range, elf_address)); - ExpectSymbol(&reader, "getpid", FromPointerCast(getpid)); + ExpectSymbol(&reader, "getpid", getpid_address); } TEST(ElfImageReader, MainExecutableSelf) { @@ -245,25 +239,54 @@ TEST(ElfImageReader, MainExecutableChild) { } TEST(ElfImageReader, OneModuleSelf) { - ReadLibcInTarget(GetSelfProcess()); + Dl_info info; + ASSERT_TRUE(dladdr(reinterpret_cast(getpid), &info)) << "dladdr:" + << dlerror(); + VMAddress elf_address = FromPointerCast(info.dli_fbase); + ReadLibcInTarget( + GetSelfProcess(), elf_address, FromPointerCast(getpid)); } -#if !defined(OS_FUCHSIA) // TODO(scottmg): Port to MultiprocessExec. -class ReadLibcChildTest : public Multiprocess { +CRASHPAD_CHILD_TEST_MAIN(ReadLibcChild) { + // Get the address of libc (by using getpid() as a representative member), + // and also the address of getpid() itself, and write them to the parent, so + // it can validate reading this information back out. + Dl_info info; + EXPECT_TRUE(dladdr(reinterpret_cast(getpid), &info)) + << "dladdr:" << dlerror(); + VMAddress elf_address = FromPointerCast(info.dli_fbase); + VMAddress getpid_address = FromPointerCast(getpid); + + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + &elf_address, + sizeof(elf_address)); + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + &getpid_address, + sizeof(getpid_address)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadLibcChildTest : public MultiprocessExec { public: - ReadLibcChildTest() : Multiprocess() {} + ReadLibcChildTest() : MultiprocessExec() {} ~ReadLibcChildTest() {} private: - void MultiprocessParent() { ReadLibcInTarget(ChildPID()); } - void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } + void MultiprocessParent() { + VMAddress elf_address, getpid_address; + CheckedReadFileExactly(ReadPipeHandle(), &elf_address, sizeof(elf_address)); + CheckedReadFileExactly( + ReadPipeHandle(), &getpid_address, sizeof(getpid_address)); + ReadLibcInTarget(ChildProcess(), elf_address, getpid_address); + } }; TEST(ElfImageReader, OneModuleChild) { ReadLibcChildTest test; + test.SetChildTestMainFunction("ReadLibcChild"); test.Run(); } -#endif // !OS_FUCHSIA } // namespace } // namespace test From ec71b63d6fab8af02f17351eae93ecf59cec2da4 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 30 Jan 2018 10:12:47 -0800 Subject: [PATCH 127/326] Rework ProcessMemory.Read(Self|Forked) to work without fork() Instead of allocating test memory in the parent and then forking and comparing against it, the child does the allocation and passes back the region's size and address. Additionally, switch the memcmp()s to be value-based comparisons instead because the region isn't available in the parent. Also renames ProcessMemory.ReadForked to .ReadChild to be correct after the change from Multiprocess to MultiprocessExec. This is necessary to have the tests work on Fuchsia. Bug: crashpad:196, crashpad:215 Change-Id: Id996a21180d87c7f2556283e9f54f6128726f9b8 Reviewed-on: https://chromium-review.googlesource.com/892102 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai Reviewed-by: Joshua Peraza --- util/process/process_memory_test.cc | 199 +++++++++++++++++----------- 1 file changed, 121 insertions(+), 78 deletions(-) diff --git a/util/process/process_memory_test.cc b/util/process/process_memory_test.cc index b6987a10..ce628e73 100644 --- a/util/process/process_memory_test.cc +++ b/util/process/process_memory_test.cc @@ -23,17 +23,131 @@ #include "gtest/gtest.h" #include "test/errors.h" #include "test/multiprocess.h" +#include "test/multiprocess_exec.h" +#include "test/process_type.h" #include "util/file/file_io.h" #include "util/misc/from_pointer_cast.h" #include "util/posix/scoped_mmap.h" -#include "util/process/process_memory_linux.h" +#include "util/process/process_memory_native.h" namespace crashpad { namespace test { namespace { -// TODO(scottmg): https://crashpad.chromium.org/bug/196. Multiprocess isn't -// ported yet. +void DoChildReadTestSetup(size_t* region_size, + std::unique_ptr* region) { + *region_size = 4 * getpagesize(); + region->reset(new char[*region_size]); + for (size_t index = 0; index < *region_size; ++index) { + (*region)[index] = index % 256; + } +} + +CRASHPAD_CHILD_TEST_MAIN(ReadTestChild) { + size_t region_size; + std::unique_ptr region; + DoChildReadTestSetup(®ion_size, ®ion); + FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); + CheckedWriteFile(out, ®ion_size, sizeof(region_size)); + VMAddress address = FromPointerCast(region.get()); + CheckedWriteFile(out, &address, sizeof(address)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadTest : public MultiprocessExec { + public: + ReadTest() : MultiprocessExec() { + SetChildTestMainFunction("ReadTestChild"); + } + + void RunAgainstSelf() { + size_t region_size; + std::unique_ptr region; + DoChildReadTestSetup(®ion_size, ®ion); + DoTest(GetSelfProcess(), + region_size, + FromPointerCast(region.get())); + } + + void RunAgainstChild() { Run(); } + + private: + void MultiprocessParent() override { + size_t region_size; + VMAddress region; + ASSERT_TRUE( + ReadFileExactly(ReadPipeHandle(), ®ion_size, sizeof(region_size))); + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), ®ion, sizeof(region))); + DoTest(ChildProcess(), region_size, region); + } + + void DoTest(ProcessType process, size_t region_size, VMAddress address) { + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); + + std::unique_ptr result(new char[region_size]); + + // Ensure that the entire region can be read. + ASSERT_TRUE(memory.Read(address, region_size, result.get())); + for (size_t i = 0; i < region_size; ++i) { + EXPECT_EQ(result[i], static_cast(i % 256)); + } + + // Ensure that a read of length 0 succeeds and doesn’t touch the result. + memset(result.get(), '\0', region_size); + ASSERT_TRUE(memory.Read(address, 0, result.get())); + for (size_t i = 0; i < region_size; ++i) { + EXPECT_EQ(result[i], 0); + } + + // Ensure that a read starting at an unaligned address works. + ASSERT_TRUE(memory.Read(address + 1, region_size - 1, result.get())); + for (size_t i = 0; i < region_size - 1; ++i) { + EXPECT_EQ(result[i], static_cast((i + 1) % 256)); + } + + // Ensure that a read ending at an unaligned address works. + ASSERT_TRUE(memory.Read(address, region_size - 1, result.get())); + for (size_t i = 0; i < region_size - 1; ++i) { + EXPECT_EQ(result[i], static_cast(i % 256)); + } + + // Ensure that a read starting and ending at unaligned addresses works. + ASSERT_TRUE(memory.Read(address + 1, region_size - 2, result.get())); + for (size_t i = 0; i < region_size - 2; ++i) { + EXPECT_EQ(result[i], static_cast((i + 1) % 256)); + } + + // Ensure that a read of exactly one page works. + size_t page_size = getpagesize(); + ASSERT_GE(region_size, page_size + page_size); + ASSERT_TRUE(memory.Read(address + page_size, page_size, result.get())); + for (size_t i = 0; i < page_size; ++i) { + EXPECT_EQ(result[i], static_cast((i + page_size) % 256)); + } + + // Ensure that reading exactly a single byte works. + result[1] = 'J'; + ASSERT_TRUE(memory.Read(address + 2, 1, result.get())); + EXPECT_EQ(result[0], 2); + EXPECT_EQ(result[1], 'J'); + } + + DISALLOW_COPY_AND_ASSIGN(ReadTest); +}; + +TEST(ProcessMemory, ReadSelf) { + ReadTest test; + test.RunAgainstSelf(); +} + +TEST(ProcessMemory, ReadChild) { + ReadTest test; + test.RunAgainstChild(); +} + +// TODO(scottmg): Need to be ported to MultiprocessExec and not rely on fork(). #if !defined(OS_FUCHSIA) class TargetProcessTest : public Multiprocess { @@ -55,77 +169,6 @@ class TargetProcessTest : public Multiprocess { DISALLOW_COPY_AND_ASSIGN(TargetProcessTest); }; -class ReadTest : public TargetProcessTest { - public: - ReadTest() - : TargetProcessTest(), - page_size_(getpagesize()), - region_size_(4 * page_size_), - region_(new char[region_size_]) { - for (size_t index = 0; index < region_size_; ++index) { - region_[index] = index % 256; - } - } - - private: - void DoTest(pid_t pid) override { - ProcessMemoryLinux memory; - ASSERT_TRUE(memory.Initialize(pid)); - - VMAddress address = FromPointerCast(region_.get()); - std::unique_ptr result(new char[region_size_]); - - // Ensure that the entire region can be read. - ASSERT_TRUE(memory.Read(address, region_size_, result.get())); - EXPECT_EQ(memcmp(region_.get(), result.get(), region_size_), 0); - - // Ensure that a read of length 0 succeeds and doesn’t touch the result. - memset(result.get(), '\0', region_size_); - ASSERT_TRUE(memory.Read(address, 0, result.get())); - for (size_t i = 0; i < region_size_; ++i) { - EXPECT_EQ(result[i], 0); - } - - // Ensure that a read starting at an unaligned address works. - ASSERT_TRUE(memory.Read(address + 1, region_size_ - 1, result.get())); - EXPECT_EQ(memcmp(region_.get() + 1, result.get(), region_size_ - 1), 0); - - // Ensure that a read ending at an unaligned address works. - ASSERT_TRUE(memory.Read(address, region_size_ - 1, result.get())); - EXPECT_EQ(memcmp(region_.get(), result.get(), region_size_ - 1), 0); - - // Ensure that a read starting and ending at unaligned addresses works. - ASSERT_TRUE(memory.Read(address + 1, region_size_ - 2, result.get())); - EXPECT_EQ(memcmp(region_.get() + 1, result.get(), region_size_ - 2), 0); - - // Ensure that a read of exactly one page works. - ASSERT_TRUE(memory.Read(address + page_size_, page_size_, result.get())); - EXPECT_EQ(memcmp(region_.get() + page_size_, result.get(), page_size_), 0); - - // Ensure that reading exactly a single byte works. - result[1] = 'J'; - ASSERT_TRUE(memory.Read(address + 2, 1, result.get())); - EXPECT_EQ(result[0], region_[2]); - EXPECT_EQ(result[1], 'J'); - } - - const size_t page_size_; - const size_t region_size_; - std::unique_ptr region_; - - DISALLOW_COPY_AND_ASSIGN(ReadTest); -}; - -TEST(ProcessMemory, ReadSelf) { - ReadTest test; - test.RunAgainstSelf(); -} - -TEST(ProcessMemory, ReadForked) { - ReadTest test; - test.RunAgainstForked(); -} - bool ReadCString(const ProcessMemory& memory, const char* pointer, std::string* result) { @@ -159,7 +202,7 @@ class ReadCStringTest : public TargetProcessTest { private: void DoTest(pid_t pid) override { - ProcessMemoryLinux memory; + ProcessMemoryNative memory; ASSERT_TRUE(memory.Initialize(pid)); std::string result; @@ -263,7 +306,7 @@ class ReadUnmappedTest : public TargetProcessTest { private: void DoTest(pid_t pid) override { - ProcessMemoryLinux memory; + ProcessMemoryNative memory; ASSERT_TRUE(memory.Initialize(pid)); VMAddress page_addr1 = pages_.addr_as(); @@ -341,8 +384,8 @@ class ReadCStringUnmappedTest : public TargetProcessTest { } private: - void DoTest(pid_t pid) { - ProcessMemoryLinux memory; + void DoTest(pid_t pid) override { + ProcessMemoryNative memory; ASSERT_TRUE(memory.Initialize(pid)); if (limit_size_) { From 693ff6d550c6b2191992a44e653de9c91f9c2229 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 30 Jan 2018 10:16:57 -0800 Subject: [PATCH 128/326] Rework ReadCString[SizeLimited](Self|Forked) to not use fork() Instead of using pointers shared between the parent/child due to fork, explicitly builds and passes them between processes. This is unfortunately a bit more verbose, but seems like it tests functionality a little better, and is required to have the test work on Fuchsia. Also renames the ...Forked to ...Child to be correct after the change from Multiprocess to MultiprocessExec. Bug: crashpad:196, crashpad:215 Change-Id: I610a7f1e35b6513805c27d9e610f7a9b9820cabc Reviewed-on: https://chromium-review.googlesource.com/892286 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai Reviewed-by: Joshua Peraza --- util/process/process_memory_test.cc | 271 ++++++++++++++++++---------- 1 file changed, 175 insertions(+), 96 deletions(-) diff --git a/util/process/process_memory_test.cc b/util/process/process_memory_test.cc index ce628e73..28d62d02 100644 --- a/util/process/process_memory_test.cc +++ b/util/process/process_memory_test.cc @@ -147,6 +147,181 @@ TEST(ProcessMemory, ReadChild) { test.RunAgainstChild(); } +constexpr char kConstCharEmpty[] = ""; +constexpr char kConstCharShort[] = "A short const char[]"; + +#define SHORT_LOCAL_STRING "A short local variable char[]" + +std::string MakeLongString() { + std::string long_string; + const size_t kStringLongSize = 4 * getpagesize(); + for (size_t index = 0; index < kStringLongSize; ++index) { + long_string.push_back((index % 255) + 1); + } + EXPECT_EQ(long_string.size(), kStringLongSize); + return long_string; +} + +void DoChildCStringReadTestSetup(const char** const_empty, + const char** const_short, + const char** local_empty, + const char** local_short, + std::string* long_string) { + *const_empty = kConstCharEmpty; + *const_short = kConstCharShort; + *local_empty = ""; + *local_short = SHORT_LOCAL_STRING; + *long_string = MakeLongString(); +} + +CRASHPAD_CHILD_TEST_MAIN(ReadCStringTestChild) { + const char* const_empty; + const char* const_short; + const char* local_empty; + const char* local_short; + std::string long_string; + DoChildCStringReadTestSetup( + &const_empty, &const_short, &local_empty, &local_short, &long_string); + const auto write_address = [](const char* p) { + VMAddress address = FromPointerCast(p); + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + &address, + sizeof(address)); + }; + write_address(const_empty); + write_address(const_short); + write_address(local_empty); + write_address(local_short); + write_address(long_string.c_str()); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadCStringTest : public MultiprocessExec { + public: + ReadCStringTest(bool limit_size) + : MultiprocessExec(), limit_size_(limit_size) { + SetChildTestMainFunction("ReadCStringTestChild"); + } + + void RunAgainstSelf() { + const char* const_empty; + const char* const_short; + const char* local_empty; + const char* local_short; + std::string long_string; + DoChildCStringReadTestSetup( + &const_empty, &const_short, &local_empty, &local_short, &long_string); + DoTest(GetSelfProcess(), + FromPointerCast(const_empty), + FromPointerCast(const_short), + FromPointerCast(local_empty), + FromPointerCast(local_short), + FromPointerCast(long_string.c_str())); + } + void RunAgainstChild() { Run(); } + + private: + void MultiprocessParent() override { +#define DECLARE_AND_READ_ADDRESS(name) \ + VMAddress name; \ + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &name, sizeof(name))); + DECLARE_AND_READ_ADDRESS(const_empty_address); + DECLARE_AND_READ_ADDRESS(const_short_address); + DECLARE_AND_READ_ADDRESS(local_empty_address); + DECLARE_AND_READ_ADDRESS(local_short_address); + DECLARE_AND_READ_ADDRESS(long_string_address); +#undef DECLARE_AND_READ_ADDRESS + + DoTest(ChildProcess(), + const_empty_address, + const_short_address, + local_empty_address, + local_short_address, + long_string_address); + } + + void DoTest(ProcessType process, + VMAddress const_empty_address, + VMAddress const_short_address, + VMAddress local_empty_address, + VMAddress local_short_address, + VMAddress long_string_address) { + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); + + std::string result; + + if (limit_size_) { + ASSERT_TRUE(memory.ReadCStringSizeLimited( + const_empty_address, arraysize(kConstCharEmpty), &result)); + EXPECT_EQ(result, kConstCharEmpty); + + ASSERT_TRUE(memory.ReadCStringSizeLimited( + const_short_address, arraysize(kConstCharShort), &result)); + EXPECT_EQ(result, kConstCharShort); + EXPECT_FALSE(memory.ReadCStringSizeLimited( + const_short_address, arraysize(kConstCharShort) - 1, &result)); + + ASSERT_TRUE( + memory.ReadCStringSizeLimited(local_empty_address, 1, &result)); + EXPECT_EQ(result, ""); + + ASSERT_TRUE(memory.ReadCStringSizeLimited( + local_short_address, strlen(SHORT_LOCAL_STRING) + 1, &result)); + EXPECT_EQ(result, SHORT_LOCAL_STRING); + EXPECT_FALSE(memory.ReadCStringSizeLimited( + local_short_address, strlen(SHORT_LOCAL_STRING), &result)); + + std::string long_string_for_comparison = MakeLongString(); + ASSERT_TRUE(memory.ReadCStringSizeLimited( + long_string_address, long_string_for_comparison.size() + 1, &result)); + EXPECT_EQ(result, long_string_for_comparison); + EXPECT_FALSE(memory.ReadCStringSizeLimited( + long_string_address, long_string_for_comparison.size(), &result)); + } else { + ASSERT_TRUE(memory.ReadCString(const_empty_address, &result)); + EXPECT_EQ(result, kConstCharEmpty); + + ASSERT_TRUE(memory.ReadCString(const_short_address, &result)); + EXPECT_EQ(result, kConstCharShort); + + ASSERT_TRUE(memory.ReadCString(local_empty_address, &result)); + EXPECT_EQ(result, ""); + + ASSERT_TRUE(memory.ReadCString(local_short_address, &result)); + EXPECT_EQ(result, SHORT_LOCAL_STRING); + + ASSERT_TRUE(memory.ReadCString(long_string_address, &result)); + EXPECT_EQ(result, MakeLongString()); + } + } + + const bool limit_size_; + + DISALLOW_COPY_AND_ASSIGN(ReadCStringTest); +}; + +TEST(ProcessMemory, ReadCStringSelf) { + ReadCStringTest test(/* limit_size= */ false); + test.RunAgainstSelf(); +} + +TEST(ProcessMemory, ReadCStringChild) { + ReadCStringTest test(/* limit_size= */ false); + test.RunAgainstChild(); +} + +TEST(ProcessMemory, ReadCStringSizeLimitedSelf) { + ReadCStringTest test(/* limit_size= */ true); + test.RunAgainstSelf(); +} + +TEST(ProcessMemory, ReadCStringSizeLimitedChild) { + ReadCStringTest test(/* limit_size= */ true); + test.RunAgainstChild(); +} + // TODO(scottmg): Need to be ported to MultiprocessExec and not rely on fork(). #if !defined(OS_FUCHSIA) @@ -183,102 +358,6 @@ bool ReadCStringSizeLimited(const ProcessMemory& memory, FromPointerCast(pointer), size, result); } -constexpr char kConstCharEmpty[] = ""; -constexpr char kConstCharShort[] = "A short const char[]"; - -class ReadCStringTest : public TargetProcessTest { - public: - ReadCStringTest(bool limit_size) - : TargetProcessTest(), - member_char_empty_(""), - member_char_short_("A short member char[]"), - limit_size_(limit_size) { - const size_t kStringLongSize = 4 * getpagesize(); - for (size_t index = 0; index < kStringLongSize; ++index) { - string_long_.push_back((index % 255) + 1); - } - EXPECT_EQ(string_long_.size(), kStringLongSize); - } - - private: - void DoTest(pid_t pid) override { - ProcessMemoryNative memory; - ASSERT_TRUE(memory.Initialize(pid)); - - std::string result; - - if (limit_size_) { - ASSERT_TRUE(ReadCStringSizeLimited( - memory, kConstCharEmpty, arraysize(kConstCharEmpty), &result)); - EXPECT_EQ(result, kConstCharEmpty); - - ASSERT_TRUE(ReadCStringSizeLimited( - memory, kConstCharShort, arraysize(kConstCharShort), &result)); - EXPECT_EQ(result, kConstCharShort); - EXPECT_FALSE(ReadCStringSizeLimited( - memory, kConstCharShort, arraysize(kConstCharShort) - 1, &result)); - - ASSERT_TRUE(ReadCStringSizeLimited( - memory, member_char_empty_, strlen(member_char_empty_) + 1, &result)); - EXPECT_EQ(result, member_char_empty_); - - ASSERT_TRUE(ReadCStringSizeLimited( - memory, member_char_short_, strlen(member_char_short_) + 1, &result)); - EXPECT_EQ(result, member_char_short_); - EXPECT_FALSE(ReadCStringSizeLimited( - memory, member_char_short_, strlen(member_char_short_), &result)); - - ASSERT_TRUE(ReadCStringSizeLimited( - memory, string_long_.c_str(), string_long_.size() + 1, &result)); - EXPECT_EQ(result, string_long_); - EXPECT_FALSE(ReadCStringSizeLimited( - memory, string_long_.c_str(), string_long_.size(), &result)); - } else { - ASSERT_TRUE(ReadCString(memory, kConstCharEmpty, &result)); - EXPECT_EQ(result, kConstCharEmpty); - - ASSERT_TRUE(ReadCString(memory, kConstCharShort, &result)); - EXPECT_EQ(result, kConstCharShort); - - ASSERT_TRUE(ReadCString(memory, member_char_empty_, &result)); - EXPECT_EQ(result, member_char_empty_); - - ASSERT_TRUE(ReadCString(memory, member_char_short_, &result)); - EXPECT_EQ(result, member_char_short_); - - ASSERT_TRUE(ReadCString(memory, string_long_.c_str(), &result)); - EXPECT_EQ(result, string_long_); - } - } - - std::string string_long_; - const char* member_char_empty_; - const char* member_char_short_; - const bool limit_size_; - - DISALLOW_COPY_AND_ASSIGN(ReadCStringTest); -}; - -TEST(ProcessMemory, ReadCStringSelf) { - ReadCStringTest test(/* limit_size= */ false); - test.RunAgainstSelf(); -} - -TEST(ProcessMemory, ReadCStringForked) { - ReadCStringTest test(/* limit_size= */ false); - test.RunAgainstForked(); -} - -TEST(ProcessMemory, ReadCStringSizeLimitedSelf) { - ReadCStringTest test(/* limit_size= */ true); - test.RunAgainstSelf(); -} - -TEST(ProcessMemory, ReadCStringSizeLimitedForked) { - ReadCStringTest test(/* limit_size= */ true); - test.RunAgainstForked(); -} - class ReadUnmappedTest : public TargetProcessTest { public: ReadUnmappedTest() From c9244d58df1d27baf91b6cad117f2b0061da152d Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 30 Jan 2018 12:41:09 -0800 Subject: [PATCH 129/326] Add ARM family minidump support Bug: crashpad:30 Change-Id: I6784d42ba6c525c4e0b16dfdbbb4949c83e32fea Reviewed-on: https://chromium-review.googlesource.com/888541 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- minidump/minidump_context.h | 96 ++++++++++++ minidump/minidump_context_writer.cc | 99 ++++++++++++ minidump/minidump_context_writer.h | 80 ++++++++++ minidump/minidump_context_writer_test.cc | 116 ++++++++------ minidump/test/minidump_context_test_util.cc | 103 ++++++++++++ minidump/test/minidump_context_test_util.h | 9 ++ snapshot/test/test_cpu_context.cc | 165 +++++++++++++------- snapshot/test/test_cpu_context.h | 40 ++--- 8 files changed, 587 insertions(+), 121 deletions(-) diff --git a/minidump/minidump_context.h b/minidump/minidump_context.h index 1226b654..a7328cad 100644 --- a/minidump/minidump_context.h +++ b/minidump/minidump_context.h @@ -336,6 +336,102 @@ struct alignas(16) MinidumpContextAMD64 { //! \} }; +//! \brief 32-bit ARM-specifc flags for MinidumpContextARM::context_flags. +enum MinidumpContextARMFlags : uint32_t { + //! \brief Identifies the context structure as 32-bit ARM. + kMinidumpContextARM = 0x40000000, + + //! \brief Indicates the validity of integer regsiters. + //! + //! Regsiters `r0`-`r15` and `cpsr` are valid. + kMinidumpContextARMInteger = kMinidumpContextARM | 0x00000002, + + //! \brief Inidicates the validity of VFP regsiters. + //! + //! Registers `d0`-`d31` and `fpscr` are valid. + kMinidumpContextARMVFP = kMinidumpContextARM | 0x00000004, + + //! \brief Indicates the validity of all registers. + kMinidumpContextARMAll = kMinidumpContextARMInteger | kMinidumpContextARMVFP, +}; + +//! \brief A 32-bit ARM CPU context (register state) carried in a minidump file. +struct MinidumpContextARM { + //! \brief A bitfield composed of values of #MinidumpContextFlags and + //! #MinidumpContextARMFlags. + //! + //! This field identifies the context structure as a 32-bit ARM CPU context, + //! and indicates which other fields in the structure are valid. + uint32_t context_flags; + + //! \brief General-purpose registers `r0`-`r15`. + uint32_t regs[11]; + uint32_t fp; // r11 + uint32_t ip; // r12 + uint32_t sp; // r13 + uint32_t lr; // r14 + uint32_t pc; // r15 + + //! \brief Current program status register. + uint32_t cpsr; + + //! \brief Floating-point status and control register. + uint32_t fpscr; + + //! \brief VFP registers `d0`-`d31`. + uint64_t vfp[32]; + + //! \brief This space is unused. It is included for compatibility with + //! breakpad (which also doesn't use it). + uint32_t extra[8]; +}; + +//! \brief 64-bit ARM-specifc flags for MinidumpContextARM64::context_flags. +enum MinidumpContextARM64Flags : uint32_t { + //! \brief Identifies the context structure as 64-bit ARM. + kMinidumpContextARM64 = 0x80000000, + + //! \brief Indicates the validty of integer registers. + //! + //! Registers `x0`-`x31`, `pc`, and `cpsr`. + kMinidumpContextARM64Integer = kMinidumpContextARM64 | 0x00000002, + + //! \brief Indicates the validity of fpsimd registers. + //! + //! Registers `v0`-`v31`, `fpsr`, and `fpcr` are valid. + kMinidumpContextARM64Fpsimd = kMinidumpContextARM64 | 0x00000004, + + //! \brief Indicates the validity of all registers. + kMinidumpContextARM64All = + kMinidumpContextARM64Integer | kMinidumpContextARM64Fpsimd, +}; + +//! \brief A 64-bit ARM CPU context (register state) carried in a minidump file. +struct MinidumpContextARM64 { + uint64_t context_flags; + + //! \brief General-purpose registers `x0`-`x30`. + uint64_t regs[31]; + + //! \brief Stack pointer or `x31`. + uint64_t sp; + + //! \brief Program counter. + uint64_t pc; + + //! \brief Current program status register. + uint32_t cpsr; + + //! \brief Floating-point status register. + uint32_t fpsr; + + //! \brief Floating-point control register. + uint32_t fpcr; + + //! \brief NEON registers `v0`-`v31`. + uint128_struct fpsimd[32]; +}; + } // namespace crashpad #endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_ diff --git a/minidump/minidump_context_writer.cc b/minidump/minidump_context_writer.cc index 218d7775..fbf291ea 100644 --- a/minidump/minidump_context_writer.cc +++ b/minidump/minidump_context_writer.cc @@ -73,6 +73,19 @@ MinidumpContextWriter::CreateFromSnapshot(const CPUContext* context_snapshot) { break; } + case kCPUArchitectureARM: { + context = std::make_unique(); + reinterpret_cast(context.get()) + ->InitializeFromSnapshot(context_snapshot->arm); + break; + } + + case kCPUArchitectureARM64: { + context = std::make_unique(); + reinterpret_cast(context.get()) + ->InitializeFromSnapshot(context_snapshot->arm64); + } + default: { LOG(ERROR) << "unknown context architecture " << context_snapshot->architecture; @@ -239,4 +252,90 @@ size_t MinidumpContextAMD64Writer::ContextSize() const { return sizeof(context_); } +MinidumpContextARMWriter::MinidumpContextARMWriter() + : MinidumpContextWriter(), context_() { + context_.context_flags = kMinidumpContextARM; +} + +MinidumpContextARMWriter::~MinidumpContextARMWriter() = default; + +void MinidumpContextARMWriter::InitializeFromSnapshot( + const CPUContextARM* context_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(context_.context_flags, kMinidumpContextARM); + + context_.context_flags = kMinidumpContextARMAll; + + static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs), + "GPRS size mismatch"); + memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); + context_.fp = context_snapshot->fp; + context_.ip = context_snapshot->ip; + context_.sp = context_snapshot->sp; + context_.lr = context_snapshot->lr; + context_.pc = context_snapshot->pc; + context_.cpsr = context_snapshot->cpsr; + + context_.fpscr = context_snapshot->vfp_regs.fpscr; + static_assert(sizeof(context_.vfp) == sizeof(context_snapshot->vfp_regs.vfp), + "VFP size mismatch"); + memcpy(context_.vfp, context_snapshot->vfp_regs.vfp, sizeof(context_.vfp)); + + memset(context_.extra, 0, sizeof(context_.extra)); +} + +bool MinidumpContextARMWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + return file_writer->Write(&context_, sizeof(context_)); +} + +size_t MinidumpContextARMWriter::ContextSize() const { + DCHECK_GE(state(), kStateFrozen); + return sizeof(context_); +} + +MinidumpContextARM64Writer::MinidumpContextARM64Writer() + : MinidumpContextWriter(), context_() { + context_.context_flags = kMinidumpContextARM64; +} + +MinidumpContextARM64Writer::~MinidumpContextARM64Writer() = default; + +void MinidumpContextARM64Writer::InitializeFromSnapshot( + const CPUContextARM64* context_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(context_.context_flags, kMinidumpContextARM64); + + context_.context_flags = kMinidumpContextARM64All; + + static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs), + "GPRs size mismatch"); + memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); + context_.sp = context_snapshot->sp; + context_.pc = context_snapshot->pc; + + if (context_snapshot->pstate > + std::numeric_limits::max()) { + LOG(WARNING) << "pstate truncation"; + } + context_.cpsr = + static_cast(context_snapshot->pstate); + + context_.fpsr = context_snapshot->fpsr; + context_.fpcr = context_snapshot->fpcr; + static_assert(sizeof(context_.fpsimd) == sizeof(context_snapshot->fpsimd), + "FPSIMD size mismatch"); + memcpy(context_.fpsimd, context_snapshot->fpsimd, sizeof(context_.fpsimd)); +} + +bool MinidumpContextARM64Writer::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + return file_writer->Write(&context_, sizeof(context_)); +} + +size_t MinidumpContextARM64Writer::ContextSize() const { + DCHECK_GE(state(), kStateFrozen); + return sizeof(context_); +} + } // namespace crashpad diff --git a/minidump/minidump_context_writer.h b/minidump/minidump_context_writer.h index 25d717e5..fb3b1513 100644 --- a/minidump/minidump_context_writer.h +++ b/minidump/minidump_context_writer.h @@ -155,6 +155,86 @@ class MinidumpContextAMD64Writer final : public MinidumpContextWriter { DISALLOW_COPY_AND_ASSIGN(MinidumpContextAMD64Writer); }; +//! \brief The writer for a MinidumpContextARM structure in a minidump file. +class MinidumpContextARMWriter final : public MinidumpContextWriter { + public: + MinidumpContextARMWriter(); + ~MinidumpContextARMWriter() override; + + //! \brief Initializes the MinidumpContextARM based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutation of context() may be done before + //! calling this method, and it is not normally necessary to alter + //! context() after calling this method. + void InitializeFromSnapshot(const CPUContextARM* context_snapshot); + + //! \brief Returns a pointer to the context structure that this object will + //! write. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. The context structure + //! must only be modified while this object is in the #kStateMutable + //! state. + MinidumpContextARM* context() { return &context_; } + + protected: + // MinidumpWritable: + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpContextWriter: + size_t ContextSize() const override; + + private: + MinidumpContextARM context_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpContextARMWriter); +}; + +//! \brief The writer for a MinidumpContextARM64 structure in a minidump file. +class MinidumpContextARM64Writer final : public MinidumpContextWriter { + public: + MinidumpContextARM64Writer(); + ~MinidumpContextARM64Writer() override; + + //! \brief Initializes the MinidumpContextARM64 based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutation of context() may be done before + //! calling this method, and it is not normally necessary to alter + //! context() after calling this method. + void InitializeFromSnapshot(const CPUContextARM64* context_snapshot); + + //! \brief Returns a pointer to the context structure that this object will + //! write. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. The context structure + //! must only be modified while this object is in the #kStateMutable + //! state. + MinidumpContextARM64* context() { return &context_; } + + protected: + // MinidumpWritable: + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpContextWriter: + size_t ContextSize() const override; + + private: + MinidumpContextARM64 context_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpContextARM64Writer); +}; + } // namespace crashpad #endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_ diff --git a/minidump/minidump_context_writer_test.cc b/minidump/minidump_context_writer_test.cc index 82b4db75..0122683c 100644 --- a/minidump/minidump_context_writer_test.cc +++ b/minidump/minidump_context_writer_test.cc @@ -28,6 +28,20 @@ namespace crashpad { namespace test { namespace { +template +void EmptyContextTest(void (*expect_context)(uint32_t, const Context*, bool)) { + Writer context_writer; + StringFile string_file; + EXPECT_TRUE(context_writer.WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), sizeof(Context)); + + const Context* observed = + MinidumpWritableAtRVA(string_file.string(), 0); + ASSERT_TRUE(observed); + + expect_context(0, observed, false); +} + TEST(MinidumpContextWriter, MinidumpContextX86Writer) { StringFile string_file; @@ -36,16 +50,8 @@ TEST(MinidumpContextWriter, MinidumpContextX86Writer) { // context. SCOPED_TRACE("zero"); - MinidumpContextX86Writer context_writer; - - EXPECT_TRUE(context_writer.WriteEverything(&string_file)); - ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextX86)); - - const MinidumpContextX86* observed = - MinidumpWritableAtRVA(string_file.string(), 0); - ASSERT_TRUE(observed); - - ExpectMinidumpContextX86(0, observed, false); + EmptyContextTest( + ExpectMinidumpContextX86); } { @@ -85,16 +91,8 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) { // context. SCOPED_TRACE("zero"); - MinidumpContextAMD64Writer context_writer; - - EXPECT_TRUE(context_writer.WriteEverything(&string_file)); - ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextAMD64)); - - const MinidumpContextAMD64* observed = - MinidumpWritableAtRVA(string_file.string(), 0); - ASSERT_TRUE(observed); - - ExpectMinidumpContextAMD64(0, observed, false); + EmptyContextTest( + ExpectMinidumpContextAMD64); } { @@ -117,48 +115,72 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) { } } -TEST(MinidumpContextWriter, CreateFromSnapshot_X86) { - constexpr uint32_t kSeed = 32; - - CPUContextX86 context_snapshot_x86; - CPUContext context_snapshot; - context_snapshot.x86 = &context_snapshot_x86; - InitializeCPUContextX86(&context_snapshot, kSeed); - +template +void FromSnapshotTest(const CPUContext& snapshot_context, + void (*expect_context)(uint32_t, const Context*, bool), + uint32_t seed) { std::unique_ptr context_writer = - MinidumpContextWriter::CreateFromSnapshot(&context_snapshot); + MinidumpContextWriter::CreateFromSnapshot(&snapshot_context); ASSERT_TRUE(context_writer); StringFile string_file; ASSERT_TRUE(context_writer->WriteEverything(&string_file)); - const MinidumpContextX86* observed = - MinidumpWritableAtRVA(string_file.string(), 0); + const Context* observed = + MinidumpWritableAtRVA(string_file.string(), 0); ASSERT_TRUE(observed); - ExpectMinidumpContextX86(kSeed, observed, true); + expect_context(seed, observed, true); } -TEST(MinidumpContextWriter, CreateFromSnapshot_AMD64) { +TEST(MinidumpContextWriter, X86_FromSnapshot) { + constexpr uint32_t kSeed = 32; + CPUContextX86 context_x86; + CPUContext context; + context.x86 = &context_x86; + InitializeCPUContextX86(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextX86, kSeed); +} + +TEST(MinidumpContextWriter, AMD64_FromSnapshot) { constexpr uint32_t kSeed = 64; + CPUContextX86_64 context_x86_64; + CPUContext context; + context.x86_64 = &context_x86_64; + InitializeCPUContextX86_64(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextAMD64, kSeed); +} - CPUContextX86_64 context_snapshot_x86_64; - CPUContext context_snapshot; - context_snapshot.x86_64 = &context_snapshot_x86_64; - InitializeCPUContextX86_64(&context_snapshot, kSeed); +TEST(MinidumpContextWriter, ARM_Zeros) { + EmptyContextTest( + ExpectMinidumpContextARM); +} - std::unique_ptr context_writer = - MinidumpContextWriter::CreateFromSnapshot(&context_snapshot); - ASSERT_TRUE(context_writer); +TEST(MinidumpContextWRiter, ARM64_Zeros) { + EmptyContextTest( + ExpectMinidumpContextARM64); +} - StringFile string_file; - ASSERT_TRUE(context_writer->WriteEverything(&string_file)); +TEST(MinidumpContextWriter, ARM_FromSnapshot) { + constexpr uint32_t kSeed = 32; + CPUContextARM context_arm; + CPUContext context; + context.arm = &context_arm; + InitializeCPUContextARM(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextARM, kSeed); +} - const MinidumpContextAMD64* observed = - MinidumpWritableAtRVA(string_file.string(), 0); - ASSERT_TRUE(observed); - - ExpectMinidumpContextAMD64(kSeed, observed, true); +TEST(MinidumpContextWriter, ARM64_FromSnapshot) { + constexpr uint32_t kSeed = 64; + CPUContextARM64 context_arm64; + CPUContext context; + context.arm64 = &context_arm64; + InitializeCPUContextARM64(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextARM64, kSeed); } } // namespace diff --git a/minidump/test/minidump_context_test_util.cc b/minidump/test/minidump_context_test_util.cc index cd8e5227..0dc3a971 100644 --- a/minidump/test/minidump_context_test_util.cc +++ b/minidump/test/minidump_context_test_util.cc @@ -140,6 +140,61 @@ void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context, context->last_exception_from_rip = value++; } +void InitializeMinidumpContextARM(MinidumpContextARM* context, uint32_t seed) { + if (seed == 0) { + memset(context, 0, sizeof(*context)); + context->context_flags = kMinidumpContextARM; + return; + } + + context->context_flags = kMinidumpContextARMAll; + + uint32_t value = seed; + + for (size_t index = 0; index < arraysize(context->regs); ++index) { + context->regs[index] = value++; + } + context->fp = value++; + context->ip = value++; + context->ip = value++; + context->sp = value++; + context->lr = value++; + context->pc = value++; + context->cpsr = value++; + + for (size_t index = 0; index < arraysize(context->vfp); ++index) { + context->vfp[index] = value++; + } + context->fpscr = value++; +} + +void InitializeMinidumpContextARM64(MinidumpContextARM64* context, + uint32_t seed) { + if (seed == 0) { + memset(context, 0, sizeof(*context)); + context->context_flags = kMinidumpContextARM64; + return; + } + + context->context_flags = kMinidumpContextARM64All; + + uint32_t value = seed; + + for (size_t index = 0; index < arraysize(context->regs); ++index) { + context->regs[index] = value++; + } + context->sp = value++; + context->pc = value++; + context->cpsr = value++; + + for (size_t index = 0; index < arraysize(context->fpsimd); ++index) { + context->fpsimd[index].lo = value++; + context->fpsimd[index].hi = value++; + } + context->fpsr = value++; + context->fpcr = value++; +} + namespace { // Using gtest assertions, compares |expected| to |observed|. This is @@ -350,5 +405,53 @@ void ExpectMinidumpContextAMD64( } } +void ExpectMinidumpContextARM(uint32_t expect_seed, + const MinidumpContextARM* observed, + bool snapshot) { + MinidumpContextARM expected; + InitializeMinidumpContextARM(&expected, expect_seed); + + EXPECT_EQ(observed->context_flags, expected.context_flags); + + for (size_t index = 0; index < arraysize(expected.regs); ++index) { + EXPECT_EQ(observed->regs[index], expected.regs[index]); + } + EXPECT_EQ(observed->fp, expected.fp); + EXPECT_EQ(observed->ip, expected.ip); + EXPECT_EQ(observed->sp, expected.sp); + EXPECT_EQ(observed->lr, expected.lr); + EXPECT_EQ(observed->pc, expected.pc); + EXPECT_EQ(observed->cpsr, expected.cpsr); + + EXPECT_EQ(observed->fpscr, expected.fpscr); + for (size_t index = 0; index < arraysize(expected.vfp); ++index) { + EXPECT_EQ(observed->vfp[index], expected.vfp[index]); + } + for (size_t index = 0; index < arraysize(expected.extra); ++index) { + EXPECT_EQ(observed->extra[index], snapshot ? 0 : expected.extra[index]); + } +} + +void ExpectMinidumpContextARM64(uint32_t expect_seed, + const MinidumpContextARM64* observed, + bool snapshot) { + MinidumpContextARM64 expected; + InitializeMinidumpContextARM64(&expected, expect_seed); + + EXPECT_EQ(observed->context_flags, expected.context_flags); + + for (size_t index = 0; index < arraysize(expected.regs); ++index) { + EXPECT_EQ(observed->regs[index], expected.regs[index]); + } + EXPECT_EQ(observed->cpsr, expected.cpsr); + + EXPECT_EQ(observed->fpsr, expected.fpsr); + EXPECT_EQ(observed->fpcr, expected.fpcr); + for (size_t index = 0; index < arraysize(expected.fpsimd); ++index) { + EXPECT_EQ(observed->fpsimd[index].lo, expected.fpsimd[index].lo); + EXPECT_EQ(observed->fpsimd[index].hi, expected.fpsimd[index].hi); + } +} + } // namespace test } // namespace crashpad diff --git a/minidump/test/minidump_context_test_util.h b/minidump/test/minidump_context_test_util.h index 14f57430..64f79cde 100644 --- a/minidump/test/minidump_context_test_util.h +++ b/minidump/test/minidump_context_test_util.h @@ -41,6 +41,9 @@ namespace test { void InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed); void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context, uint32_t seed); +void InitializeMinidumpContextARM(MinidumpContextARM* context, uint32_t seed); +void InitializeMinidumpContextARM64(MinidumpContextARM64* context, + uint32_t seed); //! \} //! \brief Verifies, via gtest assertions, that a context structure contains @@ -67,6 +70,12 @@ void ExpectMinidumpContextX86( uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot); void ExpectMinidumpContextAMD64( uint32_t expect_seed, const MinidumpContextAMD64* observed, bool snapshot); +void ExpectMinidumpContextARM(uint32_t expect_seed, + const MinidumpContextARM* observed, + bool snapshot); +void ExpectMinidumpContextARM64(uint32_t expect_seed, + const MinidumpContextARM64* observed, + bool snapshot); //! \} } // namespace test diff --git a/snapshot/test/test_cpu_context.cc b/snapshot/test/test_cpu_context.cc index a7506b87..09a5e799 100644 --- a/snapshot/test/test_cpu_context.cc +++ b/snapshot/test/test_cpu_context.cc @@ -22,6 +22,68 @@ namespace crashpad { namespace test { +namespace { + +// This is templatized because the CPUContextX86::Fxsave and +// CPUContextX86_64::Fxsave are nearly identical but have different sizes for +// the members |xmm|, |reserved_4|, and |available|. +template +void InitializeCPUContextFxsave(FxsaveType* fxsave, uint32_t* seed) { + uint32_t value = *seed; + + fxsave->fcw = static_cast(value++); + fxsave->fsw = static_cast(value++); + fxsave->ftw = static_cast(value++); + fxsave->reserved_1 = static_cast(value++); + fxsave->fop = static_cast(value++); + fxsave->fpu_ip = value++; + fxsave->fpu_cs = static_cast(value++); + fxsave->reserved_2 = static_cast(value++); + fxsave->fpu_dp = value++; + fxsave->fpu_ds = static_cast(value++); + fxsave->reserved_3 = static_cast(value++); + fxsave->mxcsr = value++; + fxsave->mxcsr_mask = value++; + for (size_t st_mm_index = 0; st_mm_index < arraysize(fxsave->st_mm); + ++st_mm_index) { + for (size_t byte = 0; byte < arraysize(fxsave->st_mm[st_mm_index].st); + ++byte) { + fxsave->st_mm[st_mm_index].st[byte] = static_cast(value++); + } + for (size_t byte = 0; + byte < arraysize(fxsave->st_mm[st_mm_index].st_reserved); + ++byte) { + fxsave->st_mm[st_mm_index].st_reserved[byte] = + static_cast(value); + } + } + for (size_t xmm_index = 0; xmm_index < arraysize(fxsave->xmm); ++xmm_index) { + for (size_t byte = 0; byte < arraysize(fxsave->xmm[xmm_index]); ++byte) { + fxsave->xmm[xmm_index][byte] = static_cast(value++); + } + } + for (size_t byte = 0; byte < arraysize(fxsave->reserved_4); ++byte) { + fxsave->reserved_4[byte] = static_cast(value++); + } + for (size_t byte = 0; byte < arraysize(fxsave->available); ++byte) { + fxsave->available[byte] = static_cast(value++); + } + + *seed = value; +} + +} // namespace + +void InitializeCPUContextX86Fxsave(CPUContextX86::Fxsave* fxsave, + uint32_t* seed) { + return InitializeCPUContextFxsave(fxsave, seed); +} + +void InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave, + uint32_t* seed) { + return InitializeCPUContextFxsave(fxsave, seed); +} + void InitializeCPUContextX86(CPUContext* context, uint32_t seed) { context->architecture = kCPUArchitectureX86; @@ -101,68 +163,61 @@ void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed) { context->x86_64->dr7 = value++; } -namespace { +void InitializeCPUContextARM(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureARM; + CPUContextARM* arm = context->arm; -// This is templatized because the CPUContextX86::Fxsave and -// CPUContextX86_64::Fxsave are nearly identical but have different sizes for -// the members |xmm|, |reserved_4|, and |available|. -template -void InitializeCPUContextFxsave(FxsaveType* fxsave, uint32_t* seed) { - uint32_t value = *seed; - - fxsave->fcw = static_cast(value++); - fxsave->fsw = static_cast(value++); - fxsave->ftw = static_cast(value++); - fxsave->reserved_1 = static_cast(value++); - fxsave->fop = static_cast(value++); - fxsave->fpu_ip = value++; - fxsave->fpu_cs = static_cast(value++); - fxsave->reserved_2 = static_cast(value++); - fxsave->fpu_dp = value++; - fxsave->fpu_ds = static_cast(value++); - fxsave->reserved_3 = static_cast(value++); - fxsave->mxcsr = value++; - fxsave->mxcsr_mask = value++; - for (size_t st_mm_index = 0; - st_mm_index < arraysize(fxsave->st_mm); - ++st_mm_index) { - for (size_t byte = 0; - byte < arraysize(fxsave->st_mm[st_mm_index].st); - ++byte) { - fxsave->st_mm[st_mm_index].st[byte] = static_cast(value++); - } - for (size_t byte = 0; - byte < arraysize(fxsave->st_mm[st_mm_index].st_reserved); - ++byte) { - fxsave->st_mm[st_mm_index].st_reserved[byte] = - static_cast(value); - } - } - for (size_t xmm_index = 0; xmm_index < arraysize(fxsave->xmm); ++xmm_index) { - for (size_t byte = 0; byte < arraysize(fxsave->xmm[xmm_index]); ++byte) { - fxsave->xmm[xmm_index][byte] = static_cast(value++); - } - } - for (size_t byte = 0; byte < arraysize(fxsave->reserved_4); ++byte) { - fxsave->reserved_4[byte] = static_cast(value++); - } - for (size_t byte = 0; byte < arraysize(fxsave->available); ++byte) { - fxsave->available[byte] = static_cast(value++); + if (seed == 0) { + memset(arm, 0, sizeof(*arm)); + return; } - *seed = value; + uint32_t value = seed; + + for (size_t index = 0; index < arraysize(arm->regs); ++index) { + arm->regs[index] = value++; + } + arm->fp = value++; + arm->ip = value++; + arm->ip = value++; + arm->sp = value++; + arm->lr = value++; + arm->pc = value++; + arm->cpsr = value++; + + for (size_t index = 0; index < arraysize(arm->vfp_regs.vfp); ++index) { + arm->vfp_regs.vfp[index] = value++; + } + arm->vfp_regs.fpscr = value++; + + arm->have_fpa_regs = false; + arm->have_vfp_regs = true; } -} // namespace +void InitializeCPUContextARM64(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureARM64; + CPUContextARM64* arm64 = context->arm64; -void InitializeCPUContextX86Fxsave(CPUContextX86::Fxsave* fxsave, - uint32_t* seed) { - return InitializeCPUContextFxsave(fxsave, seed); -} + if (seed == 0) { + memset(arm64, 0, sizeof(*arm64)); + return; + } -void InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave, - uint32_t* seed) { - return InitializeCPUContextFxsave(fxsave, seed); + uint32_t value = seed; + + for (size_t index = 0; index < arraysize(arm64->regs); ++index) { + arm64->regs[index] = value++; + } + arm64->sp = value++; + arm64->pc = value++; + arm64->pstate = value++; + + for (size_t index = 0; index < arraysize(arm64->fpsimd); ++index) { + arm64->fpsimd[index].lo = value++; + arm64->fpsimd[index].hi = value++; + } + arm64->fpsr = value++; + arm64->fpcr = value++; } } // namespace test diff --git a/snapshot/test/test_cpu_context.h b/snapshot/test/test_cpu_context.h index 0de2497a..0b4b5376 100644 --- a/snapshot/test/test_cpu_context.h +++ b/snapshot/test/test_cpu_context.h @@ -22,6 +22,25 @@ namespace crashpad { namespace test { +//! \brief Initializes an `fxsave` context substructure for testing. +//! +//! \param[out] fxsave The structure to initialize. +//! \param[in,out] seed The seed value. Initializing two `fxsave` structures of +//! the same type with identical seed values should produce identical +//! structures. Initialization with a different seed value should produce +//! a different `fxsave` structure. If \a seed is `0`, \a fxsave is zeroed +//! out entirely. If \a seed is nonzero, \a fxsave will be populated +//! entirely with nonzero values. \a seed will be updated by this function +//! to allow the caller to perform subsequent initialization of the context +//! structure containing \a fxsave. +//! +//! \{ +void InitializeCPUContextX86Fxsave(CPUContextX86::Fxsave* fxsave, + uint32_t* seed); +void InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave, + uint32_t* seed); +//! \} + //! \brief Initializes a context structure for testing. //! //! Initialization is compatible with the initialization used by minidump @@ -40,25 +59,8 @@ namespace test { //! \{ void InitializeCPUContextX86(CPUContext* context, uint32_t seed); void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed); -//! \} - -//! \brief Initializes an `fxsave` context substructure for testing. -//! -//! \param[out] fxsave The structure to initialize. -//! \param[in,out] seed The seed value. Initializing two `fxsave` structures of -//! the same type with identical seed values should produce identical -//! structures. Initialization with a different seed value should produce -//! a different `fxsave` structure. If \a seed is `0`, \a fxsave is zeroed -//! out entirely. If \a seed is nonzero, \a fxsave will be populated -//! entirely with nonzero values. \a seed will be updated by this function -//! to allow the caller to perform subsequent initialization of the context -//! structure containing \a fxsave. -//! -//! \{ -void InitializeCPUContextX86Fxsave( - CPUContextX86::Fxsave* fxsave, uint32_t* seed); -void InitializeCPUContextX86_64Fxsave( - CPUContextX86_64::Fxsave* fxsave, uint32_t* seed); +void InitializeCPUContextARM(CPUContext* context, uint32_t seed); +void InitializeCPUContextARM64(CPUContext* context, uint32_t seed); //! \} } // namespace test From 441789be5b83ce7d6c26151436673fd0b01ca6a9 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 30 Jan 2018 12:20:03 -0800 Subject: [PATCH 130/326] android: Implement Semaphore with a condition variable and mutex Bionic uses negative values of a semaphore to represent contention. `sem_timedwait` fails to restore the value to 0 on timeout resulting in an error (EBUSY) upon calling `sem_destroy`. Bug: crashpad:30 Change-Id: If1c73a54a879ebd003b0792ebb8f68ceb83ac8bb Reviewed-on: https://chromium-review.googlesource.com/894106 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- util/synchronization/semaphore.h | 7 +++++ util/synchronization/semaphore_posix.cc | 42 +++++++++++++++++++++++-- util/synchronization/semaphore_test.cc | 5 ++- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/util/synchronization/semaphore.h b/util/synchronization/semaphore.h index 49479639..dc3000b0 100644 --- a/util/synchronization/semaphore.h +++ b/util/synchronization/semaphore.h @@ -23,6 +23,9 @@ #include #elif defined(OS_WIN) #include +#elif defined(OS_ANDROID) +#include +#include #else #include #endif @@ -77,6 +80,10 @@ class Semaphore { dispatch_semaphore_t semaphore_; #elif defined(OS_WIN) HANDLE semaphore_; +#elif defined(OS_ANDROID) + std::condition_variable cv_; + std::mutex mutex_; + int value_; #else sem_t semaphore_; #endif diff --git a/util/synchronization/semaphore_posix.cc b/util/synchronization/semaphore_posix.cc index 59ed71f2..3875f3f4 100644 --- a/util/synchronization/semaphore_posix.cc +++ b/util/synchronization/semaphore_posix.cc @@ -18,13 +18,51 @@ #include #include +#include + #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "util/misc/time.h" namespace crashpad { -#if !defined(OS_MACOSX) +#if defined(OS_ANDROID) + +Semaphore::Semaphore(int value) : cv_(), mutex_(), value_(value) {} + +Semaphore::~Semaphore() = default; + +void Semaphore::Wait() { + std::unique_lock lock(mutex_); + cv_.wait(lock, [this] { return this->value_ > 0; }); + --value_; +} + +bool Semaphore::TimedWait(double seconds) { + DCHECK_GE(seconds, 0.0); + + if (isinf(seconds)) { + Wait(); + return true; + } + + std::unique_lock lock(mutex_); + if (!cv_.wait_for(lock, std::chrono::duration(seconds), [this] { + return this->value_ > 0; + })) { + return false; + } + --value_; + return true; +} + +void Semaphore::Signal() { + std::lock_guard lock(mutex_); + ++value_; + cv_.notify_one(); +} + +#elif !defined(OS_MACOSX) Semaphore::Semaphore(int value) { PCHECK(sem_init(&semaphore_, 0, value) == 0) << "sem_init"; @@ -65,6 +103,6 @@ void Semaphore::Signal() { PCHECK(sem_post(&semaphore_) == 0) << "sem_post"; } -#endif +#endif // OS_ANDROID } // namespace crashpad diff --git a/util/synchronization/semaphore_test.cc b/util/synchronization/semaphore_test.cc index e1f3648c..10f7546d 100644 --- a/util/synchronization/semaphore_test.cc +++ b/util/synchronization/semaphore_test.cc @@ -41,7 +41,10 @@ TEST(Semaphore, TimedWait) { TEST(Semaphore, TimedWaitTimeout) { Semaphore semaphore(0); - EXPECT_FALSE(semaphore.TimedWait(0.01)); // 10ms + semaphore.Signal(); + constexpr double kTenMs = 0.01; + EXPECT_TRUE(semaphore.TimedWait(kTenMs)); + EXPECT_FALSE(semaphore.TimedWait(kTenMs)); } TEST(Semaphore, TimedWaitInfinite_0) { From f9d7b8b781d173e3199c60ec0cdce690f703adb7 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 30 Jan 2018 13:52:50 -0800 Subject: [PATCH 131/326] Rework ReadUnmapped(Self|Forked) to not need fork() Avoids fork()ing as per previous tests in this file, necessary for Fuchsia. Unfortunately, I believe that mmap()/munmap() aren't actually working correctly on Fuchsia as tested by the EXPECT_FALSE reads, and so these tests incorrectly fail. Bug with repro filed upstream at ZX-1631. Bug: crashpad:196, crashpad:215 Change-Id: Iec86f64fcee12097223326f2bf2d5a5348a8a610 Reviewed-on: https://chromium-review.googlesource.com/894124 Reviewed-by: Joshua Peraza Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- util/process/process_memory_test.cc | 162 +++++++++++++++++----------- 1 file changed, 101 insertions(+), 61 deletions(-) diff --git a/util/process/process_memory_test.cc b/util/process/process_memory_test.cc index 28d62d02..1d48bf4c 100644 --- a/util/process/process_memory_test.cc +++ b/util/process/process_memory_test.cc @@ -322,6 +322,107 @@ TEST(ProcessMemory, ReadCStringSizeLimitedChild) { test.RunAgainstChild(); } +void DoReadUnmappedChildMainSetup(ScopedMmap* pages, + VMAddress* address, + size_t* page_size, + size_t* region_size) { + *page_size = getpagesize(); + *region_size = 2 * (*page_size); + if (!pages->ResetMmap(nullptr, + *region_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0)) { + ADD_FAILURE(); + return; + } + + *address = pages->addr_as(); + + char* region = pages->addr_as(); + for (size_t index = 0; index < *region_size; ++index) { + region[index] = index % 256; + } + + EXPECT_TRUE(pages->ResetAddrLen(region, *page_size)); +} + +CRASHPAD_CHILD_TEST_MAIN(ReadUnmappedChildMain) { + ScopedMmap pages; + VMAddress address; + size_t page_size, region_size; + DoReadUnmappedChildMainSetup(&pages, &address, &page_size, ®ion_size); + FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); + CheckedWriteFile(out, &address, sizeof(address)); + CheckedWriteFile(out, &page_size, sizeof(page_size)); + CheckedWriteFile(out, ®ion_size, sizeof(region_size)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadUnmappedTest : public MultiprocessExec { + public: + ReadUnmappedTest() : MultiprocessExec() { + SetChildTestMainFunction("ReadUnmappedChildMain"); + } + + void RunAgainstSelf() { + ScopedMmap pages; + VMAddress address; + size_t page_size, region_size; + DoReadUnmappedChildMainSetup(&pages, &address, &page_size, ®ion_size); + DoTest(GetSelfProcess(), address, page_size, region_size); + } + + void RunAgainstChild() { Run(); } + + private: + void MultiprocessParent() override { + VMAddress address; + size_t page_size, region_size; + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &address, sizeof(address))); + ASSERT_TRUE( + ReadFileExactly(ReadPipeHandle(), &page_size, sizeof(page_size))); + ASSERT_TRUE( + ReadFileExactly(ReadPipeHandle(), ®ion_size, sizeof(region_size))); + DoTest(ChildProcess(), address, page_size, region_size); + } + + void DoTest(ProcessType process, + VMAddress address, + size_t page_size, + size_t region_size) { + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); + + VMAddress page_addr1 = address; + VMAddress page_addr2 = page_addr1 + page_size; + + std::unique_ptr result(new char[region_size]); + EXPECT_TRUE(memory.Read(page_addr1, page_size, result.get())); + EXPECT_TRUE(memory.Read(page_addr2 - 1, 1, result.get())); + + EXPECT_FALSE(memory.Read(page_addr1, region_size, result.get())); + EXPECT_FALSE(memory.Read(page_addr2, page_size, result.get())); + EXPECT_FALSE(memory.Read(page_addr2 - 1, 2, result.get())); + } + + DISALLOW_COPY_AND_ASSIGN(ReadUnmappedTest); +}; + +TEST(ProcessMemory, ReadUnmappedSelf) { + ReadUnmappedTest test; + ASSERT_FALSE(testing::Test::HasFailure()); + test.RunAgainstSelf(); +} + +TEST(ProcessMemory, ReadUnmappedChild) { + ReadUnmappedTest test; + ASSERT_FALSE(testing::Test::HasFailure()); + test.RunAgainstChild(); +} + // TODO(scottmg): Need to be ported to MultiprocessExec and not rely on fork(). #if !defined(OS_FUCHSIA) @@ -358,67 +459,6 @@ bool ReadCStringSizeLimited(const ProcessMemory& memory, FromPointerCast(pointer), size, result); } -class ReadUnmappedTest : public TargetProcessTest { - public: - ReadUnmappedTest() - : TargetProcessTest(), - page_size_(getpagesize()), - region_size_(2 * page_size_), - result_(new char[region_size_]) { - if (!pages_.ResetMmap(nullptr, - region_size_, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, - -1, - 0)) { - ADD_FAILURE(); - return; - } - - char* region = pages_.addr_as(); - for (size_t index = 0; index < region_size_; ++index) { - region[index] = index % 256; - } - - EXPECT_TRUE(pages_.ResetAddrLen(region, page_size_)); - } - - private: - void DoTest(pid_t pid) override { - ProcessMemoryNative memory; - ASSERT_TRUE(memory.Initialize(pid)); - - VMAddress page_addr1 = pages_.addr_as(); - VMAddress page_addr2 = page_addr1 + page_size_; - - EXPECT_TRUE(memory.Read(page_addr1, page_size_, result_.get())); - EXPECT_TRUE(memory.Read(page_addr2 - 1, 1, result_.get())); - - EXPECT_FALSE(memory.Read(page_addr1, region_size_, result_.get())); - EXPECT_FALSE(memory.Read(page_addr2, page_size_, result_.get())); - EXPECT_FALSE(memory.Read(page_addr2 - 1, 2, result_.get())); - } - - ScopedMmap pages_; - const size_t page_size_; - const size_t region_size_; - std::unique_ptr result_; - - DISALLOW_COPY_AND_ASSIGN(ReadUnmappedTest); -}; - -TEST(ProcessMemory, ReadUnmappedSelf) { - ReadUnmappedTest test; - ASSERT_FALSE(testing::Test::HasFailure()); - test.RunAgainstSelf(); -} - -TEST(ProcessMemory, ReadUnmappedForked) { - ReadUnmappedTest test; - ASSERT_FALSE(testing::Test::HasFailure()); - test.RunAgainstForked(); -} - class ReadCStringUnmappedTest : public TargetProcessTest { public: ReadCStringUnmappedTest(bool limit_size) From 1f1657d573c789aa36b6022440e34d9ec30d894c Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 30 Jan 2018 14:30:50 -0800 Subject: [PATCH 132/326] Read either DT_HASH or DT_GNU_HASH to determine the size of DT_SYMTAB Without the section headers for the symbol table, there's no direct way to calculate the number of entries in the table. DT_HASH and DT_GNU_HASH are auxiliary tables that are designed to make symbol lookup faster. DT_HASH is the original and is theoretically mandatory. DT_GNU_HASH is the new-and-improved, but is more complex. In practice, however, an Android build (at least vs. API 16) has only DT_HASH, and not DT_GNU_HASH, and a Fuchsia build has only DT_GNU_HASH but not DT_HASH. So, both are tried. This change does not actually use the data in these tables to improve the speed of symbol lookup, but instead only uses them to correctly terminate the linear search. DT_HASH contains the total number of symbols in the symbol table fairly directly because there is an entry for each symbol table entry in the hash table, so the number is the same. DT_GNU_HASH regrettably does not. Instead, it's necessary to walk the buckets and chain structure to find the largest entry. DT_GNU_HASH doesn't appear in any "real" documentation that I'm aware of, other than the binutils code (at least as far as I know). Some more-and-less-useful references: - https://flapenguin.me/2017/04/24/elf-lookup-dt-hash/ - https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/ - http://deroko.phearless.org/dt_gnu_hash.txt - https://sourceware.org/ml/binutils/2006-10/msg00377.html Change-Id: I7cfc4372f29efc37446f0931d22a1f790e44076f Bug: crashpad:213, crashpad:196 Reviewed-on: https://chromium-review.googlesource.com/876879 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai Reviewed-by: Joshua Peraza --- build/run_tests.py | 19 ++-- snapshot/BUILD.gn | 13 +++ snapshot/elf/elf_dynamic_array_reader.h | 6 +- snapshot/elf/elf_image_reader.cc | 123 ++++++++++++++++++++++-- snapshot/elf/elf_image_reader.h | 29 +++++- snapshot/elf/elf_image_reader_test.cc | 61 +++++++++++- snapshot/elf/elf_symbol_table_reader.cc | 17 +++- snapshot/elf/elf_symbol_table_reader.h | 4 +- snapshot/hash_types_test.cc | 17 ++++ snapshot/snapshot_test.gyp | 13 +++ 10 files changed, 279 insertions(+), 23 deletions(-) create mode 100644 snapshot/hash_types_test.cc diff --git a/build/run_tests.py b/build/run_tests.py index 43e49f2c..6c2dacf4 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -379,15 +379,22 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line): def netcp(local_path): """Uses `netcp` to copy a file or directory to the device. Files located - inside the build dir are stored to /pkg/bin, or /pkg/lib (if .so), - otherwise to /pkg/assets. + inside the build dir are stored to /pkg/bin, otherwise to /pkg/assets. + .so files are stored somewhere completely different, into /boot/lib (!). + This is because the loader service does not yet correctly handle the + namespace in which the caller is being run, and so can only load .so files + from a couple hardcoded locations, the only writable one of which is + /boot/lib, so we copy all .so files there. This bug is filed upstream as + ZX-1619. """ in_binary_dir = local_path.startswith(binary_dir + '/') if in_binary_dir: - is_so = local_path.endswith('.so') - target_path = os.path.join(staging_root, - 'lib' if is_so else 'bin', - local_path[len(binary_dir)+1:]) + if local_path.endswith('.so'): + target_path = os.path.join( + '/boot/lib', local_path[len(binary_dir)+1:]) + else: + target_path = os.path.join( + staging_root, 'bin', local_path[len(binary_dir)+1:]) else: target_path = os.path.join(staging_root, 'assets', local_path) netcp_path = os.path.join(sdk_root, 'tools', 'netcp') diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 9a27b1ee..6b3fe65d 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -352,6 +352,7 @@ source_set("snapshot_test") { ":crashpad_snapshot_test_module", ":crashpad_snapshot_test_module_large", ":crashpad_snapshot_test_module_small", + ":crashpad_snapshot_test_both_dt_hash_styles", ] if (crashpad_is_mac) { @@ -414,6 +415,18 @@ loadable_module("crashpad_snapshot_test_module_small") { ] } +if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + loadable_module("crashpad_snapshot_test_both_dt_hash_styles") { + testonly = true + sources = [ + "hash_types_test.cc", + ] + + # This makes `ld` emit both .hash and .gnu.hash sections. + ldflags = [ "-Wl,--hash-style=both" ] + } +} + if (crashpad_is_mac) { loadable_module("crashpad_snapshot_test_module_crashy_initializer") { testonly = true diff --git a/snapshot/elf/elf_dynamic_array_reader.h b/snapshot/elf/elf_dynamic_array_reader.h index 36628a0b..e80a8cbc 100644 --- a/snapshot/elf/elf_dynamic_array_reader.h +++ b/snapshot/elf/elf_dynamic_array_reader.h @@ -50,13 +50,15 @@ class ElfDynamicArrayReader { //! //! \param[in] tag Specifies which value should be retrieved. The possible //! values for this parameter are the `DT_*` values from ``. + //! \param[in] log Specifies whether an error should be logged if \a tag is + //! not found. //! \param[out] value The value, casted to an appropriate type, if found. //! \return `true` if the value is found. template - bool GetValue(uint64_t tag, V* value) { + bool GetValue(uint64_t tag, bool log, V* value) { auto iter = values_.find(tag); if (iter == values_.end()) { - LOG(ERROR) << "tag not found"; + LOG_IF(ERROR, log) << "tag not found"; return false; } return ReinterpretBytes(iter->second, value); diff --git a/snapshot/elf/elf_image_reader.cc b/snapshot/elf/elf_image_reader.cc index 7b3389c5..6560bdd8 100644 --- a/snapshot/elf/elf_image_reader.cc +++ b/snapshot/elf/elf_image_reader.cc @@ -519,8 +519,8 @@ bool ElfImageReader::ReadDynamicStringTableAtOffset(VMSize offset, VMAddress string_table_address; VMSize string_table_size; - if (!GetAddressFromDynamicArray(DT_STRTAB, &string_table_address) || - !dynamic_array_->GetValue(DT_STRSZ, &string_table_size)) { + if (!GetAddressFromDynamicArray(DT_STRTAB, true, &string_table_address) || + !dynamic_array_->GetValue(DT_STRSZ, true, &string_table_size)) { LOG(ERROR) << "missing string table info"; return false; } @@ -542,7 +542,7 @@ bool ElfImageReader::GetDebugAddress(VMAddress* debug) { if (!InitializeDynamicArray()) { return false; } - return GetAddressFromDynamicArray(DT_DEBUG, debug); + return GetAddressFromDynamicArray(DT_DEBUG, true, debug); } bool ElfImageReader::InitializeProgramHeaders() { @@ -609,20 +609,35 @@ bool ElfImageReader::InitializeDynamicSymbolTable() { } VMAddress symbol_table_address; - if (!GetAddressFromDynamicArray(DT_SYMTAB, &symbol_table_address)) { + if (!GetAddressFromDynamicArray(DT_SYMTAB, true, &symbol_table_address)) { LOG(ERROR) << "no symbol table"; return false; } - symbol_table_.reset( - new ElfSymbolTableReader(&memory_, this, symbol_table_address)); + // Try both DT_HASH and DT_GNU_HASH. They're completely different, but both + // circuitously offer a way to find the number of entries in the symbol table. + // DT_HASH is specifically checked first, because depending on the linker, the + // count maybe be incorrect for zero-export cases. In practice, it is believed + // that the zero-export case is probably not particularly useful, so this + // incorrect count will only occur in constructed test cases (see + // ElfImageReader.DtHashAndDtGnuHashMatch). + VMSize number_of_symbol_table_entries; + if (!GetNumberOfSymbolEntriesFromDtHash(&number_of_symbol_table_entries) && + !GetNumberOfSymbolEntriesFromDtGnuHash(&number_of_symbol_table_entries)) { + LOG(ERROR) << "could not retrieve number of symbol table entries"; + return false; + } + + symbol_table_.reset(new ElfSymbolTableReader( + &memory_, this, symbol_table_address, number_of_symbol_table_entries)); symbol_table_initialized_.set_valid(); return true; } bool ElfImageReader::GetAddressFromDynamicArray(uint64_t tag, + bool log, VMAddress* address) { - if (!dynamic_array_->GetValue(tag, address)) { + if (!dynamic_array_->GetValue(tag, log, address)) { return false; } #if defined(OS_ANDROID) || defined(OS_FUCHSIA) @@ -635,6 +650,100 @@ bool ElfImageReader::GetAddressFromDynamicArray(uint64_t tag, return true; } +bool ElfImageReader::GetNumberOfSymbolEntriesFromDtHash( + VMSize* number_of_symbol_table_entries) { + if (!InitializeDynamicArray()) { + return false; + } + + VMAddress dt_hash_address; + if (!GetAddressFromDynamicArray(DT_HASH, false, &dt_hash_address)) { + return false; + } + + struct { + uint32_t nbucket; + uint32_t nchain; + } header; + + if (!memory_.Read(dt_hash_address, sizeof(header), &header)) { + LOG(ERROR) << "failed to read DT_HASH header"; + return false; + } + + *number_of_symbol_table_entries = header.nchain; + return true; +} + +bool ElfImageReader::GetNumberOfSymbolEntriesFromDtGnuHash( + VMSize* number_of_symbol_table_entries) { + if (!InitializeDynamicArray()) { + return false; + } + + VMAddress dt_gnu_hash_address; + if (!GetAddressFromDynamicArray(DT_GNU_HASH, false, &dt_gnu_hash_address)) { + return false; + } + + // See https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/ and + // https://sourceware.org/ml/binutils/2006-10/msg00377.html. + struct { + uint32_t nbuckets; + uint32_t symoffset; + uint32_t bloom_size; + uint32_t bloom_shift; + } header; + if (!memory_.Read(dt_gnu_hash_address, sizeof(header), &header)) { + LOG(ERROR) << "failed to read DT_GNU_HASH header"; + return false; + } + + std::vector buckets(header.nbuckets); + const size_t kNumBytesForBuckets = sizeof(buckets[0]) * buckets.size(); + const size_t kWordSize = + memory_.Is64Bit() ? sizeof(uint64_t) : sizeof(uint32_t); + const VMAddress buckets_address = + dt_gnu_hash_address + sizeof(header) + (kWordSize * header.bloom_size); + if (!memory_.Read(buckets_address, kNumBytesForBuckets, buckets.data())) { + LOG(ERROR) << "read buckets"; + return false; + } + + // Locate the chain that handles the largest index bucket. + uint32_t last_symbol = 0; + for (uint32_t i = 0; i < header.nbuckets; ++i) { + last_symbol = std::max(buckets[i], last_symbol); + } + + if (last_symbol < header.symoffset) { + *number_of_symbol_table_entries = header.symoffset; + return true; + } + + // Walk the bucket's chain to add the chain length to the total. + const VMAddress chains_base_address = buckets_address + kNumBytesForBuckets; + for (;;) { + uint32_t chain_entry; + if (!memory_.Read(chains_base_address + (last_symbol - header.symoffset) * + sizeof(chain_entry), + sizeof(chain_entry), + &chain_entry)) { + LOG(ERROR) << "read chain entry"; + return false; + } + + ++last_symbol; + + // If the low bit is set, this entry is the end of the chain. + if (chain_entry & 1) + break; + } + + *number_of_symbol_table_entries = last_symbol; + return true; +} + std::unique_ptr ElfImageReader::Notes( ssize_t max_note_size) { return std::make_unique( diff --git a/snapshot/elf/elf_image_reader.h b/snapshot/elf/elf_image_reader.h index 67cbc1fa..7549f346 100644 --- a/snapshot/elf/elf_image_reader.h +++ b/snapshot/elf/elf_image_reader.h @@ -207,6 +207,33 @@ class ElfImageReader { //! The caller does not take ownership of the returned object. const ProcessMemoryRange* Memory() const; + //! \brief Retrieves the number of symbol table entries in `DT_SYMTAB` + //! according to the data in the `DT_HASH` section. + //! + //! \note Exposed for testing, not normally otherwise useful. + //! + //! \param[out] number_of_symbol_table_entries The number of entries expected + //! in `DT_SYMTAB`. + //! \return `true` if a `DT_HASH` section was found, and was read + //! successfully, otherwise `false` with an error logged. + bool GetNumberOfSymbolEntriesFromDtHash( + VMSize* number_of_symbol_table_entries); + + //! \brief Retrieves the number of symbol table entries in `DT_SYMTAB` + //! according to the data in the `DT_GNU_HASH` section. + //! + //! \note Exposed for testing, not normally otherwise useful. + //! + //! \note Depending on the linker that generated the `DT_GNU_HASH` section, + //! this value may not be as expected if there are zero exported symbols. + //! + //! \param[out] number_of_symbol_table_entries The number of entries expected + //! in `DT_SYMTAB`. + //! \return `true` if a `DT_GNU_HASH` section was found, and was read + //! successfully, otherwise `false` with an error logged. + bool GetNumberOfSymbolEntriesFromDtGnuHash( + VMSize* number_of_symbol_table_entries); + private: template class ProgramHeaderTableSpecific; @@ -214,7 +241,7 @@ class ElfImageReader { bool InitializeProgramHeaders(); bool InitializeDynamicArray(); bool InitializeDynamicSymbolTable(); - bool GetAddressFromDynamicArray(uint64_t tag, VMAddress* address); + bool GetAddressFromDynamicArray(uint64_t tag, bool log, VMAddress* address); union { Elf32_Ehdr header_32_; diff --git a/snapshot/elf/elf_image_reader_test.cc b/snapshot/elf/elf_image_reader_test.cc index 32b8cfd8..dc13ad6f 100644 --- a/snapshot/elf/elf_image_reader_test.cc +++ b/snapshot/elf/elf_image_reader_test.cc @@ -15,6 +15,7 @@ #include "snapshot/elf/elf_image_reader.h" #include +#include #include #include "base/logging.h" @@ -22,6 +23,8 @@ #include "gtest/gtest.h" #include "test/multiprocess_exec.h" #include "test/process_type.h" +#include "test/scoped_module_handle.h" +#include "test/test_paths.h" #include "util/file/file_io.h" #include "util/misc/address_types.h" #include "util/misc/from_pointer_cast.h" @@ -29,7 +32,6 @@ #if defined(OS_FUCHSIA) -#include #include #include "base/fuchsia/fuchsia_logging.h" @@ -288,6 +290,63 @@ TEST(ElfImageReader, OneModuleChild) { test.Run(); } +#if defined(OS_FUCHSIA) + +// crashpad_snapshot_test_both_dt_hash_styles is specially built and forced to +// include both .hash and .gnu.hash sections. Linux, Android, and Fuchsia have +// different defaults for which of these sections should be included; this test +// confirms that we get the same count from both sections. +// +// TODO(scottmg): Investigation in https://crrev.com/c/876879 resulted in +// realizing that ld.bfd does not emit a .gnu.hash that is very useful for this +// purpose when there's 0 exported entries in the module. This is not likely to +// be too important, as there's little need to look up non-exported symbols. +// However, it makes this test not work on Linux, where the default build uses +// ld.bfd. On Fuchsia, the only linker in use is lld, and it generates the +// expected .gnu.hash. So, for now, this test is only run on Fuchsia, not Linux. +// +// TODO(scottmg): Separately, the location of the ELF on Android needs some +// work, and then the test could also be enabled there. +TEST(ElfImageReader, DtHashAndDtGnuHashMatch) { + base::FilePath module_path = + TestPaths::BuildArtifact(FILE_PATH_LITERAL("snapshot"), + FILE_PATH_LITERAL("both_dt_hash_styles"), + TestPaths::FileType::kLoadableModule); + // TODO(scottmg): Remove this when upstream Fuchsia bug ZX-1619 is resolved. + // See also explanation in build/run_tests.py for Fuchsia .so files. + module_path = module_path.BaseName(); + ScopedModuleHandle module( + dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL)); + ASSERT_TRUE(module.valid()) << "dlopen " << module_path.value() << ": " + << dlerror(); + +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif // ARCH_CPU_64_BITS + + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(GetSelfProcess())); + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, am_64_bit)); + + struct link_map* lm = reinterpret_cast(module.get()); + + ElfImageReader reader; + ASSERT_TRUE(reader.Initialize(range, lm->l_addr)); + + VMSize from_dt_hash; + ASSERT_TRUE(reader.GetNumberOfSymbolEntriesFromDtHash(&from_dt_hash)); + + VMSize from_dt_gnu_hash; + ASSERT_TRUE(reader.GetNumberOfSymbolEntriesFromDtGnuHash(&from_dt_gnu_hash)); + + EXPECT_EQ(from_dt_hash, from_dt_gnu_hash); +} + +#endif // OS_FUCHSIA + } // namespace } // namespace test } // namespace crashpad diff --git a/snapshot/elf/elf_symbol_table_reader.cc b/snapshot/elf/elf_symbol_table_reader.cc index c6395db7..c2c6abf0 100644 --- a/snapshot/elf/elf_symbol_table_reader.cc +++ b/snapshot/elf/elf_symbol_table_reader.cc @@ -51,8 +51,12 @@ uint8_t GetVisibility(const Elf64_Sym& sym) { ElfSymbolTableReader::ElfSymbolTableReader(const ProcessMemoryRange* memory, ElfImageReader* elf_reader, - VMAddress address) - : memory_(memory), elf_reader_(elf_reader), base_address_(address) {} + VMAddress address, + VMSize num_entries) + : memory_(memory), + elf_reader_(elf_reader), + base_address_(address), + num_entries_(num_entries) {} ElfSymbolTableReader::~ElfSymbolTableReader() {} @@ -68,9 +72,10 @@ bool ElfSymbolTableReader::ScanSymbolTable(const std::string& name, VMAddress address = base_address_; SymEnt entry; std::string string; - while (memory_->Read(address, sizeof(entry), &entry) && - elf_reader_->ReadDynamicStringTableAtOffset(entry.st_name, &string)) { - if (string == name) { + size_t i = 0; + while (i < num_entries_ && memory_->Read(address, sizeof(entry), &entry)) { + if (elf_reader_->ReadDynamicStringTableAtOffset(entry.st_name, &string) && + string == name) { info_out->address = entry.st_value; info_out->size = entry.st_size; info_out->shndx = entry.st_shndx; @@ -79,7 +84,9 @@ bool ElfSymbolTableReader::ScanSymbolTable(const std::string& name, info_out->visibility = GetVisibility(entry); return true; } + // TODO(scottmg): This should respect DT_SYMENT if present. address += sizeof(entry); + ++i; } return false; } diff --git a/snapshot/elf/elf_symbol_table_reader.h b/snapshot/elf/elf_symbol_table_reader.h index 880915a2..15930372 100644 --- a/snapshot/elf/elf_symbol_table_reader.h +++ b/snapshot/elf/elf_symbol_table_reader.h @@ -61,7 +61,8 @@ class ElfSymbolTableReader { // lookup. ElfSymbolTableReader(const ProcessMemoryRange* memory, ElfImageReader* elf_reader, - VMAddress address); + VMAddress address, + VMSize num_entries); ~ElfSymbolTableReader(); //! \brief Lookup information about a symbol. @@ -78,6 +79,7 @@ class ElfSymbolTableReader { const ProcessMemoryRange* const memory_; // weak ElfImageReader* const elf_reader_; // weak const VMAddress base_address_; + const VMSize num_entries_; DISALLOW_COPY_AND_ASSIGN(ElfSymbolTableReader); }; diff --git a/snapshot/hash_types_test.cc b/snapshot/hash_types_test.cc new file mode 100644 index 00000000..436323b2 --- /dev/null +++ b/snapshot/hash_types_test.cc @@ -0,0 +1,17 @@ +// Copyright 2018 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. + +int main() { + return 0; +} diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index ae4b7e9f..109895af 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -52,6 +52,7 @@ 'target_name': 'crashpad_snapshot_test', 'type': 'executable', 'dependencies': [ + 'crashpad_snapshot_test_both_dt_hash_styles', 'crashpad_snapshot_test_module', 'crashpad_snapshot_test_module_large', 'crashpad_snapshot_test_module_small', @@ -193,6 +194,18 @@ 'crashpad_info_size_test_module.cc', ], }, + { + 'target_name': 'crashpad_snapshot_test_both_dt_hash_styles', + 'type': 'executable', + 'sources': [ + 'hash_types_test.cc', + ], + + 'ldflags': [ + # This makes `ld` emit both .hash and .gnu.hash sections. + '-Wl,--hash-style=both', + ], + }, ], 'conditions': [ ['OS=="mac"', { From 3f0371cce2750e8837819b28f383151a22b75fc9 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 30 Jan 2018 17:37:00 -0800 Subject: [PATCH 133/326] Define ElfImageReader::Memory() Bug: crashpad:30 Change-Id: Ibb6c6423736daa9fb248ac0c8d724244906236d4 Reviewed-on: https://chromium-review.googlesource.com/894447 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- snapshot/elf/elf_image_reader.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/snapshot/elf/elf_image_reader.cc b/snapshot/elf/elf_image_reader.cc index 6560bdd8..2fc96089 100644 --- a/snapshot/elf/elf_image_reader.cc +++ b/snapshot/elf/elf_image_reader.cc @@ -758,4 +758,8 @@ ElfImageReader::NotesWithNameAndType(const std::string& name, this, &memory_, program_headers_.get(), max_note_size, name, type, true); } +const ProcessMemoryRange* ElfImageReader::Memory() const { + return &memory_; +} + } // namespace crashpad From 75ae5bae781d9b390ebee4612268a6fbb50b7c07 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 30 Jan 2018 15:24:36 -0800 Subject: [PATCH 134/326] Rework CrashpadInfoReader tests to not require fork() Avoid fork() so that the tests can work on Fuchsia. Fills out CrashpadInfo in the child, and then sends the addresses of various structures to the parent process to be used for expectation checking. Bug: crashpad:196, crashpad:215 Change-Id: I9ace6671d2e9184d48fe33016a01271ccfbcbfb6 Reviewed-on: https://chromium-review.googlesource.com/894705 Reviewed-by: Joshua Peraza Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- .../crashpad_info_reader_test.cc | 144 ++++++++++++------ 1 file changed, 100 insertions(+), 44 deletions(-) diff --git a/snapshot/crashpad_types/crashpad_info_reader_test.cc b/snapshot/crashpad_types/crashpad_info_reader_test.cc index 7b0629cf..73564177 100644 --- a/snapshot/crashpad_types/crashpad_info_reader_test.cc +++ b/snapshot/crashpad_types/crashpad_info_reader_test.cc @@ -23,7 +23,7 @@ #include "client/simple_address_range_bag.h" #include "client/simple_string_dictionary.h" #include "gtest/gtest.h" -#include "test/multiprocess.h" +#include "test/multiprocess_exec.h" #include "test/process_type.h" #include "util/file/file_io.h" #include "util/misc/from_pointer_cast.h" @@ -43,11 +43,11 @@ constexpr TriState kGatherIndirectlyReferencedMemory = TriState::kUnset; constexpr uint32_t kIndirectlyReferencedMemoryCap = 42; -class CrashpadInfoTest { +class CrashpadInfoTestDataSetup { public: - CrashpadInfoTest() - : extra_memory_(), simple_annotations_(), annotation_list_() { + CrashpadInfoTestDataSetup() { CrashpadInfo* info = CrashpadInfo::GetCrashpadInfo(); + info->set_extra_memory_ranges(&extra_memory_); info->set_simple_annotations(&simple_annotations_); info->set_annotations_list(&annotation_list_); @@ -57,30 +57,15 @@ class CrashpadInfoTest { kGatherIndirectlyReferencedMemory, kIndirectlyReferencedMemoryCap); } - void ExpectCrashpadInfo(ProcessType process, bool is_64_bit) { - ProcessMemoryNative memory; - ASSERT_TRUE(memory.Initialize(process)); - - ProcessMemoryRange range; - ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); - - CrashpadInfo* info = CrashpadInfo::GetCrashpadInfo(); - - CrashpadInfoReader reader; - ASSERT_TRUE(reader.Initialize(&range, FromPointerCast(info))); - EXPECT_EQ(reader.CrashpadHandlerBehavior(), kCrashpadHandlerBehavior); - EXPECT_EQ(reader.SystemCrashReporterForwarding(), - kSystemCrashReporterForwarding); - EXPECT_EQ(reader.GatherIndirectlyReferencedMemory(), - kGatherIndirectlyReferencedMemory); - EXPECT_EQ(reader.IndirectlyReferencedMemoryCap(), - kIndirectlyReferencedMemoryCap); - EXPECT_EQ(reader.ExtraMemoryRanges(), - FromPointerCast(&extra_memory_)); - EXPECT_EQ(reader.SimpleAnnotations(), - FromPointerCast(info->simple_annotations())); - EXPECT_EQ(reader.AnnotationsList(), - FromPointerCast(info->annotations_list())); + void GetAddresses(VMAddress* info_address, + VMAddress* extra_memory_address, + VMAddress* simple_annotations_address, + VMAddress* annotations_list_address) { + *info_address = FromPointerCast(CrashpadInfo::GetCrashpadInfo()); + *extra_memory_address = FromPointerCast(&extra_memory_); + *simple_annotations_address = + FromPointerCast(&simple_annotations_); + *annotations_list_address = FromPointerCast(&annotation_list_); } private: @@ -88,28 +73,86 @@ class CrashpadInfoTest { SimpleStringDictionary simple_annotations_; AnnotationList annotation_list_; - DISALLOW_COPY_AND_ASSIGN(CrashpadInfoTest); + DISALLOW_COPY_AND_ASSIGN(CrashpadInfoTestDataSetup); }; -TEST(CrashpadInfoReader, ReadFromSelf) { - CrashpadInfoTest test; +void ExpectCrashpadInfo(ProcessType process, + bool is_64_bit, + VMAddress info_address, + VMAddress extra_memory_address, + VMAddress simple_annotations_address, + VMAddress annotations_list_address) { + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); + + CrashpadInfoReader reader; + ASSERT_TRUE(reader.Initialize(&range, info_address)); + EXPECT_EQ(reader.CrashpadHandlerBehavior(), kCrashpadHandlerBehavior); + EXPECT_EQ(reader.SystemCrashReporterForwarding(), + kSystemCrashReporterForwarding); + EXPECT_EQ(reader.GatherIndirectlyReferencedMemory(), + kGatherIndirectlyReferencedMemory); + EXPECT_EQ(reader.IndirectlyReferencedMemoryCap(), + kIndirectlyReferencedMemoryCap); + EXPECT_EQ(reader.ExtraMemoryRanges(), extra_memory_address); + EXPECT_EQ(reader.SimpleAnnotations(), simple_annotations_address); + EXPECT_EQ(reader.AnnotationsList(), annotations_list_address); +} + +TEST(CrashpadInfoReader, ReadFromSelf) { #if defined(ARCH_CPU_64_BITS) constexpr bool am_64_bit = true; #else constexpr bool am_64_bit = false; #endif - test.ExpectCrashpadInfo(GetSelfProcess(), am_64_bit); + CrashpadInfoTestDataSetup test_data_setup; + VMAddress info_address; + VMAddress extra_memory_address; + VMAddress simple_annotations_address; + VMAddress annotations_list_address; + test_data_setup.GetAddresses(&info_address, + &extra_memory_address, + &simple_annotations_address, + &annotations_list_address); + ExpectCrashpadInfo(GetSelfProcess(), + am_64_bit, + info_address, + extra_memory_address, + simple_annotations_address, + annotations_list_address); } -// TODO(scottmg): This needs to be be ported to use MultiprocessExec instead of -// relying on fork() to have the same pointers in parent and child. -#if !defined(OS_FUCHSIA) +CRASHPAD_CHILD_TEST_MAIN(ReadFromChildTestMain) { + CrashpadInfoTestDataSetup test_data_setup; + VMAddress info_address; + VMAddress extra_memory_address; + VMAddress simple_annotations_address; + VMAddress annotations_list_address; + test_data_setup.GetAddresses(&info_address, + &extra_memory_address, + &simple_annotations_address, + &annotations_list_address); -class ReadFromChildTest : public Multiprocess { + FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); + CheckedWriteFile(out, &info_address, sizeof(info_address)); + CheckedWriteFile(out, &extra_memory_address, sizeof(extra_memory_address)); + CheckedWriteFile( + out, &simple_annotations_address, sizeof(simple_annotations_address)); + CheckedWriteFile( + out, &annotations_list_address, sizeof(annotations_list_address)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadFromChildTest : public MultiprocessExec { public: - ReadFromChildTest() : Multiprocess(), info_test_() {} + ReadFromChildTest() : MultiprocessExec() { + SetChildTestMainFunction("ReadFromChildTestMain"); + } ~ReadFromChildTest() = default; @@ -121,13 +164,28 @@ class ReadFromChildTest : public Multiprocess { constexpr bool am_64_bit = false; #endif - info_test_.ExpectCrashpadInfo(ChildPID(), am_64_bit); + VMAddress info_address; + VMAddress extra_memory_address; + VMAddress simple_annotations_address; + VMAddress annotations_list_address; + ASSERT_TRUE( + ReadFileExactly(ReadPipeHandle(), &info_address, sizeof(info_address))); + ASSERT_TRUE(ReadFileExactly( + ReadPipeHandle(), &extra_memory_address, sizeof(extra_memory_address))); + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), + &simple_annotations_address, + sizeof(simple_annotations_address))); + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), + &annotations_list_address, + sizeof(annotations_list_address))); + ExpectCrashpadInfo(ChildProcess(), + am_64_bit, + info_address, + extra_memory_address, + simple_annotations_address, + annotations_list_address); } - void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } - - CrashpadInfoTest info_test_; - DISALLOW_COPY_AND_ASSIGN(ReadFromChildTest); }; @@ -136,8 +194,6 @@ TEST(CrashpadInfoReader, ReadFromChild) { test.Run(); } -#endif // !defined(OS_FUCHSIA) - } // namespace } // namespace test } // namespace crashpad From 709a92981d0a17e7b79c712342b5bc0d44c504b6 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 31 Jan 2018 14:34:38 -0800 Subject: [PATCH 135/326] Rework ReadCStringUnmapped[SizeLimited](Self|Forked) to not use fork() Avoids using pointers shared between parent/child. Explicitly builds the test strings in the child process, and then passes both the address and the expected value of the string to the parent process for expectation checking. This is necessary to have the test work on Fuchsia. Also renames ...Forked to ...Child. Bug: crashpad:196, crashpad:215 Change-Id: I7f22c134301a2806eb39549e371414e7ec9bf225 Reviewed-on: https://chromium-review.googlesource.com/896228 Reviewed-by: Joshua Peraza Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- util/process/process_memory_test.cc | 220 ++++++++++++++++------------ 1 file changed, 125 insertions(+), 95 deletions(-) diff --git a/util/process/process_memory_test.cc b/util/process/process_memory_test.cc index 1d48bf4c..5827638f 100644 --- a/util/process/process_memory_test.cc +++ b/util/process/process_memory_test.cc @@ -423,120 +423,152 @@ TEST(ProcessMemory, ReadUnmappedChild) { test.RunAgainstChild(); } -// TODO(scottmg): Need to be ported to MultiprocessExec and not rely on fork(). -#if !defined(OS_FUCHSIA) +constexpr size_t kChildProcessStringLength = 10; -class TargetProcessTest : public Multiprocess { +class StringDataInChildProcess { public: - TargetProcessTest() : Multiprocess() {} - ~TargetProcessTest() {} + // This constructor only makes sense in the child process. + explicit StringDataInChildProcess(const char* cstring) + : address_(FromPointerCast(cstring)) { + memcpy(expected_value_, cstring, kChildProcessStringLength + 1); + } - void RunAgainstSelf() { DoTest(getpid()); } + void Write(FileHandle out) { + CheckedWriteFile(out, &address_, sizeof(address_)); + CheckedWriteFile(out, &expected_value_, sizeof(expected_value_)); + } - void RunAgainstForked() { Run(); } + static StringDataInChildProcess Read(FileHandle in) { + StringDataInChildProcess str; + EXPECT_TRUE(ReadFileExactly(in, &str.address_, sizeof(str.address_))); + EXPECT_TRUE( + ReadFileExactly(in, &str.expected_value_, sizeof(str.expected_value_))); + return str; + } - private: - void MultiprocessParent() override { DoTest(ChildPID()); } + VMAddress address() const { return address_; } + std::string expected_value() const { return expected_value_; } - void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); } + private: + StringDataInChildProcess() : address_(0), expected_value_() {} - virtual void DoTest(pid_t pid) = 0; - - DISALLOW_COPY_AND_ASSIGN(TargetProcessTest); + VMAddress address_; + char expected_value_[kChildProcessStringLength + 1]; }; -bool ReadCString(const ProcessMemory& memory, - const char* pointer, - std::string* result) { - return memory.ReadCString(FromPointerCast(pointer), result); +void DoCStringUnmappedTestSetup( + ScopedMmap* pages, + std::vector* strings) { + const size_t page_size = getpagesize(); + const size_t region_size = 2 * page_size; + if (!pages->ResetMmap(nullptr, + region_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0)) { + ADD_FAILURE(); + return; + } + + char* region = pages->addr_as(); + for (size_t index = 0; index < region_size; ++index) { + region[index] = 1 + index % 255; + } + + // A string at the start of the mapped region + char* string1 = region; + string1[kChildProcessStringLength] = '\0'; + + // A string near the end of the mapped region + char* string2 = region + page_size - kChildProcessStringLength * 2; + string2[kChildProcessStringLength] = '\0'; + + // A string that crosses from the mapped into the unmapped region + char* string3 = region + page_size - kChildProcessStringLength + 1; + string3[kChildProcessStringLength] = '\0'; + + // A string entirely in the unmapped region + char* string4 = region + page_size + 10; + string4[kChildProcessStringLength] = '\0'; + + strings->push_back(StringDataInChildProcess(string1)); + strings->push_back(StringDataInChildProcess(string2)); + strings->push_back(StringDataInChildProcess(string3)); + strings->push_back(StringDataInChildProcess(string4)); + + EXPECT_TRUE(pages->ResetAddrLen(region, page_size)); } -bool ReadCStringSizeLimited(const ProcessMemory& memory, - const char* pointer, - size_t size, - std::string* result) { - return memory.ReadCStringSizeLimited( - FromPointerCast(pointer), size, result); +CRASHPAD_CHILD_TEST_MAIN(ReadCStringUnmappedChildMain) { + ScopedMmap pages; + std::vector strings; + DoCStringUnmappedTestSetup(&pages, &strings); + FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); + strings[0].Write(out); + strings[1].Write(out); + strings[2].Write(out); + strings[3].Write(out); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; } -class ReadCStringUnmappedTest : public TargetProcessTest { +class ReadCStringUnmappedTest : public MultiprocessExec { public: ReadCStringUnmappedTest(bool limit_size) - : TargetProcessTest(), - page_size_(getpagesize()), - region_size_(2 * page_size_), - limit_size_(limit_size) { - if (!pages_.ResetMmap(nullptr, - region_size_, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, - -1, - 0)) { - ADD_FAILURE(); - return; - } - - char* region = pages_.addr_as(); - for (size_t index = 0; index < region_size_; ++index) { - region[index] = 1 + index % 255; - } - - // A string at the start of the mapped region - string1_ = region; - string1_[expected_length_] = '\0'; - - // A string near the end of the mapped region - string2_ = region + page_size_ - expected_length_ * 2; - string2_[expected_length_] = '\0'; - - // A string that crosses from the mapped into the unmapped region - string3_ = region + page_size_ - expected_length_ + 1; - string3_[expected_length_] = '\0'; - - // A string entirely in the unmapped region - string4_ = region + page_size_ + 10; - string4_[expected_length_] = '\0'; - - result_.reserve(expected_length_ + 1); - - EXPECT_TRUE(pages_.ResetAddrLen(region, page_size_)); + : MultiprocessExec(), limit_size_(limit_size) { + SetChildTestMainFunction("ReadCStringUnmappedChildMain"); } + void RunAgainstSelf() { + ScopedMmap pages; + std::vector strings; + DoCStringUnmappedTestSetup(&pages, &strings); + DoTest(GetSelfProcess(), strings); + } + + void RunAgainstChild() { Run(); } + private: - void DoTest(pid_t pid) override { + void MultiprocessParent() override { + std::vector strings; + strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle())); + strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle())); + strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle())); + strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle())); + ASSERT_NO_FATAL_FAILURE(); + DoTest(ChildProcess(), strings); + } + + void DoTest(ProcessType process, + const std::vector& strings) { ProcessMemoryNative memory; - ASSERT_TRUE(memory.Initialize(pid)); + ASSERT_TRUE(memory.Initialize(process)); + + std::string result; + result.reserve(kChildProcessStringLength + 1); if (limit_size_) { - ASSERT_TRUE(ReadCStringSizeLimited( - memory, string1_, expected_length_ + 1, &result_)); - EXPECT_EQ(result_, string1_); - ASSERT_TRUE(ReadCStringSizeLimited( - memory, string2_, expected_length_ + 1, &result_)); - EXPECT_EQ(result_, string2_); - EXPECT_FALSE(ReadCStringSizeLimited( - memory, string3_, expected_length_ + 1, &result_)); - EXPECT_FALSE(ReadCStringSizeLimited( - memory, string4_, expected_length_ + 1, &result_)); + ASSERT_TRUE(memory.ReadCStringSizeLimited( + strings[0].address(), kChildProcessStringLength + 1, &result)); + EXPECT_EQ(result, strings[0].expected_value()); + ASSERT_TRUE(memory.ReadCStringSizeLimited( + strings[1].address(), kChildProcessStringLength + 1, &result)); + EXPECT_EQ(result, strings[1].expected_value()); + EXPECT_FALSE(memory.ReadCStringSizeLimited( + strings[2].address(), kChildProcessStringLength + 1, &result)); + EXPECT_FALSE(memory.ReadCStringSizeLimited( + strings[3].address(), kChildProcessStringLength + 1, &result)); } else { - ASSERT_TRUE(ReadCString(memory, string1_, &result_)); - EXPECT_EQ(result_, string1_); - ASSERT_TRUE(ReadCString(memory, string2_, &result_)); - EXPECT_EQ(result_, string2_); - EXPECT_FALSE(ReadCString(memory, string3_, &result_)); - EXPECT_FALSE(ReadCString(memory, string4_, &result_)); + ASSERT_TRUE(memory.ReadCString(strings[0].address(), &result)); + EXPECT_EQ(result, strings[0].expected_value()); + ASSERT_TRUE(memory.ReadCString(strings[1].address(), &result)); + EXPECT_EQ(result, strings[1].expected_value()); + EXPECT_FALSE(memory.ReadCString(strings[2].address(), &result)); + EXPECT_FALSE(memory.ReadCString(strings[3].address(), &result)); } } - std::string result_; - ScopedMmap pages_; - const size_t page_size_; - const size_t region_size_; - static const size_t expected_length_ = 10; - char* string1_; - char* string2_; - char* string3_; - char* string4_; const bool limit_size_; DISALLOW_COPY_AND_ASSIGN(ReadCStringUnmappedTest); @@ -548,10 +580,10 @@ TEST(ProcessMemory, ReadCStringUnmappedSelf) { test.RunAgainstSelf(); } -TEST(ProcessMemory, ReadCStringUnmappedForked) { +TEST(ProcessMemory, ReadCStringUnmappedChild) { ReadCStringUnmappedTest test(/* limit_size= */ false); ASSERT_FALSE(testing::Test::HasFailure()); - test.RunAgainstForked(); + test.RunAgainstChild(); } TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedSelf) { @@ -560,14 +592,12 @@ TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedSelf) { test.RunAgainstSelf(); } -TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedForked) { +TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedChild) { ReadCStringUnmappedTest test(/* limit_size= */ true); ASSERT_FALSE(testing::Test::HasFailure()); - test.RunAgainstForked(); + test.RunAgainstChild(); } -#endif // !defined(OS_FUCHSIA) - } // namespace } // namespace test } // namespace crashpad From 574936540df33f434e82fa86565b9a5aabf89e34 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 1 Feb 2018 10:39:32 -0800 Subject: [PATCH 136/326] linux: Add CrashpadClient methods to start the handler This change includes methods to install a signal handler to launch the handler process at crash time or to launch the handler on behalf of another process. Bug: crashpad:30 Change-Id: I503c788cb3648852d09e9e8c1fe5099ca07a0277 Reviewed-on: https://chromium-review.googlesource.com/759406 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- client/BUILD.gn | 4 + client/client.gyp | 1 + client/crashpad_client.h | 65 ++++++++++ client/crashpad_client_linux.cc | 204 ++++++++++++++++++++++++++++++++ 4 files changed, 274 insertions(+) create mode 100644 client/crashpad_client_linux.cc diff --git a/client/BUILD.gn b/client/BUILD.gn index f7fd288f..f054e7ca 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -45,6 +45,10 @@ static_library("client") { ] } + if (crashpad_is_linux || crashpad_is_android) { + sources += [ "crashpad_client_linux.cc" ] + } + if (crashpad_is_win) { sources += [ "crash_report_database_win.cc", diff --git a/client/client.gyp b/client/client.gyp index 4b14c4bf..f75f9c40 100644 --- a/client/client.gyp +++ b/client/client.gyp @@ -40,6 +40,7 @@ 'crash_report_database_mac.mm', 'crash_report_database_win.cc', 'crashpad_client.h', + 'crashpad_client_linux.cc', 'crashpad_client_mac.cc', 'crashpad_client_win.cc', 'crashpad_info.cc', diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 7799bd9c..1f5bae65 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -105,6 +105,71 @@ class CrashpadClient { bool restartable, bool asynchronous_start); +#if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN + //! \brief Installs a signal handler to launch a handler process in reponse to + //! a crash. + //! + //! The handler process will create a crash dump for this process and exit. + //! + //! \param[in] handler The path to a Crashpad handler executable. + //! \param[in] database The path to a Crashpad database. The handler will be + //! started with this path as its `--database` argument. + //! \param[in] metrics_dir The path to an already existing directory where + //! metrics files can be stored. The handler will be started with this + //! path as its `--metrics-dir` argument. + //! \param[in] url The URL of an upload server. The handler will be started + //! with this URL as its `--url` argument. + //! \param[in] annotations Process annotations to set in each crash report. + //! The handler will be started with an `--annotation` argument for each + //! element in this map. + //! \param[in] arguments Additional arguments to pass to the Crashpad handler. + //! Arguments passed in other parameters and arguments required to perform + //! the handshake are the responsibility of this method, and must not be + //! specified in this parameter. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool StartHandlerAtCrash( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments); + + //! \brief Starts a handler process with an initial client. + //! + //! This method allows a process to launch the handler process on behalf of + //! another process. + //! + //! \param[in] handler The path to a Crashpad handler executable. + //! \param[in] database The path to a Crashpad database. The handler will be + //! started with this path as its `--database` argument. + //! \param[in] metrics_dir The path to an already existing directory where + //! metrics files can be stored. The handler will be started with this + //! path as its `--metrics-dir` argument. + //! \param[in] url The URL of an upload server. The handler will be started + //! with this URL as its `--url` argument. + //! \param[in] annotations Process annotations to set in each crash report. + //! The handler will be started with an `--annotation` argument for each + //! element in this map. + //! \param[in] arguments Additional arguments to pass to the Crashpad handler. + //! Arguments passed in other parameters and arguments required to perform + //! the handshake are the responsibility of this method, and must not be + //! specified in this parameter. + //! \param[in] socket The server end of a socket pair. The client end should + //! be used with an ExceptionHandlerClient. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool StartHandlerForClient( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + int socket); +#endif // OS_LINUX || OS_ANDROID || DOXYGEN + #if defined(OS_MACOSX) || DOXYGEN //! \brief Sets the process’ crash handler to a Mach service registered with //! the bootstrap server. diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc new file mode 100644 index 00000000..a36d9229 --- /dev/null +++ b/client/crashpad_client_linux.cc @@ -0,0 +1,204 @@ +// Copyright 2018 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 "client/crashpad_client.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "util/file/file_io.h" +#include "util/linux/exception_handler_client.h" +#include "util/linux/exception_information.h" +#include "util/misc/from_pointer_cast.h" +#include "util/posix/double_fork_and_exec.h" +#include "util/posix/signals.h" + +namespace crashpad { + +namespace { + +std::string FormatArgumentString(const std::string& name, + const std::string& value) { + return base::StringPrintf("--%s=%s", name.c_str(), value.c_str()); +} + +std::string FormatArgumentInt(const std::string& name, int value) { + return base::StringPrintf("--%s=%d", name.c_str(), value); +} + +std::string FormatArgumentAddress(const std::string& name, void* addr) { + return base::StringPrintf("--%s=%p", name.c_str(), addr); +} + +void BuildHandlerArgvStrings( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + std::vector* argv_strings) { + argv_strings->clear(); + + argv_strings->push_back(handler.value()); + for (const auto& argument : arguments) { + argv_strings->push_back(argument); + } + + if (!database.empty()) { + argv_strings->push_back(FormatArgumentString("database", database.value())); + } + + if (!metrics_dir.empty()) { + argv_strings->push_back( + FormatArgumentString("metrics-dir", metrics_dir.value())); + } + + if (!url.empty()) { + argv_strings->push_back(FormatArgumentString("url", url)); + } + + for (const auto& kv : annotations) { + argv_strings->push_back( + FormatArgumentString("annotation", kv.first + '=' + kv.second)); + } +} + +void ConvertArgvStrings(const std::vector argv_strings, + std::vector* argv) { + argv->clear(); + argv->reserve(argv_strings.size() + 1); + for (const auto& arg : argv_strings) { + argv->push_back(arg.c_str()); + } + argv->push_back(nullptr); +} + +// Launches a single use handler to snapshot this process. +class LaunchAtCrashHandler { + public: + static LaunchAtCrashHandler* Get() { + static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler(); + return instance; + } + + bool Initialize(std::vector* argv_in) { + argv_strings_.swap(*argv_in); + + argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception", + &exception_information_)); + + ConvertArgvStrings(argv_strings_, &argv_); + return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr); + } + + private: + LaunchAtCrashHandler() = default; + + ~LaunchAtCrashHandler() = delete; + + static void HandleCrash(int signo, siginfo_t* siginfo, void* context) { + auto state = Get(); + auto exception_information = &state->exception_information_; + + exception_information->siginfo_address = + FromPointerCastsiginfo_address)>( + siginfo); + exception_information->context_address = + FromPointerCastcontext_address)>( + context); + exception_information->thread_id = syscall(SYS_gettid); + + pid_t pid = fork(); + if (pid < 0) { + return; + } + if (pid == 0) { + execv(state->argv_[0], const_cast(state->argv_.data())); + return; + } + + int status; + waitpid(pid, &status, 0); + } + + std::vector argv_strings_; + std::vector argv_; + ExceptionInformation exception_information_; + + DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler); +}; + +} // namespace + +CrashpadClient::CrashpadClient() {} + +CrashpadClient::~CrashpadClient() {} + +bool CrashpadClient::StartHandler( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + bool restartable, + bool asynchronous_start) { + // TODO(jperaza): Implement this after the Android/Linux ExceptionHandlerSever + // supports accepting new connections. + // https://crashpad.chromium.org/bug/30 + NOTREACHED(); + return false; +} + +bool CrashpadClient::StartHandlerAtCrash( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments) { + std::vector argv; + BuildHandlerArgvStrings( + handler, database, metrics_dir, url, annotations, arguments, &argv); + + auto signal_handler = LaunchAtCrashHandler::Get(); + return signal_handler->Initialize(&argv); +} + +bool CrashpadClient::StartHandlerForClient( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + int socket) { + std::vector argv; + BuildHandlerArgvStrings( + handler, database, metrics_dir, url, annotations, arguments, &argv); + + argv.push_back(FormatArgumentInt("initial-client", socket)); + + return DoubleForkAndExec(argv, socket, true, nullptr); +} + +} // namespace crashpad From 0fa7d9d424da85b2b5a6cef02f6f410847f4abfb Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 1 Feb 2018 10:46:27 -0800 Subject: [PATCH 137/326] mac: Remove dead code Responsibility for creating argv_c has moved to DoubleForkAndExec(). Change-Id: Id663f0597ee1749df564cdacac1d877b5545750b Reviewed-on: https://chromium-review.googlesource.com/898024 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- client/crashpad_client_mac.cc | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/client/crashpad_client_mac.cc b/client/crashpad_client_mac.cc index a16f8d28..4a408206 100644 --- a/client/crashpad_client_mac.cc +++ b/client/crashpad_client_mac.cc @@ -331,17 +331,6 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { } argv.push_back(FormatArgumentInt("handshake-fd", server_write_fd.get())); - // argv_c contains const char* pointers and is terminated by nullptr. argv - // is required because the pointers in argv_c need to point somewhere, and - // they can’t point to temporaries such as those returned by - // FormatArgumentString(). - std::vector argv_c; - argv_c.reserve(argv.size() + 1); - for (const std::string& argument : argv) { - argv_c.push_back(argument.c_str()); - } - argv_c.push_back(nullptr); - // When restarting, reset the system default crash handler first. Otherwise, // the crash exception port in the handler will have been inherited from // this parent process, which was probably using the exception server now From 9b6c69cbb59f85f3b7f1fb2423e2c63111b92087 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 1 Feb 2018 15:54:56 -0800 Subject: [PATCH 138/326] Coalesce memory ranges Follows https://chromium-review.googlesource.com/c/374019/. Causes MinidumpMemoryListWriter to merge all overlapping ranges before writing the MINIDUMP_MEMORY_LIST. This is: 1) Necessary for the Google internal crash processor, which in some cases attempts to read the raw memory (displaying ASAN red zones), and aborts if there are any overlapping ranges in the minidump on load; 2) Necessary for new-ish versions of windbg (see bug 216 below). It is believed that this is a change in behavior in the tool that made dumps with overlapping ranges unreadable; 3) More efficient. The .dmp for crashy_program goes from 306K to 140K with this enabled. In Chrome minidumps where set_gather_indirectly_referenced_memory() is used (in practice this means Chrome Windows Beta, Dev, and Canary), the savings are expected to be substantial. Bug: crashpad:61, chromium:638370, crashpad:216 Change-Id: I969e1a52da555ceba59a727d933bfeef6787c7a5 Reviewed-on: https://chromium-review.googlesource.com/374539 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- minidump/minidump_memory_writer.cc | 106 +++++++-- minidump/minidump_memory_writer.h | 40 +++- minidump/minidump_memory_writer_test.cc | 296 +++++++++++++++++++++++- minidump/minidump_thread_writer.cc | 2 +- minidump/minidump_thread_writer_test.cc | 4 +- snapshot/BUILD.gn | 2 + snapshot/linux/memory_snapshot_linux.cc | 5 + snapshot/linux/memory_snapshot_linux.h | 7 + snapshot/mac/memory_snapshot_mac.cc | 5 + snapshot/mac/memory_snapshot_mac.h | 7 + snapshot/memory_snapshot.cc | 95 ++++++++ snapshot/memory_snapshot.h | 64 +++++ snapshot/memory_snapshot_test.cc | 152 ++++++++++++ snapshot/snapshot.gyp | 1 + snapshot/snapshot_test.gyp | 2 + snapshot/test/test_memory_snapshot.cc | 14 ++ snapshot/test/test_memory_snapshot.h | 2 + snapshot/win/memory_snapshot_win.cc | 6 +- snapshot/win/memory_snapshot_win.h | 7 + 19 files changed, 794 insertions(+), 23 deletions(-) create mode 100644 snapshot/memory_snapshot.cc create mode 100644 snapshot/memory_snapshot_test.cc diff --git a/minidump/minidump_memory_writer.cc b/minidump/minidump_memory_writer.cc index f6aca081..0e5f0861 100644 --- a/minidump/minidump_memory_writer.cc +++ b/minidump/minidump_memory_writer.cc @@ -14,6 +14,8 @@ #include "minidump/minidump_memory_writer.h" +#include +#include #include #include "base/auto_reset.h" @@ -37,7 +39,7 @@ SnapshotMinidumpMemoryWriter::~SnapshotMinidumpMemoryWriter() {} bool SnapshotMinidumpMemoryWriter::MemorySnapshotDelegateRead(void* data, size_t size) { DCHECK_EQ(state(), kStateWritable); - DCHECK_EQ(size, UnderlyingSnapshot().Size()); + DCHECK_EQ(size, UnderlyingSnapshot()->Size()); return file_writer_->Write(data, size); } @@ -89,7 +91,7 @@ size_t SnapshotMinidumpMemoryWriter::Alignment() { size_t SnapshotMinidumpMemoryWriter::SizeOfObject() { DCHECK_GE(state(), kStateFrozen); - return UnderlyingSnapshot().Size(); + return UnderlyingSnapshot()->Size(); } bool SnapshotMinidumpMemoryWriter::WillWriteAtOffsetImpl(FileOffset offset) { @@ -99,7 +101,7 @@ bool SnapshotMinidumpMemoryWriter::WillWriteAtOffsetImpl(FileOffset offset) { // object’s own memory_descriptor_ field. DCHECK_GE(registered_memory_descriptors_.size(), 1u); - uint64_t base_address = UnderlyingSnapshot().Address(); + uint64_t base_address = UnderlyingSnapshot()->Address(); decltype(registered_memory_descriptors_[0]->StartOfMemoryRange) local_address; if (!AssignIfInRange(&local_address, base_address)) { LOG(ERROR) << "base_address " << base_address << " out of range"; @@ -125,10 +127,11 @@ internal::MinidumpWritable::Phase SnapshotMinidumpMemoryWriter::WritePhase() { MinidumpMemoryListWriter::MinidumpMemoryListWriter() : MinidumpStreamWriter(), - memory_writers_(), + non_owned_memory_writers_(), children_(), - memory_list_base_() { -} + snapshots_created_during_merge_(), + all_memory_writers_(), + memory_list_base_() {} MinidumpMemoryListWriter::~MinidumpMemoryListWriter() { } @@ -148,25 +151,80 @@ void MinidumpMemoryListWriter::AddMemory( std::unique_ptr memory_writer) { DCHECK_EQ(state(), kStateMutable); - AddExtraMemory(memory_writer.get()); children_.push_back(std::move(memory_writer)); } -void MinidumpMemoryListWriter::AddExtraMemory( +void MinidumpMemoryListWriter::AddNonOwnedMemory( SnapshotMinidumpMemoryWriter* memory_writer) { DCHECK_EQ(state(), kStateMutable); - memory_writers_.push_back(memory_writer); + non_owned_memory_writers_.push_back(memory_writer); +} + +void MinidumpMemoryListWriter::CoalesceOwnedMemory() { + if (children_.empty()) + return; + + DropRangesThatOverlapNonOwned(); + + std::sort(children_.begin(), + children_.end(), + [](const std::unique_ptr& a_ptr, + const std::unique_ptr& b_ptr) { + const MemorySnapshot* a = a_ptr->UnderlyingSnapshot(); + const MemorySnapshot* b = b_ptr->UnderlyingSnapshot(); + if (a->Address() == b->Address()) { + return a->Size() < b->Size(); + } + return a->Address() < b->Address(); + }); + + // Remove any empty ranges. + children_.erase( + std::remove_if(children_.begin(), + children_.end(), + [](const auto& snapshot) { + return snapshot->UnderlyingSnapshot()->Size() == 0; + }), + children_.end()); + + std::vector> all_merged; + all_merged.push_back(std::move(children_.front())); + for (size_t i = 1; i < children_.size(); ++i) { + SnapshotMinidumpMemoryWriter* top = all_merged.back().get(); + auto& child = children_[i]; + if (!DetermineMergedRange( + child->UnderlyingSnapshot(), top->UnderlyingSnapshot(), nullptr)) { + // If it doesn't overlap with the current range, push it. + all_merged.push_back(std::move(child)); + } else { + // Otherwise, merge and update the current element. + std::unique_ptr merged( + top->UnderlyingSnapshot()->MergeWithOtherSnapshot( + child->UnderlyingSnapshot())); + top->SetSnapshot(merged.get()); + snapshots_created_during_merge_.push_back(std::move(merged)); + } + } + std::swap(children_, all_merged); } bool MinidumpMemoryListWriter::Freeze() { DCHECK_EQ(state(), kStateMutable); + CoalesceOwnedMemory(); + + std::copy(non_owned_memory_writers_.begin(), + non_owned_memory_writers_.end(), + std::back_inserter(all_memory_writers_)); + for (const auto& ptr : children_) + all_memory_writers_.push_back(ptr.get()); + if (!MinidumpStreamWriter::Freeze()) { return false; } - size_t memory_region_count = memory_writers_.size(); + size_t memory_region_count = all_memory_writers_.size(); CHECK_LE(children_.size(), memory_region_count); if (!AssignIfInRange(&memory_list_base_.NumberOfMemoryRanges, @@ -181,15 +239,15 @@ bool MinidumpMemoryListWriter::Freeze() { size_t MinidumpMemoryListWriter::SizeOfObject() { DCHECK_GE(state(), kStateFrozen); - DCHECK_LE(children_.size(), memory_writers_.size()); + DCHECK_LE(children_.size(), all_memory_writers_.size()); return sizeof(memory_list_base_) + - memory_writers_.size() * sizeof(MINIDUMP_MEMORY_DESCRIPTOR); + all_memory_writers_.size() * sizeof(MINIDUMP_MEMORY_DESCRIPTOR); } std::vector MinidumpMemoryListWriter::Children() { DCHECK_GE(state(), kStateFrozen); - DCHECK_LE(children_.size(), memory_writers_.size()); + DCHECK_LE(children_.size(), all_memory_writers_.size()); std::vector children; for (const auto& child : children_) { @@ -207,7 +265,8 @@ bool MinidumpMemoryListWriter::WriteObject(FileWriterInterface* file_writer) { iov.iov_len = sizeof(memory_list_base_); std::vector iovecs(1, iov); - for (const SnapshotMinidumpMemoryWriter* memory_writer : memory_writers_) { + for (const SnapshotMinidumpMemoryWriter* memory_writer : + all_memory_writers_) { iov.iov_base = memory_writer->MinidumpMemoryDescriptor(); iov.iov_len = sizeof(MINIDUMP_MEMORY_DESCRIPTOR); iovecs.push_back(iov); @@ -220,4 +279,23 @@ MinidumpStreamType MinidumpMemoryListWriter::StreamType() const { return kMinidumpStreamTypeMemoryList; } +void MinidumpMemoryListWriter::DropRangesThatOverlapNonOwned() { + std::vector> non_overlapping; + non_overlapping.reserve(children_.size()); + for (auto& child_ptr : children_) { + bool overlaps = false; + for (const auto* non_owned : non_owned_memory_writers_) { + if (DetermineMergedRange(child_ptr->UnderlyingSnapshot(), + non_owned->UnderlyingSnapshot(), + nullptr)) { + overlaps = true; + break; + } + } + if (!overlaps) + non_overlapping.push_back(std::move(child_ptr)); + } + std::swap(children_, non_overlapping); +} + } // namespace crashpad diff --git a/minidump/minidump_memory_writer.h b/minidump/minidump_memory_writer.h index 95520907..4f4f27f6 100644 --- a/minidump/minidump_memory_writer.h +++ b/minidump/minidump_memory_writer.h @@ -60,7 +60,15 @@ class SnapshotMinidumpMemoryWriter : public internal::MinidumpWritable, //! \note Valid in #kStateFrozen or any preceding state. void RegisterMemoryDescriptor(MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor); + //! \brief Sets the underlying memory snapshot. Does not take ownership of \a + //! memory_snapshot. + void SetSnapshot(const MemorySnapshot* memory_snapshot) { + memory_snapshot_ = memory_snapshot; + } + private: + friend class MinidumpMemoryListWriter; + // MemorySnapshot::Delegate: bool MemorySnapshotDelegateRead(void* data, size_t size) override; @@ -95,7 +103,7 @@ class SnapshotMinidumpMemoryWriter : public internal::MinidumpWritable, //! \brief Gets the underlying memory snapshot that the memory writer will //! write to the minidump. - const MemorySnapshot& UnderlyingSnapshot() const { return *memory_snapshot_; } + const MemorySnapshot* UnderlyingSnapshot() const { return memory_snapshot_; } MINIDUMP_MEMORY_DESCRIPTOR memory_descriptor_; @@ -147,7 +155,7 @@ class MinidumpMemoryListWriter final : public internal::MinidumpStreamWriter { //! a SnapshotMinidumpMemoryWriter for thread stack memory, is an example. //! //! \note Valid in #kStateMutable. - void AddExtraMemory(SnapshotMinidumpMemoryWriter* memory_writer); + void AddNonOwnedMemory(SnapshotMinidumpMemoryWriter* memory_writer); protected: // MinidumpWritable: @@ -159,9 +167,35 @@ class MinidumpMemoryListWriter final : public internal::MinidumpStreamWriter { // MinidumpStreamWriter: MinidumpStreamType StreamType() const override; + //! \brief Merges any overlapping and abutting memory ranges that were added + //! via AddFromSnapshot() and AddMemory() into single entries. + //! + //! This is expected to be called once just before writing, generally from + //! Freeze(). + //! + //! This function has the side-effect of merging owned ranges, dropping any + //! owned ranges that overlap with non-owned ranges, removing empty ranges, + //! and sorting all ranges by address. + //! + //! Per its name, this coalesces owned memory, however, this is not a complete + //! solution for ensuring that no overlapping memory ranges are emitted in the + //! minidump. In particular, if AddNonOwnedMemory() is used to add multiple + //! overlapping ranges, then overlapping ranges will still be emitted to the + //! minidump. Currently, AddNonOwnedMemory() is used only for adding thread + //! stacks, so overlapping shouldn't be a problem in practice. For more + //! details see https://crashpad.chromium.org/bug/61 and + //! https://crrev.com/c/374539. + void CoalesceOwnedMemory(); + private: - std::vector memory_writers_; // weak + //! \brief Drops children_ ranges that overlap non_owned_memory_writers_. + void DropRangesThatOverlapNonOwned(); + + std::vector non_owned_memory_writers_; // weak std::vector> children_; + std::vector> + snapshots_created_during_merge_; + std::vector all_memory_writers_; // weak MINIDUMP_MEMORY_LIST memory_list_base_; DISALLOW_COPY_AND_ASSIGN(MinidumpMemoryListWriter); diff --git a/minidump/minidump_memory_writer_test.cc b/minidump/minidump_memory_writer_test.cc index 0290b20d..acff9c29 100644 --- a/minidump/minidump_memory_writer_test.cc +++ b/minidump/minidump_memory_writer_test.cc @@ -241,7 +241,7 @@ TEST(MinidumpMemoryWriter, ExtraMemory) { std::make_unique(kBaseAddress0, kSize0, kValue0); auto memory_list_writer = std::make_unique(); - memory_list_writer->AddExtraMemory(test_memory_stream->memory()); + memory_list_writer->AddNonOwnedMemory(test_memory_stream->memory()); ASSERT_TRUE(minidump_file_writer.AddStream(std::move(test_memory_stream))); @@ -305,7 +305,7 @@ TEST(MinidumpMemoryWriter, AddFromSnapshot) { expect_memory_descriptors[0].Memory.DataSize = 0x1000; values[0] = 0x01; - expect_memory_descriptors[1].StartOfMemoryRange = 0x1000; + expect_memory_descriptors[1].StartOfMemoryRange = 0x2000; expect_memory_descriptors[1].Memory.DataSize = 0x2000; values[1] = 0xf4; @@ -353,6 +353,298 @@ TEST(MinidumpMemoryWriter, AddFromSnapshot) { } } +TEST(MinidumpMemoryWriter, CoalesceExplicitMultiple) { + MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[4] = {}; + uint8_t values[arraysize(expect_memory_descriptors)] = {}; + + expect_memory_descriptors[0].StartOfMemoryRange = 0; + expect_memory_descriptors[0].Memory.DataSize = 1000; + values[0] = 0x01; + + expect_memory_descriptors[1].StartOfMemoryRange = 10000; + expect_memory_descriptors[1].Memory.DataSize = 2000; + values[1] = 0xf4; + + expect_memory_descriptors[2].StartOfMemoryRange = 0x1111111111111111; + expect_memory_descriptors[2].Memory.DataSize = 1024; + values[2] = 0x99; + + expect_memory_descriptors[3].StartOfMemoryRange = 0xfedcba9876543210; + expect_memory_descriptors[3].Memory.DataSize = 1024; + values[3] = 0x88; + + struct { + uint64_t base; + size_t size; + uint8_t value; + } snapshots_to_add[] = { + // Various overlapping. + {0, 500, 0x01}, + {0, 500, 0x01}, + {250, 500, 0x01}, + {600, 400, 0x01}, + + // Empty removed. + {0, 0, 0xbb}, + {300, 0, 0xcc}, + {1000, 0, 0xdd}, + {12000, 0, 0xee}, + + // Abutting. + {10000, 500, 0xf4}, + {10500, 500, 0xf4}, + {11000, 1000, 0xf4}, + + // Large base addresses. + { 0xfedcba9876543210, 1024, 0x88 }, + { 0x1111111111111111, 1024, 0x99 }, + }; + + std::vector> memory_snapshots_owner; + std::vector memory_snapshots; + for (const auto& to_add : snapshots_to_add) { + memory_snapshots_owner.push_back(std::make_unique()); + TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get(); + memory_snapshot->SetAddress(to_add.base); + memory_snapshot->SetSize(to_add.size); + memory_snapshot->SetValue(to_add.value); + memory_snapshots.push_back(memory_snapshot); + } + + auto memory_list_writer = std::make_unique(); + memory_list_writer->AddFromSnapshot(memory_snapshots); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(std::move(memory_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + ASSERT_EQ(4u, memory_list->NumberOfMemoryRanges); + + for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index)); + ExpectMinidumpMemoryDescriptorAndContents( + &expect_memory_descriptors[index], + &memory_list->MemoryRanges[index], + string_file.string(), + values[index], + index == memory_list->NumberOfMemoryRanges - 1); + } +} + +struct TestRange { + TestRange(uint64_t base, size_t size) : base(base), size(size) {} + + uint64_t base; + size_t size; +}; + +// Parses a string spec to build a list of ranges suitable for CoalesceTest(). +std::vector ParseCoalesceSpec(const char* spec) { + std::vector result; + enum { kNone, kSpace, kDot } state = kNone; + const char* range_started_at = nullptr; + for (const char* p = spec;; ++p) { + EXPECT_TRUE(*p == ' ' || *p == '.' || *p == 0); + if (*p == ' ' || *p == 0) { + if (state == kDot) { + result.push_back( + TestRange(range_started_at - spec, p - range_started_at)); + } + state = kSpace; + range_started_at = nullptr; + } else if (*p == '.') { + if (state != kDot) { + range_started_at = p; + state = kDot; + } + } + + if (*p == 0) + break; + } + + return result; +} + +TEST(MinidumpMemoryWriter, CoalesceSpecHelperParse) { + const auto empty = ParseCoalesceSpec(""); + ASSERT_EQ(empty.size(), 0u); + + const auto a = ParseCoalesceSpec("..."); + ASSERT_EQ(a.size(), 1u); + EXPECT_EQ(a[0].base, 0u); + EXPECT_EQ(a[0].size, 3u); + + const auto b = ParseCoalesceSpec(" ..."); + ASSERT_EQ(b.size(), 1u); + EXPECT_EQ(b[0].base, 2u); + EXPECT_EQ(b[0].size, 3u); + + const auto c = ParseCoalesceSpec(" ... "); + ASSERT_EQ(c.size(), 1u); + EXPECT_EQ(c[0].base, 2u); + EXPECT_EQ(c[0].size, 3u); + + const auto d = ParseCoalesceSpec(" ... ...."); + ASSERT_EQ(d.size(), 2u); + EXPECT_EQ(d[0].base, 2u); + EXPECT_EQ(d[0].size, 3u); + EXPECT_EQ(d[1].base, 7u); + EXPECT_EQ(d[1].size, 4u); + + const auto e = ParseCoalesceSpec(" ... ...... ... "); + ASSERT_EQ(e.size(), 3u); + EXPECT_EQ(e[0].base, 2u); + EXPECT_EQ(e[0].size, 3u); + EXPECT_EQ(e[1].base, 7u); + EXPECT_EQ(e[1].size, 6u); + EXPECT_EQ(e[2].base, 14u); + EXPECT_EQ(e[2].size, 3u); +} + +constexpr uint8_t kMemoryValue = 0xcd; + +// Builds a coalesce test out of specs of ' ' and '.'. Tests that when the two +// ranges are added and coalesced, the result is equal to expected. +void CoalesceTest(const char* r1_spec, + const char* r2_spec, + const char* expected_spec) { + auto r1 = ParseCoalesceSpec(r1_spec); + auto r2 = ParseCoalesceSpec(r2_spec); + auto expected = ParseCoalesceSpec(expected_spec); + + std::vector expect_memory_descriptors; + for (const auto& range : expected) { + MINIDUMP_MEMORY_DESCRIPTOR mmd = {}; + mmd.StartOfMemoryRange = range.base; + mmd.Memory.DataSize = static_cast(range.size); + expect_memory_descriptors.push_back(mmd); + } + + std::vector> memory_snapshots_owner; + std::vector memory_snapshots; + + const auto add_test_memory_snapshots = [&memory_snapshots_owner, + &memory_snapshots]( + std::vector ranges) { + for (const auto& r : ranges) { + memory_snapshots_owner.push_back(std::make_unique()); + TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get(); + memory_snapshot->SetAddress(r.base); + memory_snapshot->SetSize(r.size); + memory_snapshot->SetValue(kMemoryValue); + memory_snapshots.push_back(memory_snapshot); + } + }; + add_test_memory_snapshots(r1); + add_test_memory_snapshots(r2); + + auto memory_list_writer = std::make_unique(); + memory_list_writer->AddFromSnapshot(memory_snapshots); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(std::move(memory_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + ASSERT_EQ(expected.size(), memory_list->NumberOfMemoryRanges); + + for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index)); + ExpectMinidumpMemoryDescriptorAndContents( + &expect_memory_descriptors[index], + &memory_list->MemoryRanges[index], + string_file.string(), + kMemoryValue, + index == memory_list->NumberOfMemoryRanges - 1); + } +} + +TEST(MinidumpMemoryWriter, CoalescePairsVariousCases) { + // clang-format off + + CoalesceTest(" .........", + " .......", + /* result */ " .............."); + + CoalesceTest(" .......", + " .........", + /* result */ " .............."); + + CoalesceTest(" ...", + " .........", + /* result */ " ........."); + + CoalesceTest(" .........", + " ......", + /* result */ " ........."); + + CoalesceTest(" ...", + " ........", + /* result */ " ........"); + + CoalesceTest(" ........", + " ...", + /* result */ " ........"); + + CoalesceTest(" ...", + " ........", + /* result */ " ........"); + + CoalesceTest(" ........", + " ...", + /* result */ " ........"); + + CoalesceTest(" ... ", + " ...", + /* result */ " ... ..."); + + CoalesceTest(" ...", + " ... ", + /* result */ " ... ..."); + + CoalesceTest("...", + ".....", + /* result */ "....."); + + CoalesceTest("...", + " ..", + /* result */ "....."); + + CoalesceTest(" .....", + " ..", + /* result */ " ......."); + + CoalesceTest(" ......... ......", + " .......", + /* result */ " .................."); + + CoalesceTest(" .......", + " ......... ......", + /* result */ " .................."); + + CoalesceTest(" .....", + " ......... ......", + /* result */ " ......... ......"); + + CoalesceTest(" ......... ....... .... .", + " ......... ...... ....", + /* result */ " .......................... ......."); + + // clang-format on +} + } // namespace } // namespace test } // namespace crashpad diff --git a/minidump/minidump_thread_writer.cc b/minidump/minidump_thread_writer.cc index 4f390ddb..67658e18 100644 --- a/minidump/minidump_thread_writer.cc +++ b/minidump/minidump_thread_writer.cc @@ -173,7 +173,7 @@ void MinidumpThreadListWriter::AddThread( if (memory_list_writer_) { SnapshotMinidumpMemoryWriter* stack = thread->Stack(); if (stack) { - memory_list_writer_->AddExtraMemory(stack); + memory_list_writer_->AddNonOwnedMemory(stack); } } diff --git a/minidump/minidump_thread_writer_test.cc b/minidump/minidump_thread_writer_test.cc index d9e85aa6..e95c9044 100644 --- a/minidump/minidump_thread_writer_test.cc +++ b/minidump/minidump_thread_writer_test.cc @@ -545,7 +545,7 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) { expect_threads[1].ThreadId = 11; expect_threads[1].SuspendCount = 12; expect_threads[1].Priority = 13; - expect_threads[1].Teb = 0xfedcba9876543210; + expect_threads[1].Teb = 0x1111111111111111; expect_threads[1].ThreadContext.DataSize = sizeof(MinidumpContextType); context_seeds[1] = 0x40000001; tebs[1].StartOfMemoryRange = expect_threads[1].Teb; @@ -554,7 +554,7 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) { expect_threads[2].ThreadId = 21; expect_threads[2].SuspendCount = 22; expect_threads[2].Priority = 23; - expect_threads[2].Teb = 0x1111111111111111; + expect_threads[2].Teb = 0xfedcba9876543210; expect_threads[2].Stack.StartOfMemoryRange = 0x3000; expect_threads[2].Stack.Memory.DataSize = 0x300; expect_threads[2].ThreadContext.DataSize = sizeof(MinidumpContextType); diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 6b3fe65d..ad07e23a 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -32,6 +32,7 @@ static_library("snapshot") { "exception_snapshot.h", "handle_snapshot.cc", "handle_snapshot.h", + "memory_snapshot.cc", "memory_snapshot.h", "minidump/minidump_annotation_reader.cc", "minidump/minidump_annotation_reader.h", @@ -279,6 +280,7 @@ source_set("snapshot_test") { sources = [ "cpu_context_test.cc", + "memory_snapshot_test.cc", "minidump/process_snapshot_minidump_test.cc", ] diff --git a/snapshot/linux/memory_snapshot_linux.cc b/snapshot/linux/memory_snapshot_linux.cc index f328fefc..dfe9d330 100644 --- a/snapshot/linux/memory_snapshot_linux.cc +++ b/snapshot/linux/memory_snapshot_linux.cc @@ -64,5 +64,10 @@ bool MemorySnapshotLinux::Read(Delegate* delegate) const { return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); } +const MemorySnapshot* MemorySnapshotLinux::MergeWithOtherSnapshot( + const MemorySnapshot* other) const { + return MergeWithOtherSnapshotImpl(this, other); +} + } // namespace internal } // namespace crashpad diff --git a/snapshot/linux/memory_snapshot_linux.h b/snapshot/linux/memory_snapshot_linux.h index 3c7c8391..8b4bcf8d 100644 --- a/snapshot/linux/memory_snapshot_linux.h +++ b/snapshot/linux/memory_snapshot_linux.h @@ -53,8 +53,15 @@ class MemorySnapshotLinux final : public MemorySnapshot { uint64_t Address() const override; size_t Size() const override; bool Read(Delegate* delegate) const override; + const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const override; private: + template + friend const MemorySnapshot* MergeWithOtherSnapshotImpl( + const T* self, + const MemorySnapshot* other); + ProcessReader* process_reader_; // weak uint64_t address_; size_t size_; diff --git a/snapshot/mac/memory_snapshot_mac.cc b/snapshot/mac/memory_snapshot_mac.cc index adc77520..ec1d4608 100644 --- a/snapshot/mac/memory_snapshot_mac.cc +++ b/snapshot/mac/memory_snapshot_mac.cc @@ -66,5 +66,10 @@ bool MemorySnapshotMac::Read(Delegate* delegate) const { return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); } +const MemorySnapshot* MemorySnapshotMac::MergeWithOtherSnapshot( + const MemorySnapshot* other) const { + return MergeWithOtherSnapshotImpl(this, other); +} + } // namespace internal } // namespace crashpad diff --git a/snapshot/mac/memory_snapshot_mac.h b/snapshot/mac/memory_snapshot_mac.h index bbc39de3..f06fbf47 100644 --- a/snapshot/mac/memory_snapshot_mac.h +++ b/snapshot/mac/memory_snapshot_mac.h @@ -52,8 +52,15 @@ class MemorySnapshotMac final : public MemorySnapshot { uint64_t Address() const override; size_t Size() const override; bool Read(Delegate* delegate) const override; + const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const override; private: + template + friend const MemorySnapshot* MergeWithOtherSnapshotImpl( + const T* self, + const MemorySnapshot* other); + ProcessReader* process_reader_; // weak uint64_t address_; uint64_t size_; diff --git a/snapshot/memory_snapshot.cc b/snapshot/memory_snapshot.cc new file mode 100644 index 00000000..a4c8b038 --- /dev/null +++ b/snapshot/memory_snapshot.cc @@ -0,0 +1,95 @@ +// Copyright 2018 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/memory_snapshot.h" + +#include + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "util/numeric/checked_range.h" + +namespace crashpad { +namespace { + +bool DetermineMergedRangeImpl(bool log, + const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange* merged) { + if (a->Size() == 0) { + LOG_IF(ERROR, log) << base::StringPrintf( + "invalid empty range at 0x%" PRIx64, a->Address()); + return false; + } + + if (b->Size() == 0) { + LOG_IF(ERROR, log) << base::StringPrintf( + "invalid empty range at 0x%" PRIx64, b->Address()); + return false; + } + + CheckedRange range_a(a->Address(), a->Size()); + if (!range_a.IsValid()) { + LOG_IF(ERROR, log) << base::StringPrintf("invalid range at 0x%" PRIx64 + ", size %" PRIuS, + range_a.base(), + range_a.size()); + return false; + } + + CheckedRange range_b(b->Address(), b->Size()); + if (!range_b.IsValid()) { + LOG_IF(ERROR, log) << base::StringPrintf("invalid range at 0x%" PRIx64 + ", size %" PRIuS, + range_b.base(), + range_b.size()); + return false; + } + + if (!range_a.OverlapsRange(range_b) && range_a.end() != range_b.base() && + range_b.end() != range_a.base()) { + LOG_IF(ERROR, log) << base::StringPrintf( + "ranges not overlapping or abutting: (0x%" PRIx64 ", size %" PRIuS + ") and (0x%" PRIx64 ", size %" PRIuS ")", + range_a.base(), + range_a.size(), + range_b.base(), + range_b.size()); + return false; + } + + if (merged) { + uint64_t base = std::min(range_a.base(), range_b.base()); + uint64_t end = std::max(range_a.end(), range_b.end()); + size_t size = static_cast(end - base); + merged->SetRange(base, size); + } + return true; +} + +} // namespace + +bool LoggingDetermineMergedRange(const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange* merged) { + return DetermineMergedRangeImpl(true, a, b, merged); +} + +bool DetermineMergedRange(const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange* merged) { + return DetermineMergedRangeImpl(false, a, b, merged); +} + +} // namespace crashpad diff --git a/snapshot/memory_snapshot.h b/snapshot/memory_snapshot.h index 47f8443e..9e274a0e 100644 --- a/snapshot/memory_snapshot.h +++ b/snapshot/memory_snapshot.h @@ -18,6 +18,10 @@ #include #include +#include + +#include "util/numeric/checked_range.h" + namespace crashpad { //! \brief An abstract interface to a snapshot representing a region of memory @@ -70,8 +74,68 @@ class MemorySnapshot { //! Delegate::MemorySnapshotDelegateRead(), which should be `true` on //! success and `false` on failure. virtual bool Read(Delegate* delegate) const = 0; + + //! \brief Creates a new MemorySnapshot based on merging this one with \a + //! other. + //! + //! The ranges described by the two snapshots must either overlap or abut, and + //! must be of the same concrete type. + //! + //! \return A newly allocated MemorySnapshot representing the merged range, or + //! `nullptr` with an error logged. + virtual const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const = 0; }; +//! \brief Given two memory snapshots, checks if they're overlapping or +//! abutting, and if so, returns the result of merging the two ranges. +//! +//! This function is useful to implement +//! MemorySnapshot::MergeWithOtherSnapshot(). +//! +//! \param[in] a The first range. Must have Size() > 0. +//! \param[in] b The second range. Must have Size() > 0. +//! \param[out] merged The resulting merged range. May be `nullptr` if only a +//! characterization of the ranges is desired. +//! +//! \return `true` if the input ranges overlap or abut, with \a merged filled +//! out, otherwise, `false` with an error logged if \a log is `true`. +bool LoggingDetermineMergedRange(const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange* merged); + +//! \brief The same as LoggingDetermineMergedRange but with no errors logged. +//! +//! \sa LoggingDetermineMergedRange +bool DetermineMergedRange(const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange* merged); + +namespace internal { + +//! \brief A standard implementation of MemorySnapshot::MergeWithOtherSnapshot() +//! for concrete MemorySnapshot implementations that use a +//! `process_reader_`. +template +const MemorySnapshot* MergeWithOtherSnapshotImpl(const T* self, + const MemorySnapshot* other) { + const T* other_as_memory_snapshot_concrete = + reinterpret_cast(other); + if (self->process_reader_ != + other_as_memory_snapshot_concrete->process_reader_) { + LOG(ERROR) << "different process_reader_ for snapshots"; + return nullptr; + } + CheckedRange merged(0, 0); + if (!LoggingDetermineMergedRange(self, other, &merged)) + return nullptr; + + std::unique_ptr result(new T()); + result->Initialize(self->process_reader_, merged.base(), merged.size()); + return result.release(); +} + +} // namespace internal } // namespace crashpad #endif // CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_ diff --git a/snapshot/memory_snapshot_test.cc b/snapshot/memory_snapshot_test.cc new file mode 100644 index 00000000..91e847fb --- /dev/null +++ b/snapshot/memory_snapshot_test.cc @@ -0,0 +1,152 @@ +// Copyright 2018 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/memory_snapshot.h" + +#include "base/macros.h" +#include "gtest/gtest.h" +#include "snapshot/test/test_memory_snapshot.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(DetermineMergedRange, NonOverlapping) { + TestMemorySnapshot a; + TestMemorySnapshot b; + a.SetAddress(0); + a.SetSize(100); + b.SetAddress(200); + b.SetSize(50); + CheckedRange range(0, 0); + EXPECT_FALSE(DetermineMergedRange(&a, &b, &range)); + EXPECT_FALSE(DetermineMergedRange(&b, &a, &range)); + + a.SetSize(199); + EXPECT_FALSE(DetermineMergedRange(&a, &b, &range)); +} + +TEST(DetermineMergedRange, Empty) { + TestMemorySnapshot a; + TestMemorySnapshot b; + a.SetAddress(100); + a.SetSize(0); + b.SetAddress(200); + b.SetSize(20); + + CheckedRange range(0, 0); + // Empty are invalid. + EXPECT_FALSE(DetermineMergedRange(&a, &b, &range)); + EXPECT_FALSE(DetermineMergedRange(&b, &a, &range)); + EXPECT_FALSE(DetermineMergedRange(&a, &a, &range)); +} + +TEST(DetermineMergedRange, Abutting) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(0); + a.SetSize(10); + b.SetAddress(10); + b.SetSize(20); + + CheckedRange range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(0u, range.base()); + EXPECT_EQ(30u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(0u, range.base()); + EXPECT_EQ(30u, range.size()); +} + +TEST(DetermineMergedRange, TypicalOverlapping) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(10); + a.SetSize(100); + b.SetAddress(50); + b.SetSize(100); + + CheckedRange range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(10u, range.base()); + EXPECT_EQ(140u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(10u, range.base()); + EXPECT_EQ(140u, range.size()); +} + +TEST(DetermineMergedRange, OneFullyInsideAnother) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(20); + a.SetSize(100); + b.SetAddress(5); + b.SetSize(200); + + CheckedRange range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(200u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(200u, range.size()); +} + +TEST(DetermineMergedRange, SameStart) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(5); + a.SetSize(100); + b.SetAddress(5); + b.SetSize(50); + + CheckedRange range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(100u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(100u, range.size()); +} + +TEST(DetermineMergedRange, SameEnd) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(5); + a.SetSize(100); + b.SetAddress(70); + b.SetSize(35); + + CheckedRange range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(100u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(100u, range.size()); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index caa5f0e0..da192341 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -108,6 +108,7 @@ 'mac/system_snapshot_mac.h', 'mac/thread_snapshot_mac.cc', 'mac/thread_snapshot_mac.h', + 'memory_snapshot.cc', 'memory_snapshot.h', 'minidump/minidump_annotation_reader.cc', 'minidump/minidump_annotation_reader.h', diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index 109895af..277bf6e8 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -53,6 +53,7 @@ 'type': 'executable', 'dependencies': [ 'crashpad_snapshot_test_both_dt_hash_styles', + 'crashpad_snapshot_test_lib', 'crashpad_snapshot_test_module', 'crashpad_snapshot_test_module_large', 'crashpad_snapshot_test_module_small', @@ -72,6 +73,7 @@ 'sources': [ 'api/module_annotations_win_test.cc', 'cpu_context_test.cc', + 'memory_snapshot_test.cc', 'crashpad_info_client_options_test.cc', 'crashpad_types/crashpad_info_reader_test.cc', 'crashpad_types/image_annotation_reader_test.cc', diff --git a/snapshot/test/test_memory_snapshot.cc b/snapshot/test/test_memory_snapshot.cc index 5201be06..a3e9ebd9 100644 --- a/snapshot/test/test_memory_snapshot.cc +++ b/snapshot/test/test_memory_snapshot.cc @@ -14,6 +14,7 @@ #include "snapshot/test/test_memory_snapshot.h" +#include #include namespace crashpad { @@ -43,5 +44,18 @@ bool TestMemorySnapshot::Read(Delegate* delegate) const { return delegate->MemorySnapshotDelegateRead(&buffer[0], size_); } +const MemorySnapshot* TestMemorySnapshot::MergeWithOtherSnapshot( + const MemorySnapshot* other) const { + CheckedRange merged(0, 0); + if (!DetermineMergedRange(this, other, &merged)) + return nullptr; + + std::unique_ptr result(new TestMemorySnapshot()); + result->SetAddress(merged.base()); + result->SetSize(merged.size()); + result->SetValue(value_); + return result.release(); +} + } // namespace test } // namespace crashpad diff --git a/snapshot/test/test_memory_snapshot.h b/snapshot/test/test_memory_snapshot.h index 80b45c66..ae17ee48 100644 --- a/snapshot/test/test_memory_snapshot.h +++ b/snapshot/test/test_memory_snapshot.h @@ -45,6 +45,8 @@ class TestMemorySnapshot final : public MemorySnapshot { uint64_t Address() const override; size_t Size() const override; bool Read(Delegate* delegate) const override; + const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const override; private: uint64_t address_; diff --git a/snapshot/win/memory_snapshot_win.cc b/snapshot/win/memory_snapshot_win.cc index 666cb936..17e8ac11 100644 --- a/snapshot/win/memory_snapshot_win.cc +++ b/snapshot/win/memory_snapshot_win.cc @@ -16,7 +16,6 @@ #include "snapshot/win/memory_snapshot_win.h" - namespace crashpad { namespace internal { @@ -67,5 +66,10 @@ bool MemorySnapshotWin::Read(Delegate* delegate) const { return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); } +const MemorySnapshot* MemorySnapshotWin::MergeWithOtherSnapshot( + const MemorySnapshot* other) const { + return MergeWithOtherSnapshotImpl(this, other); +} + } // namespace internal } // namespace crashpad diff --git a/snapshot/win/memory_snapshot_win.h b/snapshot/win/memory_snapshot_win.h index 04d11842..ebc878b8 100644 --- a/snapshot/win/memory_snapshot_win.h +++ b/snapshot/win/memory_snapshot_win.h @@ -52,8 +52,15 @@ class MemorySnapshotWin final : public MemorySnapshot { uint64_t Address() const override; size_t Size() const override; bool Read(Delegate* delegate) const override; + const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const override; private: + template + friend const MemorySnapshot* MergeWithOtherSnapshotImpl( + const T* self, + const MemorySnapshot* other); + ProcessReaderWin* process_reader_; // weak uint64_t address_; size_t size_; From 885fb47a0de6e757cd0f427648534a9e653d879c Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 2 Feb 2018 12:47:44 -0800 Subject: [PATCH 139/326] Make CrashpadClient::DumpAndCrashTargetProcess static Noticed during discussion for https://chromium-review.googlesource.com/c/chromium/src/+/896638 and the linked bug that there's no need for this to be an instance method. Make it static as it's easier to use. Bug: chromium:806661 Change-Id: I24b893e58a47b5256b3b1b43dd5f1fc2d7cc6be8 Reviewed-on: https://chromium-review.googlesource.com/898439 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- client/crashpad_client.h | 6 +++--- client/crashpad_client_win.cc | 3 ++- handler/win/crash_other_program.cc | 9 +++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 1f5bae65..7cf2eb8a 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -304,9 +304,9 @@ class CrashpadClient { //! used as the exception code in the exception record. //! //! \return `true` if the exception was triggered successfully. - bool DumpAndCrashTargetProcess(HANDLE process, - HANDLE blame_thread, - DWORD exception_code) const; + static bool DumpAndCrashTargetProcess(HANDLE process, + HANDLE blame_thread, + DWORD exception_code); enum : uint32_t { //! \brief The exception code (roughly "Client called") used when diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc index 7d9c1fb2..dd25c9ef 100644 --- a/client/crashpad_client_win.cc +++ b/client/crashpad_client_win.cc @@ -790,9 +790,10 @@ void CrashpadClient::DumpAndCrash(EXCEPTION_POINTERS* exception_pointers) { UnhandledExceptionHandler(exception_pointers); } +// static bool CrashpadClient::DumpAndCrashTargetProcess(HANDLE process, HANDLE blame_thread, - DWORD exception_code) const { + DWORD exception_code) { // Confirm we're on Vista or later. const DWORD version = GetVersion(); const DWORD major_version = LOBYTE(LOWORD(version)); diff --git a/handler/win/crash_other_program.cc b/handler/win/crash_other_program.cc index 2b62aea2..55c06994 100644 --- a/handler/win/crash_other_program.cc +++ b/handler/win/crash_other_program.cc @@ -33,7 +33,7 @@ namespace { constexpr DWORD kCrashAndDumpTargetExitCode = 0xdeadbea7; -bool CrashAndDumpTarget(const CrashpadClient& client, HANDLE process) { +bool CrashAndDumpTarget(HANDLE process) { DWORD target_pid = GetProcessId(process); ScopedFileHANDLE thread_snap(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)); @@ -61,7 +61,7 @@ bool CrashAndDumpTarget(const CrashpadClient& client, HANDLE process) { PLOG(ERROR) << "OpenThread"; return false; } - if (!client.DumpAndCrashTargetProcess( + if (!CrashpadClient::DumpAndCrashTargetProcess( process, thread.get(), kCrashAndDumpTargetExitCode)) { return false; } @@ -109,11 +109,12 @@ int CrashOtherProgram(int argc, wchar_t* argv[]) { DWORD expect_exit_code; if (argc == 3 && wcscmp(argv[2], L"noexception") == 0) { expect_exit_code = CrashpadClient::kTriggeredExceptionCode; - if (!client.DumpAndCrashTargetProcess(child.process_handle(), 0, 0)) + if (!CrashpadClient::DumpAndCrashTargetProcess( + child.process_handle(), 0, 0)) return EXIT_FAILURE; } else { expect_exit_code = kCrashAndDumpTargetExitCode; - if (!CrashAndDumpTarget(client, child.process_handle())) { + if (!CrashAndDumpTarget(child.process_handle())) { return EXIT_FAILURE; } } From 0dd11f63411204cfedfa52bf0617573fa80abb00 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 2 Feb 2018 14:04:48 -0800 Subject: [PATCH 140/326] gn: Guard addition of ELF test target Fix Windows- and Mac-in-Chromium GN builds. Bug: crashpad:79 Change-Id: I952f364ed679a13f656a8db18fb4d1fbf5858c17 Reviewed-on: https://chromium-review.googlesource.com/900206 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- snapshot/BUILD.gn | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index ad07e23a..e1d24002 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -354,7 +354,6 @@ source_set("snapshot_test") { ":crashpad_snapshot_test_module", ":crashpad_snapshot_test_module_large", ":crashpad_snapshot_test_module_small", - ":crashpad_snapshot_test_both_dt_hash_styles", ] if (crashpad_is_mac) { @@ -370,6 +369,10 @@ source_set("snapshot_test") { libs = [ "dl" ] } + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + data_deps += [ ":crashpad_snapshot_test_both_dt_hash_styles" ] + } + if (crashpad_is_win) { cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union From a8ecdbc973d969a87aaa2efffb1668efb52b799d Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 2 Feb 2018 18:51:46 -0800 Subject: [PATCH 141/326] Updates to support -Wimplicit-fallthrough https://chromium-review.googlesource.com/c/chromium/mini_chromium/+/899847 turns the warning on. This adds one annotation, and fixes one bug. Includes mini_chromium roll: .../mini_chromium$ git log 5fcfa43c1587b94132e24782579350cb8266b990..3b953302848580cdf23b50402befc0ae09d03ff9 --oneline 3b95330 (HEAD, origin/master, origin/HEAD) Add -Wimplicit-fallthrough when building on clang Bug: chromium:807632 Change-Id: I2f3ddca0228e52013844cb8d78d10cb359e851d0 Reviewed-on: https://chromium-review.googlesource.com/900317 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- DEPS | 2 +- handler/linux/exception_handler_server.cc | 3 ++- minidump/minidump_context_writer.cc | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 994bed87..6936f9f2 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '5fcfa43c1587b94132e24782579350cb8266b990', + '3b953302848580cdf23b50402befc0ae09d03ff9', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/handler/linux/exception_handler_server.cc b/handler/linux/exception_handler_server.cc index 3369c4f8..6bb55cda 100644 --- a/handler/linux/exception_handler_server.cc +++ b/handler/linux/exception_handler_server.cc @@ -24,6 +24,7 @@ #include +#include "base/compiler_specific.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "base/strings/string_number_conversions.h" @@ -150,7 +151,7 @@ class PtraceStrategyDeciderImpl : public PtraceStrategyDecider { if (HaveCapSysPtrace()) { return Strategy::kDirectPtrace; } - // fallthrough + FALLTHROUGH; case PtraceScope::kNoAttach: LOG(WARNING) << "no ptrace"; return Strategy::kNoPtrace; diff --git a/minidump/minidump_context_writer.cc b/minidump/minidump_context_writer.cc index fbf291ea..2fa2e53e 100644 --- a/minidump/minidump_context_writer.cc +++ b/minidump/minidump_context_writer.cc @@ -84,6 +84,7 @@ MinidumpContextWriter::CreateFromSnapshot(const CPUContext* context_snapshot) { context = std::make_unique(); reinterpret_cast(context.get()) ->InitializeFromSnapshot(context_snapshot->arm64); + break; } default: { From 14dbd3531d9897234991bd80c3d3af53856b295e Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 6 Feb 2018 10:57:23 -0800 Subject: [PATCH 142/326] gn win: Get main test binaries building - default to subsystem:console - don't build posix/timezone.* - add some missing libs This gets all the main binaries building and running. Most configs pass, but there's some offsets that seem different in some builds; need to investigate more. Additionally, the binaries used by end_to_end_test.py aren't yet built, so that script fails. Includes mini_chromium roll to 46eeaf9: 46eea49 gn win: Add debug info and pdb to cc/cxx 902a29f gn win: Various fixes towards making GN build work Bug: crashpad:79 Change-Id: Ie56a469b84bed7b0330172cec9f1a8aeb95f702e Reviewed-on: https://chromium-review.googlesource.com/902403 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- DEPS | 2 +- build/BUILDCONFIG.gn | 6 ++++-- handler/BUILD.gn | 11 +++++++++-- snapshot/BUILD.gn | 9 +++++++-- test/BUILD.gn | 9 ++++++++- util/BUILD.gn | 13 ++++++++----- 6 files changed, 37 insertions(+), 13 deletions(-) diff --git a/DEPS b/DEPS index 6936f9f2..e60b57a9 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '3b953302848580cdf23b50402befc0ae09d03ff9', + '46eea49ed02ecad04a296794ab25fbdd85202558', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn index 67d56ae4..82bcf73b 100644 --- a/build/BUILDCONFIG.gn +++ b/build/BUILDCONFIG.gn @@ -54,8 +54,10 @@ _default_configs = [ ] _default_executable_configs = - _default_configs + - [ "//third_party/mini_chromium/mini_chromium/build:executable" ] + _default_configs + [ + "//third_party/mini_chromium/mini_chromium/build:executable", + "//third_party/mini_chromium/mini_chromium/build:win_console", + ] set_defaults("source_set") { configs = _default_configs diff --git a/handler/BUILD.gn b/handler/BUILD.gn index 9c337697..ffc9d9b0 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -147,8 +147,15 @@ executable("crashpad_handler") { } if (crashpad_is_win) { - configs -= [ "//build/config/win:console" ] - configs += [ "//build/config/win:windowed" ] + if (crashpad_is_in_chromium) { + configs -= [ "//build/config/win:console" ] + configs += [ "//build/config/win:windowed" ] + } else { + configs -= + [ "//third_party/mini_chromium/mini_chromium/build:win_console" ] + configs += + [ "//third_party/mini_chromium/mini_chromium/build:win_windowed" ] + } } } diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index e1d24002..785f7970 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -47,8 +47,6 @@ static_library("snapshot") { "minidump/process_snapshot_minidump.cc", "minidump/process_snapshot_minidump.h", "module_snapshot.h", - "posix/timezone.cc", - "posix/timezone.h", "process_snapshot.h", "snapshot_constants.h", "system_snapshot.h", @@ -57,6 +55,13 @@ static_library("snapshot") { "unloaded_module_snapshot.h", ] + if (crashpad_is_posix) { + sources += [ + "posix/timezone.cc", + "posix/timezone.h", + ] + } + if (crashpad_is_mac) { sources += [ "mac/cpu_context_mac.cc", diff --git a/test/BUILD.gn b/test/BUILD.gn index f7541749..0ddde6e1 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -96,7 +96,6 @@ static_library("test") { if (crashpad_is_fuchsia) { sources += [ "multiprocess_exec_fuchsia.cc" ] - libs = [ "launchpad" ] } public_configs = [ "..:crashpad_config" ] @@ -121,6 +120,14 @@ static_library("test") { "../snapshot", ] } + + if (crashpad_is_win) { + libs = [ "shell32.lib" ] + } + + if (crashpad_is_fuchsia) { + libs = [ "launchpad" ] + } } source_set("test_test") { diff --git a/util/BUILD.gn b/util/BUILD.gn index 13a5da80..2f376110 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -407,11 +407,11 @@ static_library("util") { } if (crashpad_is_win) { - cflags = [ - "/wd4201", # nonstandard extension used : nameless struct/union. - "/wd4577", # 'noexcept' used with no exception handling mode specified. + libs = [ + "mincore.lib", + "user32.lib", + "winhttp.lib", ] - libs = [ "winhttp.lib" ] if (current_cpu == "x86") { asmflags = [ "/safeseh" ] @@ -568,7 +568,10 @@ source_set("util_test") { } if (crashpad_is_win) { - libs = [ "rpcrt4.lib" ] + libs = [ + "rpcrt4.lib", + "dbghelp.lib", + ] data_deps = [ ":crashpad_util_test_process_info_test_child", ":crashpad_util_test_safe_terminate_process_test_child", From 6cf4f928eb08193e0a7fecb9e3d5b19ee4ee00ba Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 5 Feb 2018 15:25:30 -0800 Subject: [PATCH 143/326] gn win: Add auxiliary test binaries used by end_to_end_test.py Requires https://chromium-review.googlesource.com/c/chromium/mini_chromium/+/902407. With this, all tests pass in Windows x64 Debug, other configs TBD. Bug: crashpad:79 Change-Id: I3f91dbb6a239b3d5f2cd3a7ef706b045af218442 Reviewed-on: https://chromium-review.googlesource.com/902463 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- handler/BUILD.gn | 102 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/handler/BUILD.gn b/handler/BUILD.gn index ffc9d9b0..728290a6 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -206,4 +206,106 @@ if (crashpad_is_win) { "$root_out_dir/crashpad_handler.com", ] } + + executable("crash_other_program") { + testonly = true + + sources = [ + "win/crash_other_program.cc", + ] + + deps = [ + "../client", + "../test", + "../third_party/gtest:gtest", + "../third_party/mini_chromium:base", + ] + } + + executable("crashy_program") { + testonly = true + + sources = [ + "win/crashy_test_program.cc", + ] + + deps = [ + "../client", + "../third_party/mini_chromium:base", + ] + } + + executable("crashy_signal") { + testonly = true + + sources = [ + "win/crashy_signal.cc", + ] + + deps = [ + "../client", + "../third_party/mini_chromium:base", + ] + } + + executable("fake_handler_that_crashes_at_startup") { + testonly = true + + sources = [ + "win/fake_handler_that_crashes_at_startup.cc", + ] + } + + executable("hanging_program") { + testonly = true + + sources = [ + "win/hanging_program.cc", + ] + + deps = [ + "../client", + "../third_party/mini_chromium:base", + ] + } + + loadable_module("loader_lock_dll") { + testonly = true + + sources = [ + "win/loader_lock_dll.cc", + ] + } + + executable("self_destroying_program") { + testonly = true + + sources = [ + "win/self_destroying_test_program.cc", + ] + + deps = [ + "../client", + "../compat", + "../snapshot", + "../third_party/mini_chromium:base", + ] + } + + if (current_cpu == "x86") { + # Cannot create an x64 DLL with embedded debug info. + executable("crashy_z7_loader") { + testonly = true + + sources = [ + "win/crashy_test_z7_loader.cc", + ] + + deps = [ + "../client", + "../test", + "../third_party/mini_chromium:base", + ] + } + } } From 36679d572ba0b5bd363ee3ea8d02e9946c39e07d Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 6 Feb 2018 11:34:20 -0800 Subject: [PATCH 144/326] win: Don't assume offsets the same when kDoesNotObserveDaylightSavingTime kDoesNotObserveDaylightSavingTime can indicate only that the standard/daylight transition is not automatic, as opposed to it not existing at all. Bug: crashpad:214 Change-Id: Ib7016806e79465a6dde605dd667b75a802e1b6c5 Reviewed-on: https://chromium-review.googlesource.com/904767 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- snapshot/win/system_snapshot_win_test.cc | 30 +++++++++++++----------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/snapshot/win/system_snapshot_win_test.cc b/snapshot/win/system_snapshot_win_test.cc index 4c994971..c87a6a6a 100644 --- a/snapshot/win/system_snapshot_win_test.cc +++ b/snapshot/win/system_snapshot_win_test.cc @@ -134,22 +134,24 @@ TEST_F(SystemSnapshotWinTest, TimeZone) { EXPECT_EQ(standard_offset_seconds % (15 * 60), 0) << "standard_offset_seconds " << standard_offset_seconds; - if (dst_status == SystemSnapshot::kDoesNotObserveDaylightSavingTime) { - EXPECT_EQ(daylight_offset_seconds, standard_offset_seconds); - EXPECT_EQ(daylight_name, standard_name); - } else { - EXPECT_EQ(daylight_offset_seconds % (15 * 60), 0) - << "daylight_offset_seconds " << daylight_offset_seconds; + // dst_status of kDoesNotObserveDaylightSavingTime can mean only that the + // adjustment is not automatic, as opposed to daylight/standard differences + // not existing at all. So it cannot be asserted that the two offsets are the + // same in that case. - // In contemporary usage, dst_delta_seconds will almost always be one hour, - // except for Lord Howe Island, Australia, which uses a 30-minute delta. - // Throughout history, other variations existed. See - // https://www.timeanddate.com/time/dst/. - int dst_delta_seconds = daylight_offset_seconds - standard_offset_seconds; - if (dst_delta_seconds != 60 * 60 && dst_delta_seconds != 30 * 60) { - FAIL() << "dst_delta_seconds " << dst_delta_seconds; - } + EXPECT_EQ(daylight_offset_seconds % (15 * 60), 0) + << "daylight_offset_seconds " << daylight_offset_seconds; + // In contemporary usage, dst_delta_seconds will almost always be one hour, + // except for Lord Howe Island, Australia, which uses a 30-minute delta. + // Throughout history, other variations existed. See + // https://www.timeanddate.com/time/dst/. + int dst_delta_seconds = daylight_offset_seconds - standard_offset_seconds; + if (dst_delta_seconds != 60 * 60 && dst_delta_seconds != 30 * 60) { + FAIL() << "dst_delta_seconds " << dst_delta_seconds; + } + + if (dst_status != SystemSnapshot::kDoesNotObserveDaylightSavingTime) { EXPECT_NE(standard_name, daylight_name); } } From 9ab4fbf1e1a7a9590f47bdb85b3c616809c6412c Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 6 Feb 2018 13:21:45 -0800 Subject: [PATCH 145/326] win: Improve child crash location test In setting up the gn build, slightly different optimization settings were applied for release builds. This caused a couple things to happen, 1) the sketchy noinline declspec was ignored, and 2) the distance between reading the IP and the actual crash exceeded the tolerance of 64 bytes in the parent. To make the test more robust to this, use CaptureContext() (I think our improved version didn't exist at the time the tests was originally written). Also, switch from crashpad::CheckedWriteFile to Windows' WriteFile(), which avoids inlining a whole lot of code at that point. The return value is not checked, but the next thing that happens is that the function crashes unconditionally, so this does not seem like a huge problem. Bug: crashpad:79 Change-Id: I8193d8ce8b01e1533c16b207813c36d6d6113d89 Reviewed-on: https://chromium-review.googlesource.com/902693 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- client/simulate_crash_win.h | 11 +++--- .../crashpad_snapshot_test_crashing_child.cc | 34 ++++++++++++------- ...pad_snapshot_test_dump_without_crashing.cc | 32 ++++++++++------- snapshot/win/exception_snapshot_win_test.cc | 4 +-- 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/client/simulate_crash_win.h b/client/simulate_crash_win.h index a20f3dad..3c5bdf18 100644 --- a/client/simulate_crash_win.h +++ b/client/simulate_crash_win.h @@ -23,11 +23,12 @@ //! \file //! \brief Captures the CPU context and captures a dump without an exception. -#define CRASHPAD_SIMULATE_CRASH() \ - do { \ - CONTEXT context; \ - crashpad::CaptureContext(&context); \ - crashpad::CrashpadClient::DumpWithoutCrash(context); \ +#define CRASHPAD_SIMULATE_CRASH() \ + do { \ + /* Not "context" to avoid variable shadowing warnings. */ \ + CONTEXT simulate_crash_cpu_context; \ + crashpad::CaptureContext(&simulate_crash_cpu_context); \ + crashpad::CrashpadClient::DumpWithoutCrash(simulate_crash_cpu_context); \ } while (false) #endif // CRASHPAD_CLIENT_SIMULATE_CRASH_WIN_H_ diff --git a/snapshot/win/crashpad_snapshot_test_crashing_child.cc b/snapshot/win/crashpad_snapshot_test_crashing_child.cc index 146c66a1..c06663a2 100644 --- a/snapshot/win/crashpad_snapshot_test_crashing_child.cc +++ b/snapshot/win/crashpad_snapshot_test_crashing_child.cc @@ -15,20 +15,11 @@ #include #include -#include "base/files/file_path.h" #include "base/logging.h" +#include "build/build_config.h" #include "client/crashpad_client.h" -#include "util/file/file_io.h" -#include "util/misc/from_pointer_cast.h" #include "util/win/address_types.h" - -namespace { - -__declspec(noinline) crashpad::WinVMAddress CurrentAddress() { - return crashpad::FromPointerCast(_ReturnAddress()); -} - -} // namespace +#include "util/win/capture_context.h" int wmain(int argc, wchar_t* argv[]) { CHECK_EQ(argc, 2); @@ -38,8 +29,25 @@ int wmain(int argc, wchar_t* argv[]) { HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle"; - crashpad::WinVMAddress break_address = CurrentAddress(); - crashpad::CheckedWriteFile(out, &break_address, sizeof(break_address)); + + CONTEXT context; + crashpad::CaptureContext(&context); +#if defined(ARCH_CPU_64_BITS) + crashpad::WinVMAddress break_address = context.Rip; +#else + crashpad::WinVMAddress break_address = context.Eip; +#endif + + // This does not used CheckedWriteFile() because at high optimization + // settings, a lot of logging code can be inlined, causing there to be a large + // number of instructions between where the IP is captured and the actual + // __debugbreak(). Instead call Windows' WriteFile() to minimize the amount of + // code here. Because the next line is going to crash in any case, there's + // minimal difference in behavior aside from an indication of what broke when + // the other end experiences a ReadFile() error. + DWORD bytes_written; + WriteFile( + out, &break_address, sizeof(break_address), &bytes_written, nullptr); __debugbreak(); diff --git a/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc b/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc index f55f503b..3bf13712 100644 --- a/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc +++ b/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc @@ -18,17 +18,8 @@ #include "base/logging.h" #include "client/crashpad_client.h" #include "client/simulate_crash.h" -#include "util/file/file_io.h" -#include "util/misc/from_pointer_cast.h" #include "util/win/address_types.h" - -namespace { - -__declspec(noinline) crashpad::WinVMAddress CurrentAddress() { - return crashpad::FromPointerCast(_ReturnAddress()); -} - -} // namespace +#include "util/win/capture_context.h" int wmain(int argc, wchar_t* argv[]) { CHECK_EQ(argc, 2); @@ -38,8 +29,25 @@ int wmain(int argc, wchar_t* argv[]) { HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle"; - crashpad::WinVMAddress current_address = CurrentAddress(); - crashpad::CheckedWriteFile(out, ¤t_address, sizeof(current_address)); + + CONTEXT context; + crashpad::CaptureContext(&context); +#if defined(ARCH_CPU_64_BITS) + crashpad::WinVMAddress break_address = context.Rip; +#else + crashpad::WinVMAddress break_address = context.Eip; +#endif + + // This does not used CheckedWriteFile() because at high optimization + // settings, a lot of logging code can be inlined, causing there to be a large + // number of instructions between where the IP is captured and the actual + // __debugbreak(). Instead call Windows' WriteFile() to minimize the amount of + // code here. Because the next line is going to crash in any case, there's + // minimal difference in behavior aside from an indication of what broke when + // the other end experiences a ReadFile() error. + DWORD bytes_written; + WriteFile( + out, &break_address, sizeof(break_address), &bytes_written, nullptr); CRASHPAD_SIMULATE_CRASH(); diff --git a/snapshot/win/exception_snapshot_win_test.cc b/snapshot/win/exception_snapshot_win_test.cc index a1ab8c65..376de7c9 100644 --- a/snapshot/win/exception_snapshot_win_test.cc +++ b/snapshot/win/exception_snapshot_win_test.cc @@ -103,7 +103,7 @@ class CrashingDelegate : public ExceptionHandlerServer::Delegate { // Verify the exception happened at the expected location with a bit of // slop space to allow for reading the current PC before the exception // happens. See TestCrashingChild(). - constexpr uint64_t kAllowedOffset = 64; + constexpr uint64_t kAllowedOffset = 100; EXPECT_GT(snapshot.Exception()->ExceptionAddress(), break_near_); EXPECT_LT(snapshot.Exception()->ExceptionAddress(), break_near_ + kAllowedOffset); @@ -204,7 +204,7 @@ class SimulateDelegate : public ExceptionHandlerServer::Delegate { // Verify the dump was captured at the expected location with some slop // space. - constexpr uint64_t kAllowedOffset = 64; + constexpr uint64_t kAllowedOffset = 100; EXPECT_GT(snapshot.Exception()->Context()->InstructionPointer(), dump_near_); EXPECT_LT(snapshot.Exception()->Context()->InstructionPointer(), From 798f6540435f2bffcbdb893e57375eff38bdff4e Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 6 Feb 2018 15:22:35 -0800 Subject: [PATCH 146/326] gn win: Use new toolchain names to support both x86 and x64 Rolls mini_chromium to include: 75b5697 gn win: Template toolchain to support x86 and x64 428a555 gn win: Automatically find location of Visual Studio Bug: crashpad:79 Change-Id: Ie7ff10f8b4853071028e3a9580e5be8e5ca5c0ec Reviewed-on: https://chromium-review.googlesource.com/905625 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- DEPS | 2 +- build/BUILDCONFIG.gn | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index e60b57a9..dab3803d 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '46eea49ed02ecad04a296794ab25fbdd85202558', + '75b56976221464e814d0015ef4d2603161614e58', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn index 82bcf73b..0e6da165 100644 --- a/build/BUILDCONFIG.gn +++ b/build/BUILDCONFIG.gn @@ -35,7 +35,7 @@ if (current_cpu == "") { if (current_os == "win") { set_default_toolchain( - "//third_party/mini_chromium/mini_chromium/build:msvc_toolchain") + "//third_party/mini_chromium/mini_chromium/build:msvc_toolchain_$current_cpu") } else { set_default_toolchain( "//third_party/mini_chromium/mini_chromium/build:gcc_like_toolchain") From 83a83c5b00501591438acbc1b4002e408ec8857b Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 7 Feb 2018 11:52:17 -0800 Subject: [PATCH 147/326] Roll mini_chromium to e7e8237 Includes: e7e8237 Don't run win_helper.py get-visual-studio-data on non-Windows 387f001 gn win: Support detection of VS2017 Bug: crashpad:79 Change-Id: I709b6a7f1b64baa3b5ab37a1e5b90c2c529c14de Reviewed-on: https://chromium-review.googlesource.com/906971 Reviewed-by: Mark Mentovai --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index dab3803d..4db917a5 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '75b56976221464e814d0015ef4d2603161614e58', + 'e7e8237132fd133bd3521ddf6966b800db6a7bca', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', From a483e2c599b865d0268d2afd909af284a8ac4d62 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 8 Feb 2018 14:17:59 -0800 Subject: [PATCH 148/326] Fix names of Windows trybots after server-side rename Bug: crashpad:79 Change-Id: I1836b1e5aec8e772e9ff48a52b0ad755bb1894ab Reviewed-on: https://chromium-review.googlesource.com/909861 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- infra/config/cq.cfg | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg index d00ef141..83be592f 100644 --- a/infra/config/cq.cfg +++ b/infra/config/cq.cfg @@ -35,14 +35,12 @@ verifiers { name: "master.client.crashpad" builders { name: "crashpad_try_mac_dbg" } builders { name: "crashpad_try_mac_rel" } - builders { name: "crashpad_try_win_x64_dbg" } - builders { name: "crashpad_try_win_x64_rel" } + builders { name: "crashpad_try_win_dbg" } + builders { name: "crashpad_try_win_rel" } # https://crbug.com/743139 - disabled until we can move these to swarming, # at which point we can just remove them. #builders { name: "crashpad_try_win_x86_dbg" } #builders { name: "crashpad_try_win_x86_rel" } - builders { name: "crashpad_try_win_x86_wow64_dbg" } - builders { name: "crashpad_try_win_x86_wow64_rel" } } } } From 38b20ca57efce584afd9c3de206b3ae3dda98b3d Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 8 Feb 2018 16:25:22 -0800 Subject: [PATCH 149/326] Relocate CaptureContext to misc and implement on Linux Previously, the mac version was under client/ and win under util/win/. This cl brings them all together under util/misc/ and combines common test code. Bug: crashpad:30 Change-Id: Idf0d0158b969d5aa9802dfc8c21f73041b2bcc6c Reviewed-on: https://chromium-review.googlesource.com/907755 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- client/BUILD.gn | 7 +- client/capture_context_mac.h | 48 --- client/capture_context_mac_test.cc | 158 --------- client/client.gyp | 7 - client/client_test.gyp | 1 - client/crashpad_client_win.cc | 2 +- client/simulate_crash_mac.h | 2 +- client/simulate_crash_win.h | 2 +- handler/crashpad_handler_test.cc | 2 +- .../crashpad_snapshot_test_crashing_child.cc | 2 +- ...pad_snapshot_test_dump_without_crashing.cc | 2 +- snapshot/win/process_reader_win.cc | 2 +- util/BUILD.gn | 14 +- util/misc/capture_context.h | 81 +++++ util/{win => misc}/capture_context_broken.cc | 4 +- util/misc/capture_context_linux.S | 324 ++++++++++++++++++ {client => util/misc}/capture_context_mac.S | 2 +- util/misc/capture_context_test.cc | 95 +++++ util/misc/capture_context_test_util.h | 41 +++ util/misc/capture_context_test_util_linux.cc | 62 ++++ util/misc/capture_context_test_util_mac.cc | 84 +++++ .../capture_context_test_util_win.cc} | 88 +---- .../capture_context_win.asm} | 0 util/util.gyp | 12 +- util/util_test.gyp | 7 +- util/win/capture_context.h | 47 --- 26 files changed, 734 insertions(+), 362 deletions(-) delete mode 100644 client/capture_context_mac.h delete mode 100644 client/capture_context_mac_test.cc create mode 100644 util/misc/capture_context.h rename util/{win => misc}/capture_context_broken.cc (90%) create mode 100644 util/misc/capture_context_linux.S rename {client => util/misc}/capture_context_mac.S (99%) create mode 100644 util/misc/capture_context_test.cc create mode 100644 util/misc/capture_context_test_util.h create mode 100644 util/misc/capture_context_test_util_linux.cc create mode 100644 util/misc/capture_context_test_util_mac.cc rename util/{win/capture_context_test.cc => misc/capture_context_test_util_win.cc} (53%) rename util/{win/capture_context.asm => misc/capture_context_win.asm} (100%) delete mode 100644 util/win/capture_context.h diff --git a/client/BUILD.gn b/client/BUILD.gn index f054e7ca..92027a5b 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -36,8 +36,6 @@ static_library("client") { if (crashpad_is_mac) { sources += [ - "capture_context_mac.S", - "capture_context_mac.h", "crash_report_database_mac.mm", "crashpad_client_mac.cc", "simulate_crash_mac.cc", @@ -92,10 +90,7 @@ source_set("client_test") { ] if (crashpad_is_mac) { - sources += [ - "capture_context_mac_test.cc", - "simulate_crash_mac_test.cc", - ] + sources += [ "simulate_crash_mac_test.cc" ] } if (crashpad_is_win) { diff --git a/client/capture_context_mac.h b/client/capture_context_mac.h deleted file mode 100644 index 74e440ed..00000000 --- a/client/capture_context_mac.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2014 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_CLIENT_CAPTURE_CONTEXT_MAC_H_ -#define CRASHPAD_CLIENT_CAPTURE_CONTEXT_MAC_H_ - -#include - -#include "build/build_config.h" - -namespace crashpad { - -#if defined(ARCH_CPU_X86_FAMILY) -using NativeCPUContext = x86_thread_state; -#endif - -//! \brief Saves the CPU context. -//! -//! The CPU context will be captured as accurately and completely as possible, -//! containing an atomic snapshot at the point of this function’s return. This -//! function does not modify any registers. -//! -//! \param[out] cpu_context The structure to store the context in. -//! -//! \note On x86_64, the value for `%%rdi` will be populated with the address of -//! this function’s argument, as mandated by the ABI. If the value of -//! `%%rdi` prior to calling this function is needed, it must be obtained -//! separately prior to calling this function. For example: -//! \code -//! uint64_t rdi; -//! asm("movq %%rdi, %0" : "=m"(rdi)); -//! \endcode -void CaptureContext(NativeCPUContext* cpu_context); - -} // namespace crashpad - -#endif // CRASHPAD_CLIENT_CAPTURE_CONTEXT_MAC_H_ diff --git a/client/capture_context_mac_test.cc b/client/capture_context_mac_test.cc deleted file mode 100644 index 15640210..00000000 --- a/client/capture_context_mac_test.cc +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2014 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 "client/capture_context_mac.h" - -#include -#include - -#include - -#include "build/build_config.h" -#include "gtest/gtest.h" -#include "util/misc/address_sanitizer.h" -#include "util/misc/implicit_cast.h" - -namespace crashpad { -namespace test { -namespace { - -// If the context structure has fields that tell whether it’s valid, such as -// magic numbers or size fields, sanity-checks those fields for validity with -// fatal gtest assertions. For other fields, where it’s possible to reason about -// their validity based solely on their contents, sanity-checks via nonfatal -// gtest assertions. -void SanityCheckContext(const NativeCPUContext& context) { -#if defined(ARCH_CPU_X86) - ASSERT_EQ(implicit_cast(context.tsh.flavor), - implicit_cast(x86_THREAD_STATE32)); - ASSERT_EQ(implicit_cast(context.tsh.count), - implicit_cast(x86_THREAD_STATE32_COUNT)); -#elif defined(ARCH_CPU_X86_64) - ASSERT_EQ(implicit_cast(context.tsh.flavor), - implicit_cast(x86_THREAD_STATE64)); - ASSERT_EQ(implicit_cast(context.tsh.count), - implicit_cast(x86_THREAD_STATE64_COUNT)); -#endif - -#if defined(ARCH_CPU_X86_FAMILY) - // The segment registers are only capable of storing 16-bit quantities, but - // the context structure provides native integer-width fields for them. Ensure - // that the high bits are all clear. - // - // Many bit positions in the flags register are reserved and will always read - // a known value. Most reserved bits are always 0, but bit 1 is always 1. - // Check that the reserved bits are all set to their expected values. Note - // that the set of reserved bits may be relaxed over time with newer CPUs, and - // that this test may need to be changed to reflect these developments. The - // current set of reserved bits are 1, 3, 5, 15, and 22 and higher. See Intel - // Software Developer’s Manual, Volume 1: Basic Architecture (253665-051), - // 3.4.3 “EFLAGS Register”, and AMD Architecture Programmer’s Manual, Volume - // 2: System Programming (24593-3.24), 3.1.6 “RFLAGS Register”. -#if defined(ARCH_CPU_X86) - EXPECT_EQ(context.uts.ts32.__cs & ~0xffff, 0u); - EXPECT_EQ(context.uts.ts32.__ds & ~0xffff, 0u); - EXPECT_EQ(context.uts.ts32.__es & ~0xffff, 0u); - EXPECT_EQ(context.uts.ts32.__fs & ~0xffff, 0u); - EXPECT_EQ(context.uts.ts32.__gs & ~0xffff, 0u); - EXPECT_EQ(context.uts.ts32.__ss & ~0xffff, 0u); - EXPECT_EQ(context.uts.ts32.__eflags & 0xffc0802a, 2u); -#elif defined(ARCH_CPU_X86_64) - EXPECT_EQ(context.uts.ts64.__cs & ~UINT64_C(0xffff), 0u); - EXPECT_EQ(context.uts.ts64.__fs & ~UINT64_C(0xffff), 0u); - EXPECT_EQ(context.uts.ts64.__gs & ~UINT64_C(0xffff), 0u); - EXPECT_EQ(context.uts.ts64.__rflags & UINT64_C(0xffffffffffc0802a), 2u); -#endif -#endif -} - -// A CPU-independent function to return the program counter. -uintptr_t ProgramCounterFromContext(const NativeCPUContext& context) { -#if defined(ARCH_CPU_X86) - return context.uts.ts32.__eip; -#elif defined(ARCH_CPU_X86_64) - return context.uts.ts64.__rip; -#endif -} - -// A CPU-independent function to return the stack pointer. -uintptr_t StackPointerFromContext(const NativeCPUContext& context) { -#if defined(ARCH_CPU_X86) - return context.uts.ts32.__esp; -#elif defined(ARCH_CPU_X86_64) - return context.uts.ts64.__rsp; -#endif -} - -void TestCaptureContext() { - NativeCPUContext context_1; - CaptureContext(&context_1); - - { - SCOPED_TRACE("context_1"); - ASSERT_NO_FATAL_FAILURE(SanityCheckContext(context_1)); - } - - // The program counter reference value is this function’s address. The - // captured program counter should be slightly greater than or equal to the - // reference program counter. - uintptr_t pc = ProgramCounterFromContext(context_1); - -#if !defined(ADDRESS_SANITIZER) - // AddressSanitizer can cause enough code bloat that the “nearby” check would - // likely fail. - const uintptr_t kReferencePC = - reinterpret_cast(TestCaptureContext); - EXPECT_LT(pc - kReferencePC, 64u); -#endif // !defined(ADDRESS_SANITIZER) - - // Declare sp and context_2 here because all local variables need to be - // declared before computing the stack pointer reference value, so that the - // reference value can be the lowest value possible. - uintptr_t sp; - NativeCPUContext context_2; - - // The stack pointer reference value is the lowest address of a local variable - // in this function. The captured program counter will be slightly less than - // or equal to the reference stack pointer. - const uintptr_t kReferenceSP = - std::min(std::min(reinterpret_cast(&context_1), - reinterpret_cast(&context_2)), - std::min(reinterpret_cast(&pc), - reinterpret_cast(&sp))); - sp = StackPointerFromContext(context_1); - EXPECT_LT(kReferenceSP - sp, 512u); - - // Capture the context again, expecting that the stack pointer stays the same - // and the program counter increases. Strictly speaking, there’s no guarantee - // that these conditions will hold, although they do for known compilers even - // under typical optimization. - CaptureContext(&context_2); - - { - SCOPED_TRACE("context_2"); - ASSERT_NO_FATAL_FAILURE(SanityCheckContext(context_2)); - } - - EXPECT_EQ(StackPointerFromContext(context_2), sp); - EXPECT_GT(ProgramCounterFromContext(context_2), pc); -} - -TEST(CaptureContextMac, CaptureContext) { - ASSERT_NO_FATAL_FAILURE(TestCaptureContext()); -} - -} // namespace -} // namespace test -} // namespace crashpad diff --git a/client/client.gyp b/client/client.gyp index f75f9c40..8fb1890b 100644 --- a/client/client.gyp +++ b/client/client.gyp @@ -33,8 +33,6 @@ 'annotation.h', 'annotation_list.cc', 'annotation_list.h', - 'capture_context_mac.S', - 'capture_context_mac.h', 'crash_report_database.cc', 'crash_report_database.h', 'crash_report_database_mac.mm', @@ -64,11 +62,6 @@ ], }, }], - ['OS!="mac"', { - 'sources!': [ - 'capture_context_mac.S', - ], - }], ], 'direct_dependent_settings': { 'include_dirs': [ diff --git a/client/client_test.gyp b/client/client_test.gyp index 4ea4ef2b..75f67238 100644 --- a/client/client_test.gyp +++ b/client/client_test.gyp @@ -37,7 +37,6 @@ 'sources': [ 'annotation_test.cc', 'annotation_list_test.cc', - 'capture_context_mac_test.cc', 'crash_report_database_test.cc', 'crashpad_client_win_test.cc', 'prune_crash_reports_test.cc', diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc index dd25c9ef..8ceded33 100644 --- a/client/crashpad_client_win.cc +++ b/client/crashpad_client_win.cc @@ -30,10 +30,10 @@ #include "base/strings/utf_string_conversions.h" #include "base/synchronization/lock.h" #include "util/file/file_io.h" +#include "util/misc/capture_context.h" #include "util/misc/from_pointer_cast.h" #include "util/misc/random_string.h" #include "util/win/address_types.h" -#include "util/win/capture_context.h" #include "util/win/command_line.h" #include "util/win/critical_section_with_debug_info.h" #include "util/win/get_function.h" diff --git a/client/simulate_crash_mac.h b/client/simulate_crash_mac.h index e14db3c9..dcbcaae3 100644 --- a/client/simulate_crash_mac.h +++ b/client/simulate_crash_mac.h @@ -17,7 +17,7 @@ #include -#include "client/capture_context_mac.h" +#include "util/misc/capture_context.h" //! \file diff --git a/client/simulate_crash_win.h b/client/simulate_crash_win.h index 3c5bdf18..140424f3 100644 --- a/client/simulate_crash_win.h +++ b/client/simulate_crash_win.h @@ -18,7 +18,7 @@ #include #include "client/crashpad_client.h" -#include "util/win/capture_context.h" +#include "util/misc/capture_context.h" //! \file diff --git a/handler/crashpad_handler_test.cc b/handler/crashpad_handler_test.cc index 65fed90f..79076b86 100644 --- a/handler/crashpad_handler_test.cc +++ b/handler/crashpad_handler_test.cc @@ -28,7 +28,7 @@ #include "test/test_paths.h" #include "test/win/win_multiprocess_with_temp_dir.h" #include "util/file/file_reader.h" -#include "util/win/capture_context.h" +#include "util/misc/capture_context.h" namespace crashpad { namespace test { diff --git a/snapshot/win/crashpad_snapshot_test_crashing_child.cc b/snapshot/win/crashpad_snapshot_test_crashing_child.cc index c06663a2..759cc138 100644 --- a/snapshot/win/crashpad_snapshot_test_crashing_child.cc +++ b/snapshot/win/crashpad_snapshot_test_crashing_child.cc @@ -18,8 +18,8 @@ #include "base/logging.h" #include "build/build_config.h" #include "client/crashpad_client.h" +#include "util/misc/capture_context.h" #include "util/win/address_types.h" -#include "util/win/capture_context.h" int wmain(int argc, wchar_t* argv[]) { CHECK_EQ(argc, 2); diff --git a/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc b/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc index 3bf13712..e2c524ae 100644 --- a/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc +++ b/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc @@ -18,8 +18,8 @@ #include "base/logging.h" #include "client/crashpad_client.h" #include "client/simulate_crash.h" +#include "util/misc/capture_context.h" #include "util/win/address_types.h" -#include "util/win/capture_context.h" int wmain(int argc, wchar_t* argv[]) { CHECK_EQ(argc, 2); diff --git a/snapshot/win/process_reader_win.cc b/snapshot/win/process_reader_win.cc index 73a0ca78..2fa02584 100644 --- a/snapshot/win/process_reader_win.cc +++ b/snapshot/win/process_reader_win.cc @@ -21,8 +21,8 @@ #include "base/numerics/safe_conversions.h" #include "base/strings/stringprintf.h" +#include "util/misc/capture_context.h" #include "util/misc/time.h" -#include "util/win/capture_context.h" #include "util/win/nt_internals.h" #include "util/win/ntstatus_logging.h" #include "util/win/process_structs.h" diff --git a/util/BUILD.gn b/util/BUILD.gn index 2f376110..586ed537 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -83,6 +83,7 @@ static_library("util") { "misc/address_types.h", "misc/arraysize_unsafe.h", "misc/as_underlying_type.h", + "misc/capture_context.h", "misc/clock.h", "misc/from_pointer_cast.h", "misc/implicit_cast.h", @@ -231,6 +232,7 @@ static_library("util") { "mach/task_for_pid.h", "mach/task_memory.cc", "mach/task_memory.h", + "misc/capture_context_mac.S", "misc/clock_mac.cc", "misc/paths_mac.cc", "net/http_transport_mac.mm", @@ -272,6 +274,7 @@ static_library("util") { "linux/thread_info.cc", "linux/thread_info.h", "linux/traits.h", + "misc/capture_context_linux.S", "misc/paths_linux.cc", "posix/process_info_linux.cc", "process/process_memory_linux.cc", @@ -303,7 +306,6 @@ static_library("util") { "synchronization/semaphore_win.cc", "thread/thread_win.cc", "win/address_types.h", - "win/capture_context.h", "win/checked_win_address_range.h", "win/command_line.cc", "win/command_line.h", @@ -357,12 +359,12 @@ static_library("util") { # https://crbug.com/762167. if (host_os == "win") { sources += [ - "win/capture_context.asm", + "misc/capture_context_win.asm", "win/safe_terminate_process.asm", ] } else { sources += [ - "win/capture_context_broken.cc", + "misc/capture_context_broken.cc", "win/safe_terminate_process_broken.cc", ] } @@ -430,6 +432,8 @@ source_set("util_test") { "file/filesystem_test.cc", "file/string_file_test.cc", "misc/arraysize_unsafe_test.cc", + "misc/capture_context_test.cc", + "misc/capture_context_test_util.h", "misc/clock_test.cc", "misc/from_pointer_cast_test.cc", "misc/initialization_state_dcheck_test.cc", @@ -508,6 +512,7 @@ source_set("util_test") { "mach/scoped_task_suspend_test.cc", "mach/symbolic_constants_mach_test.cc", "mach/task_memory_test.cc", + "misc/capture_context_test_util_mac.cc", ] } @@ -519,6 +524,7 @@ source_set("util_test") { "linux/ptrace_broker_test.cc", "linux/ptracer_test.cc", "linux/scoped_ptrace_attach_test.cc", + "misc/capture_context_test_util_linux.cc", ] } @@ -532,7 +538,7 @@ source_set("util_test") { if (crashpad_is_win) { sources += [ - "win/capture_context_test.cc", + "misc/capture_context_test_util_win.cc", "win/command_line_test.cc", "win/critical_section_with_debug_info_test.cc", "win/exception_handler_server_test.cc", diff --git a/util/misc/capture_context.h b/util/misc/capture_context.h new file mode 100644 index 00000000..d12fad6a --- /dev/null +++ b/util/misc/capture_context.h @@ -0,0 +1,81 @@ +// Copyright 2014 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_UTIL_MISC_CAPTURE_CONTEXT_H_ +#define CRASHPAD_UTIL_MISC_CAPTURE_CONTEXT_H_ + +#include "build/build_config.h" + +#if defined(OS_MACOSX) +#include +#elif defined(OS_WIN) +#include +#elif defined(OS_LINUX) || defined(OS_ANDROID) +#include +#endif // OS_MACOSX + +namespace crashpad { + +#if defined(OS_MACOSX) +#if defined(ARCH_CPU_X86_FAMILY) +using NativeCPUContext = x86_thread_state; +#endif +#elif defined(OS_WIN) +using NativeCPUContext = CONTEXT; +#elif defined(OS_LINUX) || defined(OS_ANDROID) +using NativeCPUContext = ucontext_t; +#endif // OS_MACOSX + +//! \brief Saves the CPU context. +//! +//! The CPU context will be captured as accurately and completely as possible, +//! containing an atomic snapshot at the point of this function’s return. This +//! function does not modify any registers. +//! +//! This function is a replacement for `RtlCaptureContext()` and `getcontext()` +//! which contain bugs and/or limitations. +//! +//! On 32-bit x86, `RtlCaptureContext()` requires that `ebp` be used as a frame +//! pointer, and returns `ebp`, `esp`, and `eip` out of sync with the other +//! registers. Both the 32-bit x86 and 64-bit x86_64 versions of +//! `RtlCaptureContext()` capture only the state of the integer registers, +//! ignoring floating-point and vector state. +//! +//! \param[out] cpu_context The structure to store the context in. +//! +//! \note The ABI may require that this function's argument is passed by +//! register, preventing this fuction from saving the original value of that +//! register. This occurs in the following circumstances: +//! +//! OS | Architecture | Register +//! ------------|--------------|--------- +//! Win | x86_64 | `%%rcx` +//! macOS/Linux | x86_64 | `%%rdi` +//! Linux | ARM/ARM64 | `r0`/`x0` +//! +//! Additionally, the value `LR` on ARM/ARM64 will be the return address of +//! this function. +//! +//! If the value of these register prior to calling this function are needed +//! they must be obtained separately prior to calling this function. For +//! example: +//! \code +//! uint64_t rdi; +//! asm("movq %%rdi, %0" : "=m"(rdi)); +//! \endcode +void CaptureContext(NativeCPUContext* cpu_context); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_CAPTURE_CONTEXT_H_ diff --git a/util/win/capture_context_broken.cc b/util/misc/capture_context_broken.cc similarity index 90% rename from util/win/capture_context_broken.cc rename to util/misc/capture_context_broken.cc index 4a641515..ab7a5974 100644 --- a/util/win/capture_context_broken.cc +++ b/util/misc/capture_context_broken.cc @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "util/win/capture_context.h" +#include "util/misc/capture_context.h" #include "base/logging.h" namespace crashpad { -void CaptureContext(CONTEXT* context) { +void CaptureContext(NativeCPUContext* context) { // Don't use this file in production. CHECK(false) << "Don't use this! For cross builds only. See https://crbug.com/762167."; diff --git a/util/misc/capture_context_linux.S b/util/misc/capture_context_linux.S new file mode 100644 index 00000000..c16d0c74 --- /dev/null +++ b/util/misc/capture_context_linux.S @@ -0,0 +1,324 @@ +// Copyright 2018 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. + +// namespace crashpad { +// void CaptureContext(ucontext_t* context); +// } // namespace crashpad +#define CAPTURECONTEXT_SYMBOL _ZN8crashpad14CaptureContextEP8ucontext + + .text + .globl CAPTURECONTEXT_SYMBOL +#if defined(__i386__) || defined(__x86_64__) + .balign 16, 0x90 +#elif defined(__arm__) || defined(__aarch64__) + .balign 4, 0x0 +#endif + +CAPTURECONTEXT_SYMBOL: + +#if defined(__i386__) + + .cfi_startproc + + pushl %ebp + .cfi_def_cfa_offset 8 + .cfi_offset %ebp, -8 + movl %esp, %ebp + .cfi_def_cfa_register %ebp + + // Note that 16-byte stack alignment is not maintained because this function + // does not call out to any other. + + // pushfl first, because some instructions (but probably none used here) + // affect %eflags. %eflags will be in -4(%ebp). + pushfl + + // Save the original value of %eax, and use %eax to hold the ucontext_t* + // argument. The original value of %eax will be in -8(%ebp). + pushl %eax + movl 8(%ebp), %eax + + // Save the original value of %ecx, and use %ecx as a scratch register. + pushl %ecx + + // The segment registers are 16 bits wide, but mcontext_t declares them + // as unsigned 32-bit values, so zero the top half. + xorl %ecx, %ecx + movw %gs, %cx + movl %ecx, 0x14(%eax) // context->uc_mcontext.xgs + movw %fs, %cx + movl %ecx, 0x18(%eax) // context->uc_mcontext.xfs + movw %es, %cx + movl %ecx, 0x1c(%eax) // context->uc_mcontext.xes + movw %ds, %cx + movl %ecx, 0x20(%eax) // context->uc_mcontext.xds + + // General-purpose registers whose values haven’t changed can be captured + // directly. + movl %edi, 0x24(%eax) // context->uc_mcontext.edi + movl %esi, 0x28(%eax) // context->uc_mcontext.esi + + // The original %ebp was saved on the stack in this function’s prologue. + movl (%ebp), %ecx + movl %ecx, 0x2c(%eax) // context->uc_mcontext.ebp + + // %esp was saved in %ebp in this function’s prologue, but the caller’s %esp + // is 8 more than this value: 4 for the original %ebp saved on the stack in + // this function’s prologue, and 4 for the return address saved on the stack + // by the call instruction that reached this function. + leal 8(%ebp), %ecx + movl %ecx, 0x30(%eax) // context->uc_mcontext.esp + + // More general-purpose registers + movl %ebx, 0x34(%eax) // context->uc_mcontext.ebx + movl %edx, 0x38(%eax) // context->uc_mcontext.edx + + // The original %ecx was saved on the stack above. + movl -12(%ebp), %ecx + movl %ecx, 0x3c(%eax) // context->uc_mcontext.ecx + + // The original %eax was saved on the stack above. + movl -8(%ebp), %ecx + movl %ecx, 0x40(%eax) // context->uc_mcontext.eax + + // trapno and err are unused so zero them out. + xorl %ecx, %ecx + movl %ecx, 0x44(%eax) // context->uc_mcontext.trapno + movl %ecx, 0x48(%eax) // context->uc_mcontext.err + + // %eip can’t be accessed directly, but the return address saved on the stack + // by the call instruction that reached this function can be used. + movl 4(%ebp), %ecx + movl %ecx, 0x4c(%eax) // context->uc_mcontext.eip + + // More segment registers + xorl %ecx, %ecx + movw %cs, %cx + movl %ecx, 0x50(%eax) // context->uc_mcontext.xcs + + // The original %eflags was saved on the stack above. + movl -4(%ebp), %ecx + movl %ecx, 0x54(%eax) // context->uc_mcontext.eflags + + // uesp is unused so zero it out. + xorl %ecx, %ecx + movl %ecx, 0x58(%eax) // context->uc_mcontext.uesp + + // The last segment register. + movw %ss, %cx + movl %ecx, 0x5c(%eax) // context->uc_mcontext.xss + + // TODO(jperaza): save floating-point registers. + + // Clean up by restoring clobbered registers, even those considered volatile + // by the ABI, so that the captured context represents the state at this + // function’s exit. + popl %ecx + popl %eax + popfl + + popl %ebp + + ret + + .cfi_endproc + +#elif defined(__x86_64__) + + .cfi_startproc + + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + + // Note that 16-byte stack alignment is not maintained because this function + // does not call out to any other. + + // pushfq first, because some instructions (but probably none used here) + // affect %rflags. %rflags will be in -8(%rbp). + pushfq + + // General-purpose registers whose values haven’t changed can be captured + // directly. + movq %r8, 0x28(%rdi) // context->uc_mcontext.r8 + movq %r9, 0x30(%rdi) // context->uc_mcontext.r9 + movq %r10, 0x38(%rdi) // context->uc_mcontext.r10 + movq %r11, 0x40(%rdi) // context->uc_mcontext.r11 + movq %r12, 0x48(%rdi) // context->uc_mcontext.r12 + movq %r13, 0x50(%rdi) // context->uc_mcontext.r13 + movq %r14, 0x58(%rdi) // context->uc_mcontext.r14 + movq %r15, 0x60(%rdi) // context->uc_mcontext.r15 + + // Because of the calling convention, there’s no way to recover the value of + // the caller’s %rdi as it existed prior to calling this function. This + // function captures a snapshot of the register state at its return, which + // involves %rdi containing a pointer to its first argument. Callers that + // require the value of %rdi prior to calling this function should obtain it + // separately. For example: + // uint64_t rdi; + // asm("movq %%rdi, %0" : "=m"(rdi)); + movq %rdi, 0x68(%rdi) // context->uc_mcontext.rdi + + movq %rsi, 0x70(%rdi) // context->uc_mcontext.rsi + + // Use %r8 as a scratch register now that it has been saved. + // The original %rbp was saved on the stack in this function’s prologue. + movq (%rbp), %r8 + movq %r8, 0x78(%rdi) // context->uc_mcontext.rbp + + // Save the remaining general-purpose registers. + movq %rbx, 0x80(%rdi) // context->uc_mcontext.rbx + movq %rdx, 0x88(%rdi) // context->uc_mcontext.rdx + movq %rax, 0x90(%rdi) // context->uc_mcontext.rax + movq %rcx, 0x98(%rdi) // context->uc_mcontext.rcx + + // %rsp was saved in %rbp in this function’s prologue, but the caller’s %rsp + // is 16 more than this value: 8 for the original %rbp saved on the stack in + // this function’s prologue, and 8 for the return address saved on the stack + // by the call instruction that reached this function. + leaq 16(%rbp), %r8 + movq %r8, 0xa0(%rdi) // context->uc_mcontext.rsp + + // %rip can’t be accessed directly, but the return address saved on the stack + // by the call instruction that reached this function can be used. + movq 8(%rbp), %r8 + movq %r8, 0xa8(%rdi) // context->uc_mcontext.rip + + // The original %rflags was saved on the stack above. + movq -8(%rbp), %r8 + movq %r8, 0xb0(%rdi) // context->uc_mcontext.eflags + + // Save the segment registers + movw %cs, 0xb8(%rdi) // context->uc_mcontext.cs + movw %gs, 0xba(%rdi) // context->uc_mcontext.gs + movw %fs, 0xbc(%rdi) // context->uc_mcontext.fs + + xorw %ax, %ax + movw %ax, 0xbe(%rdi) // context->uc_mcontext.padding + + // Zero out the remainder of the unused pseudo-registers + xorq %r8, %r8 + movq %r8, 0xc0(%rdi) // context->uc_mcontext.err + movq %r8, 0xc8(%rdi) // context->uc_mcontext.trapno + movq %r8, 0xd0(%rdi) // context->uc_mcontext.oldmask + movq %r8, 0xd8(%rdi) // context->uc_mcontext.cr2 + + // Clean up by restoring clobbered registers, even those considered volatile + // by the ABI, so that the captured context represents the state at this + // function’s exit. + movq 0x90(%rdi), %rax + movq 0x28(%rdi), %r8 + + // TODO(jperaza): save floating-point registers. + + popfq + + popq %rbp + + ret + + .cfi_endproc + +#elif defined(__arm__) + + // The original r0 can't be recovered. + str r0, [r0, #0x20] + + // Now advance r0 to point to the register array. + add r0, r0, #0x24 + + // Save registers r1-r12 at context->uc_mcontext.regs[i]. + stm r0, {r1-r12} + + // Restore r0. + sub r0, r0, #0x24 + + // Save named general purpose registers. + str FP, [r0, #0x4c] // context->uc_mcontext.fp + str IP, [r0, #0x50] // context->uc_mcontext.ip + str SP, [r0, #0x54] // context->uc_mcontext.sp + + // The original LR can't be recovered. + str LR, [r0, #0x58] // context->uc_mcontext.lr + + // The link register holds the return address for this function. + str LR, [r0, #0x5c] // context->uc_mcontext.pc + + // Use r1 as a scratch register. + + // CPSR is a deprecated synonym for APSR. + mrs r1, APSR + str r1, [r0, #0x60] // context->uc_mcontext.cpsr + + // Zero out unused fields. + mov r1, #0x0 + str r1, [r0, #0x14] // context->uc_mcontext.trap_no + str r1, [r0, #0x18] // context->uc_mcontext.error_code + str r1, [r0, #0x1c] // context->uc_mcontext.oldmask + str r1, [r0, #0x64] // context->uc_mcontext.fault_address + + // Restore r1. + ldr r1, [r0, #0x24] + + // TODO(jperaza): save floating-point registers. + + mov PC, LR + +#elif defined(__aarch64__) + + // Zero out fault_address, which is unused. + str x31, [x0, #0xb0] // context->uc_mcontext.fault_address + + // Save general purpose registers in context->uc_mcontext.regs[i]. + // The original x0 can't be recovered. + stp x0, x1, [x0, #0xb8] + stp x2, x3, [x0, #0xc8] + stp x4, x5, [x0, #0xd8] + stp x6, x7, [x0, #0xe8] + stp x8, x9, [x0, #0xf8] + stp x10, x11, [x0, #0x108] + stp x12, x13, [x0, #0x118] + stp x14, x15, [x0, #0x128] + stp x16, x17, [x0, #0x138] + stp x18, x19, [x0, #0x148] + stp x20, x21, [x0, #0x158] + stp x22, x23, [x0, #0x168] + stp x24, x25, [x0, #0x178] + stp x26, x27, [x0, #0x188] + stp x28, x29, [x0, #0x198] + + // The original LR can't be recovered. + str LR, [x0, #0x1a8] + + // Use x1 as a scratch register. + mov x1, SP + str x1, [x0, #0x1b0] // context->uc_mcontext.sp + + // The link register holds the return address for this function. + str LR, [x0, #0x1b8] // context->uc_mcontext.pc + + // NZCV, pstate, and CPSR are synonyms. + mrs x1, NZCV + str x1, [x0, #0x1c0] // context->uc_mcontext.pstate + + // Restore x1 from the saved context. + ldr x1, [x0, #0xc0] + + // TODO(jperaza): save floating-point registers. + + ret + +#endif // __i386__ diff --git a/client/capture_context_mac.S b/util/misc/capture_context_mac.S similarity index 99% rename from client/capture_context_mac.S rename to util/misc/capture_context_mac.S index 942d8413..39c6ca6a 100644 --- a/client/capture_context_mac.S +++ b/util/misc/capture_context_mac.S @@ -22,7 +22,7 @@ .section __TEXT,__text,regular,pure_instructions .private_extern CAPTURECONTEXT_SYMBOL .globl CAPTURECONTEXT_SYMBOL - .align 4, 0x90 + .balign 16, 0x90 CAPTURECONTEXT_SYMBOL: #if defined(__i386__) diff --git a/util/misc/capture_context_test.cc b/util/misc/capture_context_test.cc new file mode 100644 index 00000000..e31883e7 --- /dev/null +++ b/util/misc/capture_context_test.cc @@ -0,0 +1,95 @@ +// Copyright 2014 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 "util/misc/capture_context.h" + +#include + +#include + +#include "gtest/gtest.h" +#include "util/misc/address_sanitizer.h" +#include "util/misc/capture_context_test_util.h" + +namespace crashpad { +namespace test { +namespace { + +void TestCaptureContext() { + NativeCPUContext context_1; + CaptureContext(&context_1); + + { + SCOPED_TRACE("context_1"); + ASSERT_NO_FATAL_FAILURE(SanityCheckContext(context_1)); + } + + // The program counter reference value is this function’s address. The + // captured program counter should be slightly greater than or equal to the + // reference program counter. + uintptr_t pc = ProgramCounterFromContext(context_1); + +#if !defined(ADDRESS_SANITIZER) + // AddressSanitizer can cause enough code bloat that the “nearby” check would + // likely fail. + const uintptr_t kReferencePC = + reinterpret_cast(TestCaptureContext); + EXPECT_PRED2([](uintptr_t actual, + uintptr_t reference) { return actual - reference < 64u; }, + pc, + kReferencePC); +#endif // !defined(ADDRESS_SANITIZER) + + // Declare sp and context_2 here because all local variables need to be + // declared before computing the stack pointer reference value, so that the + // reference value can be the lowest value possible. + uintptr_t sp; + NativeCPUContext context_2; + + // The stack pointer reference value is the lowest address of a local variable + // in this function. The captured program counter will be slightly less than + // or equal to the reference stack pointer. + const uintptr_t kReferenceSP = + std::min(std::min(reinterpret_cast(&context_1), + reinterpret_cast(&context_2)), + std::min(reinterpret_cast(&pc), + reinterpret_cast(&sp))); + sp = StackPointerFromContext(context_1); + EXPECT_PRED2([](uintptr_t actual, + uintptr_t reference) { return reference - actual < 512u; }, + sp, + kReferenceSP); + + // Capture the context again, expecting that the stack pointer stays the same + // and the program counter increases. Strictly speaking, there’s no guarantee + // that these conditions will hold, although they do for known compilers even + // under typical optimization. + CaptureContext(&context_2); + + { + SCOPED_TRACE("context_2"); + ASSERT_NO_FATAL_FAILURE(SanityCheckContext(context_2)); + } + + EXPECT_EQ(StackPointerFromContext(context_2), sp); + EXPECT_GT(ProgramCounterFromContext(context_2), pc); +} + +TEST(CaptureContext, CaptureContext) { + ASSERT_NO_FATAL_FAILURE(TestCaptureContext()); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/util/misc/capture_context_test_util.h b/util/misc/capture_context_test_util.h new file mode 100644 index 00000000..5a5ff7d5 --- /dev/null +++ b/util/misc/capture_context_test_util.h @@ -0,0 +1,41 @@ +// Copyright 2018 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 "util/misc/capture_context.h" + +#include + +namespace crashpad { +namespace test { + +//! \brief Sanity check conditions that should be true for any NativeCPUContext +//! produced by CaptureContext(). +//! +//! If the context structure has fields that tell whether it’s valid, such as +//! magic numbers or size fields, sanity-checks those fields for validity with +//! fatal gtest assertions. For other fields, where it’s possible to reason +//! about their validity based solely on their contents, sanity-checks via +//! nonfatal gtest assertions. +//! +//! \param[in] context The context to check. +void SanityCheckContext(const NativeCPUContext& context); + +//! \brief Return the value of the program counter from a NativeCPUContext. +uintptr_t ProgramCounterFromContext(const NativeCPUContext& context); + +//! \brief Return the value of the stack pointer from a NativeCPUContext. +uintptr_t StackPointerFromContext(const NativeCPUContext& context); + +} // namespace test +} // namespace crashpad diff --git a/util/misc/capture_context_test_util_linux.cc b/util/misc/capture_context_test_util_linux.cc new file mode 100644 index 00000000..fb64e5d4 --- /dev/null +++ b/util/misc/capture_context_test_util_linux.cc @@ -0,0 +1,62 @@ +// Copyright 2018 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 "util/misc/capture_context_test_util.h" + +#include "base/logging.h" +#include "gtest/gtest.h" +#include "util/misc/from_pointer_cast.h" + +namespace crashpad { +namespace test { + +void SanityCheckContext(const NativeCPUContext& context) { +#if defined(ARCH_CPU_X86) + // Nothing to do here yet. +#elif defined(ARCH_CPU_X86_64) + EXPECT_EQ(context.uc_mcontext.gregs[REG_RDI], + FromPointerCast(&context)); +#elif defined(ARCH_CPU_ARMEL) + EXPECT_EQ(context.uc_mcontext.arm_r0, FromPointerCast(&context)); +#elif defined(ARCH_CPU_ARM64) + EXPECT_EQ(context.uc_mcontext.regs[0], FromPointerCast(&context)); +#endif +} + +uintptr_t ProgramCounterFromContext(const NativeCPUContext& context) { +#if defined(ARCH_CPU_X86) + return context.uc_mcontext.gregs[REG_EIP]; +#elif defined(ARCH_CPU_X86_64) + return context.uc_mcontext.gregs[REG_RIP]; +#elif defined(ARCH_CPU_ARMEL) + return context.uc_mcontext.arm_pc; +#elif defined(ARCH_CPU_ARM64) + return context.uc_mcontext.pc; +#endif +} + +uintptr_t StackPointerFromContext(const NativeCPUContext& context) { +#if defined(ARCH_CPU_X86) + return context.uc_mcontext.gregs[REG_ESP]; +#elif defined(ARCH_CPU_X86_64) + return context.uc_mcontext.gregs[REG_RSP]; +#elif defined(ARCH_CPU_ARMEL) + return context.uc_mcontext.arm_sp; +#elif defined(ARCH_CPU_ARM64) + return context.uc_mcontext.sp; +#endif +} + +} // namespace test +} // namespace crashpad diff --git a/util/misc/capture_context_test_util_mac.cc b/util/misc/capture_context_test_util_mac.cc new file mode 100644 index 00000000..afe09163 --- /dev/null +++ b/util/misc/capture_context_test_util_mac.cc @@ -0,0 +1,84 @@ +// Copyright 2018 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 "util/misc/capture_context_test_util.h" + +#include "gtest/gtest.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { + +void SanityCheckContext(const NativeCPUContext& context) { +#if defined(ARCH_CPU_X86) + ASSERT_EQ(implicit_cast(context.tsh.flavor), + implicit_cast(x86_THREAD_STATE32)); + ASSERT_EQ(implicit_cast(context.tsh.count), + implicit_cast(x86_THREAD_STATE32_COUNT)); +#elif defined(ARCH_CPU_X86_64) + ASSERT_EQ(implicit_cast(context.tsh.flavor), + implicit_cast(x86_THREAD_STATE64)); + ASSERT_EQ(implicit_cast(context.tsh.count), + implicit_cast(x86_THREAD_STATE64_COUNT)); +#endif + +#if defined(ARCH_CPU_X86_FAMILY) +// The segment registers are only capable of storing 16-bit quantities, but +// the context structure provides native integer-width fields for them. Ensure +// that the high bits are all clear. +// +// Many bit positions in the flags register are reserved and will always read +// a known value. Most reserved bits are always 0, but bit 1 is always 1. +// Check that the reserved bits are all set to their expected values. Note +// that the set of reserved bits may be relaxed over time with newer CPUs, and +// that this test may need to be changed to reflect these developments. The +// current set of reserved bits are 1, 3, 5, 15, and 22 and higher. See Intel +// Software Developer’s Manual, Volume 1: Basic Architecture (253665-051), +// 3.4.3 “EFLAGS Register”, and AMD Architecture Programmer’s Manual, Volume +// 2: System Programming (24593-3.24), 3.1.6 “RFLAGS Register”. +#if defined(ARCH_CPU_X86) + EXPECT_EQ(context.uts.ts32.__cs & ~0xffff, 0u); + EXPECT_EQ(context.uts.ts32.__ds & ~0xffff, 0u); + EXPECT_EQ(context.uts.ts32.__es & ~0xffff, 0u); + EXPECT_EQ(context.uts.ts32.__fs & ~0xffff, 0u); + EXPECT_EQ(context.uts.ts32.__gs & ~0xffff, 0u); + EXPECT_EQ(context.uts.ts32.__ss & ~0xffff, 0u); + EXPECT_EQ(context.uts.ts32.__eflags & 0xffc0802a, 2u); +#elif defined(ARCH_CPU_X86_64) + EXPECT_EQ(context.uts.ts64.__cs & ~UINT64_C(0xffff), 0u); + EXPECT_EQ(context.uts.ts64.__fs & ~UINT64_C(0xffff), 0u); + EXPECT_EQ(context.uts.ts64.__gs & ~UINT64_C(0xffff), 0u); + EXPECT_EQ(context.uts.ts64.__rflags & UINT64_C(0xffffffffffc0802a), 2u); +#endif +#endif +} + +uintptr_t ProgramCounterFromContext(const NativeCPUContext& context) { +#if defined(ARCH_CPU_X86) + return context.uts.ts32.__eip; +#elif defined(ARCH_CPU_X86_64) + return context.uts.ts64.__rip; +#endif +} + +uintptr_t StackPointerFromContext(const NativeCPUContext& context) { +#if defined(ARCH_CPU_X86) + return context.uts.ts32.__esp; +#elif defined(ARCH_CPU_X86_64) + return context.uts.ts64.__rsp; +#endif +} + +} // namespace test +} // namespace crashpad diff --git a/util/win/capture_context_test.cc b/util/misc/capture_context_test_util_win.cc similarity index 53% rename from util/win/capture_context_test.cc rename to util/misc/capture_context_test_util_win.cc index 292e4749..239beacd 100644 --- a/util/win/capture_context_test.cc +++ b/util/misc/capture_context_test_util_win.cc @@ -1,4 +1,4 @@ -// Copyright 2015 The Crashpad Authors. All rights reserved. +// Copyright 2018 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. @@ -12,40 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "util/win/capture_context.h" - -#include -#include - -#include +#include "util/misc/capture_context_test_util.h" #include "base/macros.h" -#include "build/build_config.h" #include "gtest/gtest.h" namespace crashpad { namespace test { -namespace { -// If the context structure has fields that tell whether it’s valid, such as -// magic numbers or size fields, sanity-checks those fields for validity with -// fatal gtest assertions. For other fields, where it’s possible to reason about -// their validity based solely on their contents, sanity-checks via nonfatal -// gtest assertions. -void SanityCheckContext(const CONTEXT& context) { +void SanityCheckContext(const NativeCPUContext& context) { #if defined(ARCH_CPU_X86) - constexpr uint32_t must_have = CONTEXT_i386 | - CONTEXT_CONTROL | - CONTEXT_INTEGER | - CONTEXT_SEGMENTS | + constexpr uint32_t must_have = CONTEXT_i386 | CONTEXT_CONTROL | + CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT; ASSERT_EQ(context.ContextFlags & must_have, must_have); constexpr uint32_t may_have = CONTEXT_EXTENDED_REGISTERS; ASSERT_EQ(context.ContextFlags & ~(must_have | may_have), 0u); #elif defined(ARCH_CPU_X86_64) - ASSERT_EQ(context.ContextFlags, - static_cast(CONTEXT_AMD64 | CONTEXT_CONTROL | - CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT)); + ASSERT_EQ( + context.ContextFlags, + static_cast(CONTEXT_AMD64 | CONTEXT_CONTROL | CONTEXT_INTEGER | + CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT)); #endif #if defined(ARCH_CPU_X86_FAMILY) @@ -107,8 +94,7 @@ void SanityCheckContext(const CONTEXT& context) { #endif } -// A CPU-independent function to return the program counter. -uintptr_t ProgramCounterFromContext(const CONTEXT& context) { +uintptr_t ProgramCounterFromContext(const NativeCPUContext& context) { #if defined(ARCH_CPU_X86) return context.Eip; #elif defined(ARCH_CPU_X86_64) @@ -116,8 +102,7 @@ uintptr_t ProgramCounterFromContext(const CONTEXT& context) { #endif } -// A CPU-independent function to return the stack pointer. -uintptr_t StackPointerFromContext(const CONTEXT& context) { +uintptr_t StackPointerFromContext(const NativeCPUContext& context) { #if defined(ARCH_CPU_X86) return context.Esp; #elif defined(ARCH_CPU_X86_64) @@ -125,56 +110,5 @@ uintptr_t StackPointerFromContext(const CONTEXT& context) { #endif } -void TestCaptureContext() { - CONTEXT context_1; - CaptureContext(&context_1); - - { - SCOPED_TRACE("context_1"); - ASSERT_NO_FATAL_FAILURE(SanityCheckContext(context_1)); - } - - // The program counter reference value is this function’s address. The - // captured program counter should be slightly greater than or equal to the - // reference program counter. - uintptr_t pc = ProgramCounterFromContext(context_1); - - // Declare sp and context_2 here because all local variables need to be - // declared before computing the stack pointer reference value, so that the - // reference value can be the lowest value possible. - uintptr_t sp; - CONTEXT context_2; - - // The stack pointer reference value is the lowest address of a local variable - // in this function. The captured program counter will be slightly less than - // or equal to the reference stack pointer. - const uintptr_t kReferenceSP = - std::min(std::min(reinterpret_cast(&context_1), - reinterpret_cast(&context_2)), - std::min(reinterpret_cast(&pc), - reinterpret_cast(&sp))); - sp = StackPointerFromContext(context_1); - EXPECT_LT(kReferenceSP - sp, 512u); - - // Capture the context again, expecting that the stack pointer stays the same - // and the program counter increases. Strictly speaking, there’s no guarantee - // that these conditions will hold, although they do for known compilers even - // under typical optimization. - CaptureContext(&context_2); - - { - SCOPED_TRACE("context_2"); - ASSERT_NO_FATAL_FAILURE(SanityCheckContext(context_2)); - } - - EXPECT_EQ(StackPointerFromContext(context_2), sp); - EXPECT_GT(ProgramCounterFromContext(context_2), pc); -} - -TEST(CaptureContextWin, CaptureContext) { - ASSERT_NO_FATAL_FAILURE(TestCaptureContext()); -} - -} // namespace } // namespace test } // namespace crashpad diff --git a/util/win/capture_context.asm b/util/misc/capture_context_win.asm similarity index 100% rename from util/win/capture_context.asm rename to util/misc/capture_context_win.asm diff --git a/util/util.gyp b/util/util.gyp index 0000ee2d..877fa980 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -125,6 +125,10 @@ 'misc/address_types.h', 'misc/arraysize_unsafe.h', 'misc/as_underlying_type.h', + 'misc/capture_context.h', + 'misc/capture_context_linux.S', + 'misc/capture_context_mac.S', + 'misc/capture_context_win.asm', 'misc/clock.h', 'misc/clock_mac.cc', 'misc/clock_posix.cc', @@ -232,8 +236,6 @@ 'thread/worker_thread.cc', 'thread/worker_thread.h', 'win/address_types.h', - 'win/capture_context.asm', - 'win/capture_context.h', 'win/checked_win_address_range.h', 'win/command_line.cc', 'win/command_line.h', @@ -340,6 +342,8 @@ '$(SDKROOT)/usr/lib/libbsm.dylib', ], }, + }, { # else: OS!=mac + 'sources!': [ 'misc/capture_context_mac.S' ], }], ['OS=="win"', { 'link_settings': { @@ -369,7 +373,7 @@ ], }, { # else: OS!="win" 'sources!': [ - 'win/capture_context.asm', + 'misc/capture_context_win.asm', 'win/safe_terminate_process.asm', ], }], @@ -381,6 +385,7 @@ }, }, { # else: OS!="linux" 'sources!': [ + 'misc/capture_context_linux.S', 'net/http_transport_libcurl.cc', ], }], @@ -394,6 +399,7 @@ ['OS=="android"', { 'sources/': [ ['include', '^linux/'], + ['include', '^misc/capture_context_linux\\.S$'], ['include', '^misc/paths_linux\\.cc$'], ['include', '^posix/process_info_linux\\.cc$'], ['include', '^process/process_memory_linux\\.cc$'], diff --git a/util/util_test.gyp b/util/util_test.gyp index a2873270..d07dc82a 100644 --- a/util/util_test.gyp +++ b/util/util_test.gyp @@ -67,6 +67,11 @@ 'mach/symbolic_constants_mach_test.cc', 'mach/task_memory_test.cc', 'misc/arraysize_unsafe_test.cc', + 'misc/capture_context_test.cc', + 'misc/capture_context_test_util.h', + 'misc/capture_context_test_util_linux.cc', + 'misc/capture_context_test_util_mac.cc', + 'misc/capture_context_test_util_win.cc', 'misc/clock_test.cc', 'misc/from_pointer_cast_test.cc', 'misc/initialization_state_dcheck_test.cc', @@ -105,7 +110,6 @@ 'thread/thread_log_messages_test.cc', 'thread/thread_test.cc', 'thread/worker_thread_test.cc', - 'win/capture_context_test.cc', 'win/command_line_test.cc', 'win/critical_section_with_debug_info_test.cc', 'win/exception_handler_server_test.cc', @@ -156,6 +160,7 @@ ['OS=="android"', { 'sources/': [ ['include', '^linux/'], + ['include', '^misc/capture_context_test_util_linux\\.cc$'], ], }], ], diff --git a/util/win/capture_context.h b/util/win/capture_context.h deleted file mode 100644 index 2f501f80..00000000 --- a/util/win/capture_context.h +++ /dev/null @@ -1,47 +0,0 @@ -// 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_CLIENT_CAPTURE_CONTEXT_WIN_H_ -#define CRASHPAD_CLIENT_CAPTURE_CONTEXT_WIN_H_ - -#include - -namespace crashpad { - -//! \brief Saves the CPU context. -//! -//! The CPU context will be captured as accurately and completely as possible, -//! containing an atomic snapshot at the point of this function’s return. This -//! function does not modify any registers. -//! -//! This function captures all integer registers as well as the floating-point -//! and vector (SSE) state. It does not capture debug registers, which are -//! inaccessible by user code. -//! -//! This function is a replacement for `RtlCaptureContext()`, which contains -//! bugs and limitations. On 32-bit x86, `RtlCaptureContext()` requires that -//! `ebp` be used as a frame pointer, and returns `ebp`, `esp`, and `eip` out of -//! sync with the other registers. Both the 32-bit x86 and 64-bit x86_64 -//! versions of `RtlCaptureContext()` capture only the state of the integer -//! registers, ignoring floating-point and vector state. -//! -//! \param[out] context The structure to store the context in. -//! -//! \note On x86_64, the value for `rcx` will be populated with the address of -//! this function’s argument, as mandated by the ABI. -void CaptureContext(CONTEXT* context); - -} // namespace crashpad - -#endif // CRASHPAD_CLIENT_CAPTURE_CONTEXT_WIN_H_ From a8ad3bdbdfb118775b44f69909bd0609c29d8c23 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Fri, 9 Feb 2018 11:44:48 -0800 Subject: [PATCH 150/326] linux: fix incorrect fallthrough Previously, an error would have been logged twice. Bug: crashpad:30 Change-Id: I9445c022550ad14497186c6878863fbf72d8cd59 Reviewed-on: https://chromium-review.googlesource.com/911822 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- util/linux/ptracer.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/linux/ptracer.cc b/util/linux/ptracer.cc index 20115819..84447362 100644 --- a/util/linux/ptracer.cc +++ b/util/linux/ptracer.cc @@ -285,9 +285,9 @@ size_t GetGeneralPurposeRegistersAndLength(pid_t tid, switch (errno) { #if defined(ARCH_CPU_ARMEL) case EIO: - if (GetGeneralPurposeRegistersLegacy(tid, context, can_log)) { - return sizeof(context->t32); - } + return GetGeneralPurposeRegistersLegacy(tid, context, can_log) + ? sizeof(context->t32) + : 0; #endif // ARCH_CPU_ARMEL default: PLOG_IF(ERROR, can_log) << "ptrace"; From 73e862e15a6c3c7ed42866e6c0bbc1b15a97186f Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 13 Feb 2018 11:46:18 -0800 Subject: [PATCH 151/326] fuchsia: Exclude capture_context_test.cc from test build Pending a definition of NativeCPUContext, and an implementation of CaptureContext(). Bug: crashpad:196 Change-Id: Ibd7721cb740d7662379bb6b22e7804738e16c724 Reviewed-on: https://chromium-review.googlesource.com/916902 Reviewed-by: Joshua Peraza Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- util/BUILD.gn | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/util/BUILD.gn b/util/BUILD.gn index 586ed537..a1c1309a 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -432,7 +432,6 @@ source_set("util_test") { "file/filesystem_test.cc", "file/string_file_test.cc", "misc/arraysize_unsafe_test.cc", - "misc/capture_context_test.cc", "misc/capture_context_test_util.h", "misc/clock_test.cc", "misc/from_pointer_cast_test.cc", @@ -467,6 +466,14 @@ source_set("util_test") { "thread/worker_thread_test.cc", ] + if (!crashpad_is_fuchsia) { + sources += [ + # No NativeCPUContext defined for Fuchsia yet. + # https://crashpad.chromium.org/bug/196. + "misc/capture_context_test.cc", + ] + } + if (!crashpad_is_android && !crashpad_is_fuchsia) { # Android and Fuchsia will each require an HTTPTransport implementation # (libcurl isn’t in either’s SDK) and a solution to From b83f4c731d8f9af962fd42e2f106ef6ac87abb39 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 13 Feb 2018 12:09:33 -0800 Subject: [PATCH 152/326] Let UUID::InitializeFromString accept StringPiece16 too Split out of crrev.com/c/689745 by jperaza, with a simple test added. It is useful for this to be an overload instead of a separate signature so that code that extracts a UUID string out of a filename can treat it generically between Windows and non-Windows. Bug: crashpad:196, crashpad:206 Change-Id: I0d7d84a93d9526d1aae8839179dfe903acca091b Reviewed-on: https://chromium-review.googlesource.com/916885 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- util/misc/uuid.cc | 4 ++++ util/misc/uuid.h | 1 + util/misc/uuid_test.cc | 25 +++++++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/util/misc/uuid.cc b/util/misc/uuid.cc index 92fba760..ffd49708 100644 --- a/util/misc/uuid.cc +++ b/util/misc/uuid.cc @@ -84,6 +84,10 @@ bool UUID::InitializeFromString(const base::StringPiece& string) { return true; } +bool UUID::InitializeFromString(const base::StringPiece16& string) { + return InitializeFromString(UTF16ToUTF8(string)); +} + bool UUID::InitializeWithNew() { #if defined(OS_MACOSX) uuid_t uuid; diff --git a/util/misc/uuid.h b/util/misc/uuid.h index 4e5884e2..af801222 100644 --- a/util/misc/uuid.h +++ b/util/misc/uuid.h @@ -63,6 +63,7 @@ struct UUID { //! been initialized with the data. `false` if the string could not be //! parsed, with the object state untouched. bool InitializeFromString(const base::StringPiece& string); + bool InitializeFromString(const base::StringPiece16& string); //! \brief Initializes the %UUID using a standard system facility to generate //! the value. diff --git a/util/misc/uuid_test.cc b/util/misc/uuid_test.cc index 72b8216b..c05c5c1b 100644 --- a/util/misc/uuid_test.cc +++ b/util/misc/uuid_test.cc @@ -214,6 +214,31 @@ TEST(UUID, FromString) { // Mixed case. uuid.InitializeFromString("5762C15D-50b5-4171-a2e9-7429C9EC6CAB"); EXPECT_EQ(uuid.ToString(), "5762c15d-50b5-4171-a2e9-7429c9ec6cab"); + + // Test accepting a StringPiece16. + // clang-format off + static constexpr base::char16 kChar16UUID[] = { + 'f', '3', '2', 'e', '5', 'b', 'd', 'c', '-', + '2', '6', '8', '1', '-', + '4', 'c', '7', '3', '-', + 'a', '4', 'e', '6', '-', + '3', '3', '3', 'f', 'f', 'd', '3', '3', 'b', '3', '3', '3', + }; + // clang-format on + EXPECT_TRUE(uuid.InitializeFromString( + base::StringPiece16(kChar16UUID, arraysize(kChar16UUID)))); + EXPECT_EQ(uuid.ToString(), "f32e5bdc-2681-4c73-a4e6-333ffd33b333"); + +#if defined(OS_WIN) + // Test accepting a StringPiece16 via L"" literals on Windows. + EXPECT_TRUE( + uuid.InitializeFromString(L"F32E5BDC-2681-4C73-A4E6-444FFD44B444")); + EXPECT_EQ(uuid.ToString(), "f32e5bdc-2681-4c73-a4e6-444ffd44b444"); + + EXPECT_TRUE( + uuid.InitializeFromString(L"5762C15D-50b5-4171-a2e9-5555C5EC5CAB")); + EXPECT_EQ(uuid.ToString(), "5762c15d-50b5-4171-a2e9-5555c5ec5cab"); +#endif // OS_WIN } #if defined(OS_WIN) From f878f155172b9fe149af050fedc391d16af00c4d Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 13 Feb 2018 12:36:18 -0800 Subject: [PATCH 153/326] fuchsia: Add flock() stub to get test binaries linking again Because the SDK isn't pinned, this broke recently: https://build.chromium.org/p/client.crashpad/builders/crashpad_fuchsia_x64_dbg/builds/59/steps/compile%20with%20ninja/logs/stdio due to changes in limiting exported symbols, flock() being one of the casualties: https://fuchsia.googlesource.com/zircon/+log/HEAD/third_party/ulib/musl/exported.map Temporarily add a stub local definition here, as excluding LoggingLockFile() is needed by Settings, which is used downstream by a bunch of things, and so requires a lot of build file gymnastics to exclude. Once there's a Fuchsia implementation of Settings, this can be deleted and LoggingLock/UnlockFile can be #if'd out for Fuchsia. Bug: crashpad:196 Change-Id: I0971736572b940c8bc2364c01dafc6844f9303d7 Reviewed-on: https://chromium-review.googlesource.com/917083 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- util/file/file_io_posix.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/util/file/file_io_posix.cc b/util/file/file_io_posix.cc index 2993279d..bb6e87ea 100644 --- a/util/file/file_io_posix.cc +++ b/util/file/file_io_posix.cc @@ -157,6 +157,19 @@ FileHandle LoggingOpenFileForReadAndWrite(const base::FilePath& path, return fd; } +#if defined(OS_FUCHSIA) +int flock(int, int) { + // TODO(scottmg): https://crashpad.chromium.org/bug/196: + // This was removed from the libc of Fuchsia recently. A new implementation of + // Settings is being worked on that doesn't require flock(), but until then, + // it's more useful to have it link, but fail at runtime than it is to exclude + // a lot of code (Settings, which requires excludes the CrashReportDatabase, + // which requires excluding a variety of tests, the handler, and so on.). + NOTREACHED(); + return ENOSYS; +} +#endif // OS_FUCHSIA + bool LoggingLockFile(FileHandle file, FileLocking locking) { int operation = (locking == FileLocking::kShared) ? LOCK_SH : LOCK_EX; int rv = HANDLE_EINTR(flock(file, operation)); From e5bbdaff87a9dc1ffba88ab017c98d348f5560c3 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 13 Feb 2018 14:25:14 -0800 Subject: [PATCH 154/326] Pass FilePath to Settings in Initialize() Pulled out of jperaza's https://crrev.com/c/689745. Future updates to the CrashReportDatabase would like to be decide on the Settings location later than the constructor, but still keep the Settings object embedded inline. To allow this, pass the location FilePath in Initialize() rather than to the constructor. Bug: crashpad:206 Change-Id: I8792188314541f6fd0bd04b168d22f8e445bc187 Reviewed-on: https://chromium-review.googlesource.com/916533 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- client/crash_report_database_mac.mm | 4 ++-- client/crash_report_database_win.cc | 8 ++------ client/settings.cc | 12 +++++------- client/settings.h | 12 ++++++++++-- client/settings_test.cc | 28 ++++++++++++++-------------- 5 files changed, 33 insertions(+), 31 deletions(-) diff --git a/client/crash_report_database_mac.mm b/client/crash_report_database_mac.mm index 7a9154b8..3bba2b63 100644 --- a/client/crash_report_database_mac.mm +++ b/client/crash_report_database_mac.mm @@ -243,7 +243,7 @@ class CrashReportDatabaseMac : public CrashReportDatabase { CrashReportDatabaseMac::CrashReportDatabaseMac(const base::FilePath& path) : CrashReportDatabase(), base_dir_(path), - settings_(base_dir_.Append(kSettings)), + settings_(), xattr_new_names_(false), initialized_() { } @@ -268,7 +268,7 @@ bool CrashReportDatabaseMac::Initialize(bool may_create) { return false; } - if (!settings_.Initialize()) + if (!settings_.Initialize(base_dir_.Append(kSettings))) return false; // Do an xattr operation as the last step, to ensure the filesystem has diff --git a/client/crash_report_database_win.cc b/client/crash_report_database_win.cc index 538eff5f..d6699e1b 100644 --- a/client/crash_report_database_win.cc +++ b/client/crash_report_database_win.cc @@ -610,11 +610,7 @@ class CrashReportDatabaseWin : public CrashReportDatabase { }; CrashReportDatabaseWin::CrashReportDatabaseWin(const base::FilePath& path) - : CrashReportDatabase(), - base_dir_(path), - settings_(base_dir_.Append(kSettings)), - initialized_() { -} + : CrashReportDatabase(), base_dir_(path), settings_(), initialized_() {} CrashReportDatabaseWin::~CrashReportDatabaseWin() { } @@ -634,7 +630,7 @@ bool CrashReportDatabaseWin::Initialize(bool may_create) { if (!CreateDirectoryIfNecessary(base_dir_.Append(kReportsDirectory))) return false; - if (!settings_.Initialize()) + if (!settings_.Initialize(base_dir_.Append(kSettings))) return false; INITIALIZATION_STATE_SET_VALID(initialized_); diff --git a/client/settings.cc b/client/settings.cc index 15d16f2e..8d4dbec8 100644 --- a/client/settings.cc +++ b/client/settings.cc @@ -59,16 +59,14 @@ struct Settings::Data { UUID client_id; }; -Settings::Settings(const base::FilePath& file_path) - : file_path_(file_path), - initialized_() { -} +Settings::Settings() = default; -Settings::~Settings() { -} +Settings::~Settings() = default; -bool Settings::Initialize() { +bool Settings::Initialize(const base::FilePath& file_path) { + DCHECK(initialized_.is_uninitialized()); initialized_.set_invalid(); + file_path_ = file_path; Data settings; if (!OpenForWritingAndReadSettings(&settings).is_valid()) diff --git a/client/settings.h b/client/settings.h index b64f74fb..488080c1 100644 --- a/client/settings.h +++ b/client/settings.h @@ -44,10 +44,18 @@ struct ScopedLockedFileHandleTraits { //! should be retrieved via CrashReportDatabase::GetSettings(). class Settings { public: - explicit Settings(const base::FilePath& file_path); + Settings(); ~Settings(); - bool Initialize(); + //! \brief Initializes the settings data store. + //! + //! This method must be called only once, and must be successfully called + //! before any other method in this class may be called. + //! + //! \param[in] path The location to store the settings data. + //! \return `true` if the data store was initialized successfully, otherwise + //! `false` with an error logged. + bool Initialize(const base::FilePath& path); //! \brief Retrieves the immutable identifier for this client, which is used //! on a server to locate all crash reports from a specific Crashpad diff --git a/client/settings_test.cc b/client/settings_test.cc index ca961a23..3a5730bd 100644 --- a/client/settings_test.cc +++ b/client/settings_test.cc @@ -26,7 +26,7 @@ namespace { class SettingsTest : public testing::Test { public: - SettingsTest() : settings_(settings_path()) {} + SettingsTest() = default; base::FilePath settings_path() { return temp_dir_.path().Append(FILE_PATH_LITERAL("settings")); @@ -49,7 +49,7 @@ class SettingsTest : public testing::Test { protected: // testing::Test: void SetUp() override { - ASSERT_TRUE(settings()->Initialize()); + ASSERT_TRUE(settings()->Initialize(settings_path())); } private: @@ -64,8 +64,8 @@ TEST_F(SettingsTest, ClientID) { EXPECT_TRUE(settings()->GetClientID(&client_id)); EXPECT_NE(client_id, UUID()); - Settings local_settings(settings_path()); - EXPECT_TRUE(local_settings.Initialize()); + Settings local_settings; + EXPECT_TRUE(local_settings.Initialize(settings_path())); UUID actual; EXPECT_TRUE(local_settings.GetClientID(&actual)); EXPECT_EQ(actual, client_id); @@ -81,8 +81,8 @@ TEST_F(SettingsTest, UploadsEnabled) { EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled)); EXPECT_TRUE(enabled); - Settings local_settings(settings_path()); - EXPECT_TRUE(local_settings.Initialize()); + Settings local_settings; + EXPECT_TRUE(local_settings.Initialize(settings_path())); enabled = false; EXPECT_TRUE(local_settings.GetUploadsEnabled(&enabled)); EXPECT_TRUE(enabled); @@ -107,8 +107,8 @@ TEST_F(SettingsTest, LastUploadAttemptTime) { EXPECT_TRUE(settings()->GetLastUploadAttemptTime(&actual)); EXPECT_EQ(actual, expected); - Settings local_settings(settings_path()); - EXPECT_TRUE(local_settings.Initialize()); + Settings local_settings; + EXPECT_TRUE(local_settings.Initialize(settings_path())); actual = -1; EXPECT_TRUE(local_settings.GetLastUploadAttemptTime(&actual)); EXPECT_EQ(actual, expected); @@ -120,8 +120,8 @@ TEST_F(SettingsTest, LastUploadAttemptTime) { TEST_F(SettingsTest, BadFileOnInitialize) { InitializeBadFile(); - Settings settings(settings_path()); - EXPECT_TRUE(settings.Initialize()); + Settings settings; + EXPECT_TRUE(settings.Initialize(settings_path())); } TEST_F(SettingsTest, BadFileOnGet) { @@ -131,8 +131,8 @@ TEST_F(SettingsTest, BadFileOnGet) { EXPECT_TRUE(settings()->GetClientID(&client_id)); EXPECT_NE(client_id, UUID()); - Settings local_settings(settings_path()); - EXPECT_TRUE(local_settings.Initialize()); + Settings local_settings; + EXPECT_TRUE(local_settings.Initialize(settings_path())); UUID actual; EXPECT_TRUE(local_settings.GetClientID(&actual)); EXPECT_EQ(actual, client_id); @@ -161,8 +161,8 @@ TEST_F(SettingsTest, UnlinkFile) { << ErrnoMessage("unlink"); #endif - Settings local_settings(settings_path()); - EXPECT_TRUE(local_settings.Initialize()); + Settings local_settings; + EXPECT_TRUE(local_settings.Initialize(settings_path())); UUID new_client_id; EXPECT_TRUE(local_settings.GetClientID(&new_client_id)); EXPECT_NE(new_client_id, client_id); From c45ba7920e01e89b8e9c00dbe588b4d73804d057 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 13 Feb 2018 17:28:11 -0800 Subject: [PATCH 155/326] Make NewReport objects own their associated database resources This change updates CrashReportDatbase::NewReport objects to own the file handle associated with the new report, now accessible via a FileWriter. NewReport's destructor closes its file handle and removes its new report unless disarmed with FinishedWritingCrashReport, eliminating the need for CallErrorWritingCrashReport. Bug: crashpad:206 Change-Id: Iccb5bbc0ebadb07a237ff8eb938389afcfeae2a5 Reviewed-on: https://chromium-review.googlesource.com/916941 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai Reviewed-by: Scott Graham --- client/crash_report_database.cc | 37 ++++--- client/crash_report_database.h | 104 +++++++----------- client/crash_report_database_mac.mm | 96 +++++----------- client/crash_report_database_test.cc | 46 +++----- client/crash_report_database_win.cc | 65 +++-------- client/prune_crash_reports_test.cc | 12 +- handler/mac/crash_report_exception_handler.cc | 17 +-- handler/win/crash_report_exception_handler.cc | 17 +-- tools/crashpad_database_util.cc | 12 +- util/misc/metrics.cc | 3 +- util/misc/metrics.h | 2 +- 11 files changed, 149 insertions(+), 262 deletions(-) diff --git a/client/crash_report_database.cc b/client/crash_report_database.cc index 8451e469..860807f7 100644 --- a/client/crash_report_database.cc +++ b/client/crash_report_database.cc @@ -14,6 +14,8 @@ #include "client/crash_report_database.h" +#include "build/build_config.h" + namespace crashpad { CrashReportDatabase::Report::Report() @@ -26,22 +28,31 @@ CrashReportDatabase::Report::Report() upload_attempts(0), upload_explicitly_requested(false) {} -CrashReportDatabase::CallErrorWritingCrashReport::CallErrorWritingCrashReport( - CrashReportDatabase* database, - NewReport* new_report) - : database_(database), - new_report_(new_report) { -} +CrashReportDatabase::NewReport::NewReport() + : writer_(std::make_unique()), uuid_(), file_remover_() {} -CrashReportDatabase::CallErrorWritingCrashReport:: - ~CallErrorWritingCrashReport() { - if (new_report_) { - database_->ErrorWritingCrashReport(new_report_); +CrashReportDatabase::NewReport::~NewReport() = default; + +bool CrashReportDatabase::NewReport::Initialize( + const base::FilePath& directory, + const base::FilePath::StringType& extension) { + if (!uuid_.InitializeWithNew()) { + return false; } -} -void CrashReportDatabase::CallErrorWritingCrashReport::Disarm() { - new_report_ = nullptr; +#if defined(OS_WIN) + const std::wstring uuid_string = uuid_.ToString16(); +#else + const std::string uuid_string = uuid_.ToString(); +#endif + + const base::FilePath path = directory.Append(uuid_string + extension); + if (!writer_->Open( + path, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)) { + return false; + } + file_remover_.reset(path); + return true; } } // namespace crashpad diff --git a/client/crash_report_database.h b/client/crash_report_database.h index 62117899..a7d35ee1 100644 --- a/client/crash_report_database.h +++ b/client/crash_report_database.h @@ -24,6 +24,8 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "util/file/file_io.h" +#include "util/file/file_writer.h" +#include "util/file/scoped_remove_file.h" #include "util/misc/metrics.h" #include "util/misc/uuid.h" @@ -98,44 +100,32 @@ class CrashReportDatabase { //! \brief A crash report that is in the process of being written. //! - //! An instance of this struct should be created via PrepareNewCrashReport() - //! and destroyed with FinishedWritingCrashReport(). - struct NewReport { - //! The file handle to which the report should be written. - FileHandle handle; + //! An instance of this struct should be created via PrepareNewCrashReport(). + class NewReport { + public: + NewReport(); + ~NewReport(); + + //! An open FileWriter with which to write the report. + FileWriter* Writer() const { return writer_.get(); } //! A unique identifier by which this report will always be known to the //! database. - UUID uuid; - - //! The path to the crash report being written. - base::FilePath path; - }; - - //! \brief A scoper to cleanly handle the interface requirement imposed by - //! PrepareNewCrashReport(). - //! - //! Calls ErrorWritingCrashReport() upon destruction unless disarmed by - //! calling Disarm(). Armed upon construction. - class CallErrorWritingCrashReport { - public: - //! \brief Arms the object to call ErrorWritingCrashReport() on \a database - //! with an argument of \a new_report on destruction. - CallErrorWritingCrashReport(CrashReportDatabase* database, - NewReport* new_report); - - //! \brief Calls ErrorWritingCrashReport() if the object is armed. - ~CallErrorWritingCrashReport(); - - //! \brief Disarms the object so that CallErrorWritingCrashReport() will not - //! be called upon destruction. - void Disarm(); + const UUID& ReportID() { return uuid_; } private: - CrashReportDatabase* database_; // weak - NewReport* new_report_; // weak + friend class CrashReportDatabase; + friend class CrashReportDatabaseMac; + friend class CrashReportDatabaseWin; - DISALLOW_COPY_AND_ASSIGN(CallErrorWritingCrashReport); + bool Initialize(const base::FilePath& directory, + const base::FilePath::StringType& extension); + + std::unique_ptr writer_; + UUID uuid_; + ScopedRemoveFile file_remover_; + + DISALLOW_COPY_AND_ASSIGN(NewReport); }; //! \brief The result code for operations performed on a database. @@ -217,49 +207,31 @@ class CrashReportDatabase { //! \brief Creates a record of a new crash report. //! - //! Callers can then write the crash report using the file handle provided. - //! The caller does not own the new crash report record or its file handle, - //! both of which must be explicitly disposed of by calling - //! FinishedWritingCrashReport() or ErrorWritingCrashReport(). + //! Callers should write the crash report using the FileWriter provided. + //! Callers should then call FinishedWritingCrashReport() to complete report + //! creation. If an error is encountered while writing the crash report, no + //! special action needs to be taken. If FinishedWritingCrashReport() is not + //! called, the report will be removed from the database when \a report is + //! destroyed. //! - //! To arrange to call ErrorWritingCrashReport() during any early return, use - //! CallErrorWritingCrashReport. - //! - //! \param[out] report A NewReport object containing a file handle to which - //! the crash report data should be written. Only valid if this returns - //! #kNoError. The caller must not delete the NewReport object or close - //! the file handle within. + //! \param[out] report A NewReport object containing a FileWriter with which + //! to write the report data. Only valid if this returns #kNoError. //! //! \return The operation status code. - virtual OperationStatus PrepareNewCrashReport(NewReport** report) = 0; + virtual OperationStatus PrepareNewCrashReport( + std::unique_ptr* report) = 0; - //! \brief Informs the database that a crash report has been written. - //! - //! After calling this method, the database is permitted to move and rename - //! the file at NewReport::path. + //! \brief Informs the database that a crash report has been successfully + //! written. //! //! \param[in] report A NewReport obtained with PrepareNewCrashReport(). The - //! NewReport object and file handle within will be invalidated as part of - //! this call. + //! NewReport object will be invalidated as part of this call. //! \param[out] uuid The UUID of this crash report. //! //! \return The operation status code. - virtual OperationStatus FinishedWritingCrashReport(NewReport* report, - UUID* uuid) = 0; - - //! \brief Informs the database that an error occurred while attempting to - //! write a crash report, and that any resources associated with it should - //! be cleaned up. - //! - //! After calling this method, the database is permitted to remove the file at - //! NewReport::path. - //! - //! \param[in] report A NewReport obtained with PrepareNewCrashReport(). The - //! NewReport object and file handle within will be invalidated as part of - //! this call. - //! - //! \return The operation status code. - virtual OperationStatus ErrorWritingCrashReport(NewReport* report) = 0; + virtual OperationStatus FinishedWritingCrashReport( + std::unique_ptr report, + UUID* uuid) = 0; //! \brief Returns the crash report record for the unique identifier. //! diff --git a/client/crash_report_database_mac.mm b/client/crash_report_database_mac.mm index 3bba2b63..011ff03e 100644 --- a/client/crash_report_database_mac.mm +++ b/client/crash_report_database_mac.mm @@ -107,6 +107,8 @@ std::string XattrNameInternal(const base::StringPiece& name, bool new_name) { name.data()); } +} // namespace + //! \brief A CrashReportDatabase that uses HFS+ extended attributes to store //! report metadata. //! @@ -130,10 +132,10 @@ class CrashReportDatabaseMac : public CrashReportDatabase { // CrashReportDatabase: Settings* GetSettings() override; - OperationStatus PrepareNewCrashReport(NewReport** report) override; - OperationStatus FinishedWritingCrashReport(NewReport* report, + OperationStatus PrepareNewCrashReport( + std::unique_ptr* report) override; + OperationStatus FinishedWritingCrashReport(std::unique_ptr report, UUID* uuid) override; - OperationStatus ErrorWritingCrashReport(NewReport* report) override; OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override; OperationStatus GetPendingReports(std::vector* reports) override; OperationStatus GetCompletedReports(std::vector* reports) override; @@ -301,103 +303,67 @@ Settings* CrashReportDatabaseMac::GetSettings() { } CrashReportDatabase::OperationStatus -CrashReportDatabaseMac::PrepareNewCrashReport(NewReport** out_report) { +CrashReportDatabaseMac::PrepareNewCrashReport( + std::unique_ptr* out_report) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::unique_ptr report(new NewReport()); - - uuid_t uuid_gen; - uuid_generate(uuid_gen); - report->uuid.InitializeFromBytes(uuid_gen); - - report->path = - base_dir_.Append(kWriteDirectory) - .Append(report->uuid.ToString() + "." + kCrashReportFileExtension); - - report->handle = HANDLE_EINTR( - open(report->path.value().c_str(), - O_WRONLY | O_EXLOCK | O_CREAT | O_EXCL | O_NOCTTY | O_CLOEXEC, - 0600)); - if (report->handle < 0) { - PLOG(ERROR) << "open " << report->path.value(); + if (!report->Initialize(base_dir_.Append(kWriteDirectory), + std::string(".") + kCrashReportFileExtension)) { return kFileSystemError; } // TODO(rsesek): Potentially use an fsetxattr() here instead. - if (!WriteXattr( - report->path, XattrName(kXattrUUID), report->uuid.ToString())) { - PLOG_IF(ERROR, IGNORE_EINTR(close(report->handle)) != 0) << "close"; + if (!WriteXattr(report->file_remover_.get(), + XattrName(kXattrUUID), + report->ReportID().ToString())) { return kDatabaseError; } - *out_report = report.release(); - + out_report->reset(report.release()); return kNoError; } CrashReportDatabase::OperationStatus -CrashReportDatabaseMac::FinishedWritingCrashReport(NewReport* report, - UUID* uuid) { +CrashReportDatabaseMac::FinishedWritingCrashReport( + std::unique_ptr report, + UUID* uuid) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - // Takes ownership of the |handle| and the O_EXLOCK. - base::ScopedFD lock(report->handle); - - // Take ownership of the report. - std::unique_ptr scoped_report(report); + const base::FilePath& path = report->file_remover_.get(); // Get the report's UUID to return. std::string uuid_string; - if (ReadXattr(report->path, XattrName(kXattrUUID), - &uuid_string) != XattrStatus::kOK || + if (ReadXattr(path, XattrName(kXattrUUID), &uuid_string) != + XattrStatus::kOK || !uuid->InitializeFromString(uuid_string)) { - LOG(ERROR) << "Failed to read UUID for crash report " - << report->path.value(); + LOG(ERROR) << "Failed to read UUID for crash report " << path.value(); return kDatabaseError; } - if (*uuid != report->uuid) { - LOG(ERROR) << "UUID mismatch for crash report " << report->path.value(); + if (*uuid != report->ReportID()) { + LOG(ERROR) << "UUID mismatch for crash report " << path.value(); return kDatabaseError; } // Record the creation time of this report. - if (!WriteXattrTimeT(report->path, XattrName(kXattrCreationTime), - time(nullptr))) { + if (!WriteXattrTimeT(path, XattrName(kXattrCreationTime), time(nullptr))) { return kDatabaseError; } + FileOffset size = report->Writer()->Seek(0, SEEK_END); + // Move the report to its new location for uploading. base::FilePath new_path = - base_dir_.Append(kUploadPendingDirectory).Append(report->path.BaseName()); - if (rename(report->path.value().c_str(), new_path.value().c_str()) != 0) { - PLOG(ERROR) << "rename " << report->path.value() << " to " - << new_path.value(); + base_dir_.Append(kUploadPendingDirectory).Append(path.BaseName()); + if (rename(path.value().c_str(), new_path.value().c_str()) != 0) { + PLOG(ERROR) << "rename " << path.value() << " to " << new_path.value(); return kFileSystemError; } + ignore_result(report->file_remover_.release()); Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated); - Metrics::CrashReportSize(report->handle); - - return kNoError; -} - -CrashReportDatabase::OperationStatus -CrashReportDatabaseMac::ErrorWritingCrashReport(NewReport* report) { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - - // Takes ownership of the |handle| and the O_EXLOCK. - base::ScopedFD lock(report->handle); - - // Take ownership of the report. - std::unique_ptr scoped_report(report); - - // Remove the file that the report would have been written to had no error - // occurred. - if (unlink(report->path.value().c_str()) != 0) { - PLOG(ERROR) << "unlink " << report->path.value(); - return kFileSystemError; - } + Metrics::CrashReportSize(size); return kNoError; } @@ -774,8 +740,6 @@ std::unique_ptr InitializeInternal( return std::unique_ptr(database_mac.release()); } -} // namespace - // static std::unique_ptr CrashReportDatabase::Initialize( const base::FilePath& path) { diff --git a/client/crash_report_database_test.cc b/client/crash_report_database_test.cc index c4266961..d84b23fc 100644 --- a/client/crash_report_database_test.cc +++ b/client/crash_report_database_test.cc @@ -14,13 +14,13 @@ #include "client/crash_report_database.h" -#include "build/build_config.h" #include "client/settings.h" #include "gtest/gtest.h" #include "test/errors.h" #include "test/file.h" #include "test/scoped_temp_dir.h" #include "util/file/file_io.h" +#include "util/file/filesystem.h" namespace crashpad { namespace test { @@ -48,20 +48,19 @@ class CrashReportDatabaseTest : public testing::Test { } void CreateCrashReport(CrashReportDatabase::Report* report) { - CrashReportDatabase::NewReport* new_report = nullptr; + std::unique_ptr new_report; ASSERT_EQ(db_->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); static constexpr char kTest[] = "test"; - ASSERT_TRUE(LoggingWriteFile(new_report->handle, kTest, sizeof(kTest))); + ASSERT_TRUE(new_report->Writer()->Write(kTest, sizeof(kTest))); UUID uuid; - EXPECT_EQ(db_->FinishedWritingCrashReport(new_report, &uuid), + EXPECT_EQ(db_->FinishedWritingCrashReport(std::move(new_report), &uuid), CrashReportDatabase::kNoError); EXPECT_EQ(db_->LookUpCrashReport(uuid, report), CrashReportDatabase::kNoError); ExpectPreparedCrashReport(*report); - ASSERT_TRUE(FileExists(report->file_path)); } void UploadReport(const UUID& uuid, bool successful, const std::string& id) { @@ -176,13 +175,12 @@ TEST_F(CrashReportDatabaseTest, Initialize) { } TEST_F(CrashReportDatabaseTest, NewCrashReport) { - CrashReportDatabase::NewReport* new_report; + std::unique_ptr new_report; EXPECT_EQ(db()->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); - UUID expect_uuid = new_report->uuid; - EXPECT_TRUE(FileExists(new_report->path)) << new_report->path.value(); + UUID expect_uuid = new_report->ReportID(); UUID uuid; - EXPECT_EQ(db()->FinishedWritingCrashReport(new_report, &uuid), + EXPECT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), CrashReportDatabase::kNoError); EXPECT_EQ(uuid, expect_uuid); @@ -201,17 +199,6 @@ TEST_F(CrashReportDatabaseTest, NewCrashReport) { EXPECT_TRUE(reports.empty()); } -TEST_F(CrashReportDatabaseTest, ErrorWritingCrashReport) { - CrashReportDatabase::NewReport* new_report = nullptr; - ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), - CrashReportDatabase::kNoError); - base::FilePath new_report_path = new_report->path; - EXPECT_TRUE(FileExists(new_report_path)) << new_report_path.value(); - EXPECT_EQ(db()->ErrorWritingCrashReport(new_report), - CrashReportDatabase::kNoError); - EXPECT_FALSE(FileExists(new_report_path)) << new_report_path.value(); -} - TEST_F(CrashReportDatabaseTest, LookUpCrashReport) { UUID uuid; @@ -495,12 +482,11 @@ TEST_F(CrashReportDatabaseTest, UploadAlreadyUploaded) { } TEST_F(CrashReportDatabaseTest, MoveDatabase) { - CrashReportDatabase::NewReport* new_report; + std::unique_ptr new_report; EXPECT_EQ(db()->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); - EXPECT_TRUE(FileExists(new_report->path)) << new_report->path.value(); UUID uuid; - EXPECT_EQ(db()->FinishedWritingCrashReport(new_report, &uuid), + EXPECT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), CrashReportDatabase::kNoError); RelocateDatabase(); @@ -509,28 +495,22 @@ TEST_F(CrashReportDatabaseTest, MoveDatabase) { EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), CrashReportDatabase::kNoError); ExpectPreparedCrashReport(report); - EXPECT_TRUE(FileExists(report.file_path)) << report.file_path.value(); } TEST_F(CrashReportDatabaseTest, ReportRemoved) { - CrashReportDatabase::NewReport* new_report; + std::unique_ptr new_report; EXPECT_EQ(db()->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); - EXPECT_TRUE(FileExists(new_report->path)) << new_report->path.value(); + UUID uuid; - EXPECT_EQ(db()->FinishedWritingCrashReport(new_report, &uuid), + EXPECT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), CrashReportDatabase::kNoError); CrashReportDatabase::Report report; EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), CrashReportDatabase::kNoError); -#if defined(OS_WIN) - EXPECT_EQ(_wunlink(report.file_path.value().c_str()), 0); -#else - EXPECT_EQ(unlink(report.file_path.value().c_str()), 0) - << ErrnoMessage("unlink"); -#endif + EXPECT_TRUE(LoggingRemoveFile(report.file_path)); EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), CrashReportDatabase::kReportNotFound); diff --git a/client/crash_report_database_win.cc b/client/crash_report_database_win.cc index d6699e1b..ee0832e3 100644 --- a/client/crash_report_database_win.cc +++ b/client/crash_report_database_win.cc @@ -571,6 +571,8 @@ bool CreateDirectoryIfNecessary(const base::FilePath& path) { return EnsureDirectory(path); } +} // namespace + // CrashReportDatabaseWin ------------------------------------------------------ class CrashReportDatabaseWin : public CrashReportDatabase { @@ -582,10 +584,10 @@ class CrashReportDatabaseWin : public CrashReportDatabase { // CrashReportDatabase: Settings* GetSettings() override; - OperationStatus PrepareNewCrashReport(NewReport** report) override; - OperationStatus FinishedWritingCrashReport(NewReport* report, + OperationStatus PrepareNewCrashReport( + std::unique_ptr* report) override; + OperationStatus FinishedWritingCrashReport(std::unique_ptr report, UUID* uuid) override; - OperationStatus ErrorWritingCrashReport(NewReport* report) override; OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override; OperationStatus GetPendingReports(std::vector* reports) override; OperationStatus GetCompletedReports(std::vector* reports) override; @@ -643,67 +645,38 @@ Settings* CrashReportDatabaseWin::GetSettings() { } OperationStatus CrashReportDatabaseWin::PrepareNewCrashReport( - NewReport** report) { + std::unique_ptr* report) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::unique_ptr new_report(new NewReport()); - if (!new_report->uuid.InitializeWithNew()) - return kFileSystemError; - new_report->path = base_dir_.Append(kReportsDirectory) - .Append(new_report->uuid.ToString16() + L"." + - kCrashReportFileExtension); - new_report->handle = LoggingOpenFileForWrite(new_report->path, - FileWriteMode::kCreateOrFail, - FilePermissions::kOwnerOnly); - if (new_report->handle == INVALID_HANDLE_VALUE) + if (!new_report->Initialize(base_dir_.Append(kReportsDirectory), + std::wstring(L".") + kCrashReportFileExtension)) { return kFileSystemError; + } - *report = new_report.release(); + report->reset(new_report.release()); return kNoError; } OperationStatus CrashReportDatabaseWin::FinishedWritingCrashReport( - NewReport* report, + std::unique_ptr report, UUID* uuid) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - // Take ownership of the report. - std::unique_ptr scoped_report(report); - // Take ownership of the file handle. - ScopedFileHandle handle(report->handle); - std::unique_ptr metadata(AcquireMetadata()); if (!metadata) return kDatabaseError; - metadata->AddNewRecord(ReportDisk(scoped_report->uuid, - scoped_report->path, + metadata->AddNewRecord(ReportDisk(report->ReportID(), + report->file_remover_.get(), time(nullptr), ReportState::kPending)); - *uuid = scoped_report->uuid; + + ignore_result(report->file_remover_.release()); + + *uuid = report->ReportID(); Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated); - Metrics::CrashReportSize(handle.get()); - - return kNoError; -} - -OperationStatus CrashReportDatabaseWin::ErrorWritingCrashReport( - NewReport* report) { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - - // Take ownership of the report. - std::unique_ptr scoped_report(report); - - // Close the outstanding handle. - LoggingCloseFile(report->handle); - - // We failed to write, so remove the dump file. There's no entry in the - // metadata table yet. - if (!DeleteFile(scoped_report->path.value().c_str())) { - PLOG(ERROR) << "DeleteFile " - << base::UTF16ToUTF8(scoped_report->path.value()); - return kFileSystemError; - } + Metrics::CrashReportSize(report->Writer()->Seek(0, SEEK_END)); return kNoError; } @@ -899,8 +872,6 @@ OperationStatus CrashReportDatabaseWin::RequestUpload(const UUID& uuid) { return kNoError; } -} // namespace - // static std::unique_ptr CrashReportDatabase::Initialize( const base::FilePath& path) { diff --git a/client/prune_crash_reports_test.cc b/client/prune_crash_reports_test.cc index 54d6941e..e5e5a41a 100644 --- a/client/prune_crash_reports_test.cc +++ b/client/prune_crash_reports_test.cc @@ -36,9 +36,8 @@ class MockDatabase : public CrashReportDatabase { public: // CrashReportDatabase: MOCK_METHOD0(GetSettings, Settings*()); - MOCK_METHOD1(PrepareNewCrashReport, OperationStatus(NewReport**)); - MOCK_METHOD2(FinishedWritingCrashReport, OperationStatus(NewReport*, UUID*)); - MOCK_METHOD1(ErrorWritingCrashReport, OperationStatus(NewReport*)); + MOCK_METHOD1(PrepareNewCrashReport, + OperationStatus(std::unique_ptr*)); MOCK_METHOD2(LookUpCrashReport, OperationStatus(const UUID&, Report*)); MOCK_METHOD1(GetPendingReports, OperationStatus(std::vector*)); MOCK_METHOD1(GetCompletedReports, OperationStatus(std::vector*)); @@ -50,6 +49,13 @@ class MockDatabase : public CrashReportDatabase { OperationStatus(const UUID&, Metrics::CrashSkippedReason)); MOCK_METHOD1(DeleteReport, OperationStatus(const UUID&)); MOCK_METHOD1(RequestUpload, OperationStatus(const UUID&)); + + // gmock doesn't support mocking methods with non-copyable types such as + // unique_ptr. + OperationStatus FinishedWritingCrashReport(std::unique_ptr report, + UUID* uuid) override { + return kNoError; + } }; time_t NDaysAgo(int num_days) { diff --git a/handler/mac/crash_report_exception_handler.cc b/handler/mac/crash_report_exception_handler.cc index 6f9cdbe6..2317df24 100644 --- a/handler/mac/crash_report_exception_handler.cc +++ b/handler/mac/crash_report_exception_handler.cc @@ -14,6 +14,7 @@ #include "handler/mac/crash_report_exception_handler.h" +#include #include #include "base/logging.h" @@ -155,7 +156,7 @@ kern_return_t CrashReportExceptionHandler::CatchMachException( process_snapshot.SetClientID(client_id); process_snapshot.SetAnnotationsSimpleMap(*process_annotations_); - CrashReportDatabase::NewReport* new_report; + std::unique_ptr new_report; CrashReportDatabase::OperationStatus database_status = database_->PrepareNewCrashReport(&new_report); if (database_status != CrashReportDatabase::kNoError) { @@ -164,28 +165,22 @@ kern_return_t CrashReportExceptionHandler::CatchMachException( return KERN_FAILURE; } - process_snapshot.SetReportID(new_report->uuid); - - CrashReportDatabase::CallErrorWritingCrashReport - call_error_writing_crash_report(database_, new_report); - - WeakFileHandleFileWriter file_writer(new_report->handle); + process_snapshot.SetReportID(new_report->ReportID()); MinidumpFileWriter minidump; minidump.InitializeFromSnapshot(&process_snapshot); AddUserExtensionStreams( user_stream_data_sources_, &process_snapshot, &minidump); - if (!minidump.WriteEverything(&file_writer)) { + if (!minidump.WriteEverything(new_report->Writer())) { Metrics::ExceptionCaptureResult( Metrics::CaptureResult::kMinidumpWriteFailed); return KERN_FAILURE; } - call_error_writing_crash_report.Disarm(); - UUID uuid; - database_status = database_->FinishedWritingCrashReport(new_report, &uuid); + database_status = + database_->FinishedWritingCrashReport(std::move(new_report), &uuid); if (database_status != CrashReportDatabase::kNoError) { Metrics::ExceptionCaptureResult( Metrics::CaptureResult::kFinishedWritingCrashReportFailed); diff --git a/handler/win/crash_report_exception_handler.cc b/handler/win/crash_report_exception_handler.cc index 0ab206c1..b1ea8446 100644 --- a/handler/win/crash_report_exception_handler.cc +++ b/handler/win/crash_report_exception_handler.cc @@ -15,6 +15,7 @@ #include "handler/win/crash_report_exception_handler.h" #include +#include #include "client/crash_report_database.h" #include "client/settings.h" @@ -90,7 +91,7 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException( process_snapshot.SetClientID(client_id); process_snapshot.SetAnnotationsSimpleMap(*process_annotations_); - CrashReportDatabase::NewReport* new_report; + std::unique_ptr new_report; CrashReportDatabase::OperationStatus database_status = database_->PrepareNewCrashReport(&new_report); if (database_status != CrashReportDatabase::kNoError) { @@ -100,29 +101,23 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException( return termination_code; } - process_snapshot.SetReportID(new_report->uuid); - - CrashReportDatabase::CallErrorWritingCrashReport - call_error_writing_crash_report(database_, new_report); - - WeakFileHandleFileWriter file_writer(new_report->handle); + process_snapshot.SetReportID(new_report->ReportID()); MinidumpFileWriter minidump; minidump.InitializeFromSnapshot(&process_snapshot); AddUserExtensionStreams( user_stream_data_sources_, &process_snapshot, &minidump); - if (!minidump.WriteEverything(&file_writer)) { + if (!minidump.WriteEverything(new_report->Writer())) { LOG(ERROR) << "WriteEverything failed"; Metrics::ExceptionCaptureResult( Metrics::CaptureResult::kMinidumpWriteFailed); return termination_code; } - call_error_writing_crash_report.Disarm(); - UUID uuid; - database_status = database_->FinishedWritingCrashReport(new_report, &uuid); + database_status = + database_->FinishedWritingCrashReport(std::move(new_report), &uuid); if (database_status != CrashReportDatabase::kNoError) { LOG(ERROR) << "FinishedWritingCrashReport failed"; Metrics::ExceptionCaptureResult( diff --git a/tools/crashpad_database_util.cc b/tools/crashpad_database_util.cc index cc21698b..b4c2a15b 100644 --- a/tools/crashpad_database_util.cc +++ b/tools/crashpad_database_util.cc @@ -584,16 +584,13 @@ int DatabaseUtilMain(int argc, char* argv[]) { file_reader = std::move(file_path_reader); } - CrashReportDatabase::NewReport* new_report; + std::unique_ptr new_report; CrashReportDatabase::OperationStatus status = database->PrepareNewCrashReport(&new_report); if (status != CrashReportDatabase::kNoError) { return EXIT_FAILURE; } - CrashReportDatabase::CallErrorWritingCrashReport - call_error_writing_crash_report(database.get(), new_report); - char buf[4096]; FileOperationResult read_result; do { @@ -601,16 +598,13 @@ int DatabaseUtilMain(int argc, char* argv[]) { if (read_result < 0) { return EXIT_FAILURE; } - if (read_result > 0 && - !LoggingWriteFile(new_report->handle, buf, read_result)) { + if (read_result > 0 && !new_report->Writer()->Write(buf, read_result)) { return EXIT_FAILURE; } } while (read_result > 0); - call_error_writing_crash_report.Disarm(); - UUID uuid; - status = database->FinishedWritingCrashReport(new_report, &uuid); + status = database->FinishedWritingCrashReport(std::move(new_report), &uuid); if (status != CrashReportDatabase::kNoError) { return EXIT_FAILURE; } diff --git a/util/misc/metrics.cc b/util/misc/metrics.cc index f4fb5882..7d191f71 100644 --- a/util/misc/metrics.cc +++ b/util/misc/metrics.cc @@ -61,8 +61,7 @@ void Metrics::CrashReportPending(PendingReportReason reason) { } // static -void Metrics::CrashReportSize(FileHandle file) { - const FileOffset size = LoggingFileSizeByHandle(file); +void Metrics::CrashReportSize(FileOffset size) { UMA_HISTOGRAM_CUSTOM_COUNTS( "Crashpad.CrashReportSize", size, 0, 20 * 1024 * 1024, 50); } diff --git a/util/misc/metrics.h b/util/misc/metrics.h index b4bea910..fbbf3ecb 100644 --- a/util/misc/metrics.h +++ b/util/misc/metrics.h @@ -50,7 +50,7 @@ class Metrics { //! \brief Reports the size of a crash report file in bytes. Should be called //! when a new report is written to disk. - static void CrashReportSize(FileHandle file); + static void CrashReportSize(FileOffset size); //! \brief Reports on a crash upload attempt, and if it succeeded. static void CrashUploadAttempted(bool successful); From 7d5487fc44b3c58e3b02ce83ad2dc6125b1d95fb Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 14 Feb 2018 17:07:04 -0800 Subject: [PATCH 156/326] minidump: add switch cases to handle linux/android and ARM Bug: crashpad:30 Change-Id: Ib2ff936451a68415f39f6a19f561d2f536daf8d3 Reviewed-on: https://chromium-review.googlesource.com/920786 Reviewed-by: Scott Graham Commit-Queue: Joshua Peraza --- minidump/minidump_system_info_writer.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/minidump/minidump_system_info_writer.cc b/minidump/minidump_system_info_writer.cc index 9c665f4f..4a22b436 100644 --- a/minidump/minidump_system_info_writer.cc +++ b/minidump/minidump_system_info_writer.cc @@ -123,6 +123,12 @@ void MinidumpSystemInfoWriter::InitializeFromSnapshot( case kCPUArchitectureX86_64: cpu_architecture = kMinidumpCPUArchitectureAMD64; break; + case kCPUArchitectureARM: + cpu_architecture = kMinidumpCPUArchitectureARM; + break; + case kCPUArchitectureARM64: + cpu_architecture = kMinidumpCPUArchitectureARM64; + break; default: NOTREACHED(); cpu_architecture = kMinidumpCPUArchitectureUnknown; @@ -160,6 +166,12 @@ void MinidumpSystemInfoWriter::InitializeFromSnapshot( case SystemSnapshot::kOperatingSystemWindows: operating_system = kMinidumpOSWin32NT; break; + case SystemSnapshot::kOperatingSystemLinux: + operating_system = kMinidumpOSLinux; + break; + case SystemSnapshot::kOperatingSystemAndroid: + operating_system = kMinidumpOSAndroid; + break; default: NOTREACHED(); operating_system = kMinidumpOSUnknown; From c406797ce62083d84cc072e133c5c48fd63445fd Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 15 Feb 2018 08:17:12 -0800 Subject: [PATCH 157/326] Add UploadReport to manage database resources during upload This change adds CrashReportDatabase::UploadReport which owns the report's file handle during upload. An upload is recorded as a success by calling RecordUploadComplete(). If RecordUploadComplete() is not called, the operation is recorded as a failure when the UploadReport is destroyed. Bug: crashpad:206 Change-Id: I8385d08d52185ad30b06a3ed054de9812ae006a2 Reviewed-on: https://chromium-review.googlesource.com/917983 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai Reviewed-by: Robert Sesek --- client/crash_report_database.cc | 24 +++++++ client/crash_report_database.h | 91 +++++++++++++++++++-------- client/crash_report_database_mac.mm | 53 ++++++++-------- client/crash_report_database_test.cc | 24 ++++--- client/crash_report_database_win.cc | 42 ++++++------- client/prune_crash_reports_test.cc | 5 +- handler/crash_report_upload_thread.cc | 78 +++++------------------ handler/crash_report_upload_thread.h | 4 +- 8 files changed, 168 insertions(+), 153 deletions(-) diff --git a/client/crash_report_database.cc b/client/crash_report_database.cc index 860807f7..afd751d8 100644 --- a/client/crash_report_database.cc +++ b/client/crash_report_database.cc @@ -55,4 +55,28 @@ bool CrashReportDatabase::NewReport::Initialize( return true; } +CrashReportDatabase::UploadReport::UploadReport() + : Report(), reader_(std::make_unique()), database_(nullptr) {} + +CrashReportDatabase::UploadReport::~UploadReport() { + if (database_) { + database_->RecordUploadAttempt(this, false, std::string()); + } +} + +bool CrashReportDatabase::UploadReport::Initialize(const base::FilePath path, + CrashReportDatabase* db) { + database_ = db; + return reader_->Open(path); +} + +CrashReportDatabase::OperationStatus CrashReportDatabase::RecordUploadComplete( + std::unique_ptr report_in, + const std::string& id) { + UploadReport* report = const_cast(report_in.get()); + + report->database_ = nullptr; + return RecordUploadAttempt(report, true, id); +} + } // namespace crashpad diff --git a/client/crash_report_database.h b/client/crash_report_database.h index a7d35ee1..afde2817 100644 --- a/client/crash_report_database.h +++ b/client/crash_report_database.h @@ -24,6 +24,7 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "util/file/file_io.h" +#include "util/file/file_reader.h" #include "util/file/file_writer.h" #include "util/file/scoped_remove_file.h" #include "util/misc/metrics.h" @@ -49,7 +50,7 @@ class Settings; //! processed, or it was has been brought back from 'Completed' state by //! user request. //! 3. Completed: The report has been locally processed, either by uploading -//! it to a collection server and calling RecordUploadAttempt(), or by +//! it to a collection server and calling RecordUploadComplete(), or by //! calling SkipReportUpload(). class CrashReportDatabase { public: @@ -100,7 +101,7 @@ class CrashReportDatabase { //! \brief A crash report that is in the process of being written. //! - //! An instance of this struct should be created via PrepareNewCrashReport(). + //! An instance of this class should be created via PrepareNewCrashReport(). class NewReport { public: NewReport(); @@ -128,6 +129,30 @@ class CrashReportDatabase { DISALLOW_COPY_AND_ASSIGN(NewReport); }; + //! \brief A crash report that is in the process of being uploaded. + //! + //! An instance of this class should be created via GetReportForUploading(). + class UploadReport : public Report { + public: + UploadReport(); + virtual ~UploadReport(); + + // An open FileReader with which to read the report. + FileReader* Reader() const { return reader_.get(); } + + private: + friend class CrashReportDatabase; + friend class CrashReportDatabaseMac; + friend class CrashReportDatabaseWin; + + bool Initialize(const base::FilePath path, CrashReportDatabase* database); + + std::unique_ptr reader_; + CrashReportDatabase* database_; + + DISALLOW_COPY_AND_ASSIGN(UploadReport); + }; + //! \brief The result code for operations performed on a database. enum OperationStatus { //! \brief No error occurred. @@ -260,42 +285,38 @@ class CrashReportDatabase { //! \return The operation status code. virtual OperationStatus GetCompletedReports(std::vector* reports) = 0; - //! \brief Obtains a report object for uploading to a collection server. + //! \brief Obtains and locks a report object for uploading to a collection + //! server. //! - //! The file at Report::file_path should be uploaded by the caller, and then - //! the returned Report object must be disposed of via a call to - //! RecordUploadAttempt(). - //! - //! A subsequent call to this method with the same \a uuid is illegal until - //! RecordUploadAttempt() has been called. + //! Callers should upload the crash report using the FileReader provided. + //! Callers should then call RecordUploadComplete() to record a successful + //! upload. If RecordUploadComplete() is not called, the upload attempt will + //! be recorded as unsuccessful and the report lock released when \a report is + //! destroyed. //! //! \param[in] uuid The unique identifier for the crash report record. //! \param[out] report A crash report record for the report to be uploaded. - //! The caller does not own this object. Only valid if this returns - //! #kNoError. + //! Only valid if this returns #kNoError. //! //! \return The operation status code. - virtual OperationStatus GetReportForUploading(const UUID& uuid, - const Report** report) = 0; + virtual OperationStatus GetReportForUploading( + const UUID& uuid, + std::unique_ptr* report) = 0; - //! \brief Adjusts a crash report record’s metadata to account for an upload - //! attempt, and updates the last upload attempt time as returned by + //! \brief Records a successful upload for a report and updates the last + //! upload attempt time as returned by //! Settings::GetLastUploadAttemptTime(). //! - //! After calling this method, the database is permitted to move and rename - //! the file at Report::file_path. - //! - //! \param[in] report The report object obtained from - //! GetReportForUploading(). This object is invalidated after this call. - //! \param[in] successful Whether the upload attempt was successful. - //! \param[in] id The identifier assigned to this crash report by the - //! collection server. Must be empty if \a successful is `false`; may be - //! empty if it is `true`. + //! \param[in] report A UploadReport object obtained from + //! GetReportForUploading(). The UploadReport object will be invalidated + //! and the report unlocked as part of this call. + //! \param[in] id The possibly empty identifier assigned to this crash report + //! by the collection server. //! //! \return The operation status code. - virtual OperationStatus RecordUploadAttempt(const Report* report, - bool successful, - const std::string& id) = 0; + OperationStatus RecordUploadComplete( + std::unique_ptr report, + const std::string& id); //! \brief Moves a report from the pending state to the completed state, but //! without the report being uploaded. @@ -331,6 +352,22 @@ class CrashReportDatabase { CrashReportDatabase() {} private: + //! \brief Adjusts a crash report record’s metadata to account for an upload + //! attempt, and updates the last upload attempt time as returned by + //! Settings::GetLastUploadAttemptTime(). + //! + //! \param[in] report The report object obtained from + //! GetReportForUploading(). + //! \param[in] successful Whether the upload attempt was successful. + //! \param[in] id The identifier assigned to this crash report by the + //! collection server. Must be empty if \a successful is `false`; may be + //! empty if it is `true`. + //! + //! \return The operation status code. + virtual OperationStatus RecordUploadAttempt(UploadReport* report, + bool successful, + const std::string& id) = 0; + DISALLOW_COPY_AND_ASSIGN(CrashReportDatabase); }; diff --git a/client/crash_report_database_mac.mm b/client/crash_report_database_mac.mm index 011ff03e..d0197fce 100644 --- a/client/crash_report_database_mac.mm +++ b/client/crash_report_database_mac.mm @@ -139,17 +139,20 @@ class CrashReportDatabaseMac : public CrashReportDatabase { OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override; OperationStatus GetPendingReports(std::vector* reports) override; OperationStatus GetCompletedReports(std::vector* reports) override; - OperationStatus GetReportForUploading(const UUID& uuid, - const Report** report) override; - OperationStatus RecordUploadAttempt(const Report* report, - bool successful, - const std::string& id) override; + OperationStatus GetReportForUploading( + const UUID& uuid, + std::unique_ptr* report) override; OperationStatus SkipReportUpload(const UUID& uuid, Metrics::CrashSkippedReason reason) override; OperationStatus DeleteReport(const UUID& uuid) override; OperationStatus RequestUpload(const UUID& uuid) override; private: + // CrashReportDatabase: + OperationStatus RecordUploadAttempt(UploadReport* report, + bool successful, + const std::string& id) override; + //! \brief Report states for use with LocateCrashReport(). //! //! ReportState may be considered to be a bitfield. @@ -163,10 +166,10 @@ class CrashReportDatabaseMac : public CrashReportDatabase { //! \brief A private extension of the Report class that maintains bookkeeping //! information of the database. - struct UploadReport : public Report { + struct UploadReportMac : public UploadReport { //! \brief Stores the flock of the file for the duration of //! GetReportForUploading() and RecordUploadAttempt(). - int lock_fd; + base::ScopedFD lock_fd; }; //! \brief Locates a crash report in the database by UUID. @@ -406,31 +409,36 @@ CrashReportDatabaseMac::GetCompletedReports( } CrashReportDatabase::OperationStatus -CrashReportDatabaseMac::GetReportForUploading(const UUID& uuid, - const Report** report) { +CrashReportDatabaseMac::GetReportForUploading( + const UUID& uuid, + std::unique_ptr* report) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - base::FilePath report_path = LocateCrashReport(uuid, kReportStatePending); - if (report_path.empty()) + auto upload_report = std::make_unique(); + + upload_report->file_path = LocateCrashReport(uuid, kReportStatePending); + if (upload_report->file_path.empty()) return kReportNotFound; - std::unique_ptr upload_report(new UploadReport()); - upload_report->file_path = report_path; - - base::ScopedFD lock(ObtainReportLock(report_path)); + base::ScopedFD lock(ObtainReportLock(upload_report->file_path)); if (!lock.is_valid()) return kBusyError; - if (!ReadReportMetadataLocked(report_path, upload_report.get())) + if (!ReadReportMetadataLocked(upload_report->file_path, upload_report.get())) return kDatabaseError; - upload_report->lock_fd = lock.release(); - *report = upload_report.release(); + if (!upload_report->reader_->Open(upload_report->file_path)) { + return kFileSystemError; + } + + upload_report->database_ = this; + upload_report->lock_fd.reset(lock.release()); + report->reset(upload_report.release()); return kNoError; } CrashReportDatabase::OperationStatus -CrashReportDatabaseMac::RecordUploadAttempt(const Report* report, +CrashReportDatabaseMac::RecordUploadAttempt(UploadReport* report, bool successful, const std::string& id) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); @@ -445,13 +453,6 @@ CrashReportDatabaseMac::RecordUploadAttempt(const Report* report, if (report_path.empty()) return kReportNotFound; - std::unique_ptr upload_report( - static_cast(report)); - - base::ScopedFD lock(upload_report->lock_fd); - if (!lock.is_valid()) - return kBusyError; - if (successful) { CrashReportDatabase::OperationStatus os = MarkReportCompletedLocked(report_path, &report_path); diff --git a/client/crash_report_database_test.cc b/client/crash_report_database_test.cc index d84b23fc..31087b93 100644 --- a/client/crash_report_database_test.cc +++ b/client/crash_report_database_test.cc @@ -69,15 +69,19 @@ class CrashReportDatabaseTest : public testing::Test { time_t times[2]; ASSERT_TRUE(settings->GetLastUploadAttemptTime(×[0])); - const CrashReportDatabase::Report* report = nullptr; + std::unique_ptr report; ASSERT_EQ(db_->GetReportForUploading(uuid, &report), CrashReportDatabase::kNoError); EXPECT_NE(report->uuid, UUID()); EXPECT_FALSE(report->file_path.empty()); EXPECT_TRUE(FileExists(report->file_path)) << report->file_path.value(); EXPECT_GT(report->creation_time, 0); - EXPECT_EQ(db_->RecordUploadAttempt(report, successful, id), - CrashReportDatabase::kNoError); + if (successful) { + EXPECT_EQ(db_->RecordUploadComplete(std::move(report), id), + CrashReportDatabase::kNoError); + } else { + report.reset(); + } ASSERT_TRUE(settings->GetLastUploadAttemptTime(×[1])); EXPECT_NE(times[1], 0); @@ -452,16 +456,16 @@ TEST_F(CrashReportDatabaseTest, DuelingUploads) { CrashReportDatabase::Report report; CreateCrashReport(&report); - const CrashReportDatabase::Report* upload_report; + std::unique_ptr upload_report; EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report), CrashReportDatabase::kNoError); - const CrashReportDatabase::Report* upload_report_2 = nullptr; + std::unique_ptr upload_report_2; EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report_2), CrashReportDatabase::kBusyError); EXPECT_FALSE(upload_report_2); - EXPECT_EQ(db()->RecordUploadAttempt(upload_report, true, std::string()), + EXPECT_EQ(db()->RecordUploadComplete(std::move(upload_report), std::string()), CrashReportDatabase::kNoError); } @@ -469,16 +473,16 @@ TEST_F(CrashReportDatabaseTest, UploadAlreadyUploaded) { CrashReportDatabase::Report report; CreateCrashReport(&report); - const CrashReportDatabase::Report* upload_report; + std::unique_ptr upload_report; EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report), CrashReportDatabase::kNoError); - EXPECT_EQ(db()->RecordUploadAttempt(upload_report, true, std::string()), + EXPECT_EQ(db()->RecordUploadComplete(std::move(upload_report), std::string()), CrashReportDatabase::kNoError); - const CrashReportDatabase::Report* upload_report_2 = nullptr; + std::unique_ptr upload_report_2; EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report_2), CrashReportDatabase::kReportNotFound); - EXPECT_FALSE(upload_report_2); + EXPECT_FALSE(upload_report_2.get()); } TEST_F(CrashReportDatabaseTest, MoveDatabase) { diff --git a/client/crash_report_database_win.cc b/client/crash_report_database_win.cc index ee0832e3..fb3cd8f7 100644 --- a/client/crash_report_database_win.cc +++ b/client/crash_report_database_win.cc @@ -29,6 +29,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "client/settings.h" +#include "util/misc/implicit_cast.h" #include "util/misc/initialization_state_dcheck.h" #include "util/misc/metrics.h" @@ -591,17 +592,20 @@ class CrashReportDatabaseWin : public CrashReportDatabase { OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override; OperationStatus GetPendingReports(std::vector* reports) override; OperationStatus GetCompletedReports(std::vector* reports) override; - OperationStatus GetReportForUploading(const UUID& uuid, - const Report** report) override; - OperationStatus RecordUploadAttempt(const Report* report, - bool successful, - const std::string& id) override; + OperationStatus GetReportForUploading( + const UUID& uuid, + std::unique_ptr* report) override; OperationStatus SkipReportUpload(const UUID& uuid, Metrics::CrashSkippedReason reason) override; OperationStatus DeleteReport(const UUID& uuid) override; OperationStatus RequestUpload(const UUID& uuid) override; private: + // CrashReportDatabase: + OperationStatus RecordUploadAttempt(UploadReport* report, + bool successful, + const std::string& id) override; + std::unique_ptr AcquireMetadata(); base::FilePath base_dir_; @@ -716,44 +720,38 @@ OperationStatus CrashReportDatabaseWin::GetCompletedReports( OperationStatus CrashReportDatabaseWin::GetReportForUploading( const UUID& uuid, - const Report** report) { + std::unique_ptr* report) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::unique_ptr metadata(AcquireMetadata()); if (!metadata) return kDatabaseError; - // TODO(scottmg): After returning this report to the client, there is no way - // to reap this report if the uploader fails to call RecordUploadAttempt() or - // SkipReportUpload() (if it crashed or was otherwise buggy). To resolve this, - // one possibility would be to change the interface to be FileHandle based, so - // that instead of giving the file_path back to the client and changing state - // to kUploading, we return an exclusive access handle, and use that as the - // signal that the upload is pending, rather than an update to state in the - // metadata. Alternatively, there could be a "garbage collection" at startup - // where any reports that are orphaned in the kUploading state are either - // reset to kPending to retry, or discarded. + ReportDisk* report_disk; OperationStatus os = metadata->FindSingleReportAndMarkDirty( uuid, ReportState::kPending, &report_disk); if (os == kNoError) { report_disk->state = ReportState::kUploading; - // Create a copy for passing back to client. This will be freed in - // RecordUploadAttempt. - *report = new Report(*report_disk); + auto upload_report = std::make_unique(); + *implicit_cast(upload_report.get()) = *report_disk; + + if (!upload_report->Initialize(upload_report->file_path, this)) { + return kFileSystemError; + } + + report->reset(upload_report.release()); } return os; } OperationStatus CrashReportDatabaseWin::RecordUploadAttempt( - const Report* report, + UploadReport* report, bool successful, const std::string& id) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); Metrics::CrashUploadAttempted(successful); - // Take ownership, allocated in GetReportForUploading. - std::unique_ptr upload_report(report); std::unique_ptr metadata(AcquireMetadata()); if (!metadata) return kDatabaseError; diff --git a/client/prune_crash_reports_test.cc b/client/prune_crash_reports_test.cc index e5e5a41a..2648dee2 100644 --- a/client/prune_crash_reports_test.cc +++ b/client/prune_crash_reports_test.cc @@ -42,9 +42,10 @@ class MockDatabase : public CrashReportDatabase { MOCK_METHOD1(GetPendingReports, OperationStatus(std::vector*)); MOCK_METHOD1(GetCompletedReports, OperationStatus(std::vector*)); MOCK_METHOD2(GetReportForUploading, - OperationStatus(const UUID&, const Report**)); + OperationStatus(const UUID&, + std::unique_ptr*)); MOCK_METHOD3(RecordUploadAttempt, - OperationStatus(const Report*, bool, const std::string&)); + OperationStatus(UploadReport*, bool, const std::string&)); MOCK_METHOD2(SkipReportUpload, OperationStatus(const UUID&, Metrics::CrashSkippedReason)); MOCK_METHOD1(DeleteReport, OperationStatus(const UUID&)); diff --git a/handler/crash_report_upload_thread.cc b/handler/crash_report_upload_thread.cc index 7505524b..8d7149ea 100644 --- a/handler/crash_report_upload_thread.cc +++ b/handler/crash_report_upload_thread.cc @@ -45,44 +45,6 @@ namespace crashpad { -namespace { - -// Calls CrashReportDatabase::RecordUploadAttempt() with |successful| set to -// false upon destruction unless disarmed by calling Fire() or Disarm(). Fire() -// triggers an immediate call. Armed upon construction. -class CallRecordUploadAttempt { - public: - CallRecordUploadAttempt(CrashReportDatabase* database, - const CrashReportDatabase::Report* report) - : database_(database), - report_(report) { - } - - ~CallRecordUploadAttempt() { - Fire(); - } - - void Fire() { - if (report_) { - database_->RecordUploadAttempt(report_, false, std::string()); - } - - Disarm(); - } - - void Disarm() { - report_ = nullptr; - } - - private: - CrashReportDatabase* database_; // weak - const CrashReportDatabase::Report* report_; // weak - - DISALLOW_COPY_AND_ASSIGN(CallRecordUploadAttempt); -}; - -} // namespace - CrashReportUploadThread::CrashReportUploadThread(CrashReportDatabase* database, const std::string& url, const Options& options) @@ -229,7 +191,7 @@ void CrashReportUploadThread::ProcessPendingReport( } } - const CrashReportDatabase::Report* upload_report; + std::unique_ptr upload_report; CrashReportDatabase::OperationStatus status = database_->GetReportForUploading(report.uuid, &upload_report); switch (status) { @@ -256,18 +218,16 @@ void CrashReportUploadThread::ProcessPendingReport( return; } - CallRecordUploadAttempt call_record_upload_attempt(database_, upload_report); - std::string response_body; - UploadResult upload_result = UploadReport(upload_report, &response_body); + UploadResult upload_result = + UploadReport(upload_report.get(), &response_body); switch (upload_result) { case UploadResult::kSuccess: - call_record_upload_attempt.Disarm(); - database_->RecordUploadAttempt(upload_report, true, response_body); + database_->RecordUploadComplete(std::move(upload_report), response_body); break; case UploadResult::kPermanentFailure: case UploadResult::kRetry: - call_record_upload_attempt.Fire(); + upload_report.reset(); // TODO(mark): Deal with retries properly: don’t call SkipReportUplaod() // if the result was kRetry and the report hasn’t already been retried @@ -279,17 +239,12 @@ void CrashReportUploadThread::ProcessPendingReport( } CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport( - const CrashReportDatabase::Report* report, + const CrashReportDatabase::UploadReport* report, std::string* response_body) { std::map parameters; - FileReader minidump_file_reader; - if (!minidump_file_reader.Open(report->file_path)) { - // If the minidump file can’t be opened, all hope is lost. - return UploadResult::kPermanentFailure; - } - - FileOffset start_offset = minidump_file_reader.SeekGet(); + FileReader* reader = report->Reader(); + FileOffset start_offset = reader->SeekGet(); if (start_offset < 0) { return UploadResult::kPermanentFailure; } @@ -299,12 +254,12 @@ CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport( // parameters, but as long as there’s a dump file, the server can decide what // to do with it. ProcessSnapshotMinidump minidump_process_snapshot; - if (minidump_process_snapshot.Initialize(&minidump_file_reader)) { + if (minidump_process_snapshot.Initialize(reader)) { parameters = BreakpadHTTPFormParametersFromMinidump(&minidump_process_snapshot); } - if (!minidump_file_reader.SeekSet(start_offset)) { + if (!reader->SeekSet(start_offset)) { return UploadResult::kPermanentFailure; } @@ -322,15 +277,10 @@ CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport( } } - http_multipart_builder.SetFileAttachment( - kMinidumpKey, -#if defined(OS_WIN) - base::UTF16ToUTF8(report->file_path.BaseName().value()), -#else - report->file_path.BaseName().value(), -#endif - &minidump_file_reader, - "application/octet-stream"); + http_multipart_builder.SetFileAttachment(kMinidumpKey, + report->uuid.ToString() + ".dmp", + reader, + "application/octet-stream"); std::unique_ptr http_transport(HTTPTransport::Create()); HTTPHeaders content_headers; diff --git a/handler/crash_report_upload_thread.h b/handler/crash_report_upload_thread.h index cdd1502b..69e7a3c2 100644 --- a/handler/crash_report_upload_thread.h +++ b/handler/crash_report_upload_thread.h @@ -148,14 +148,14 @@ class CrashReportUploadThread : public WorkerThread::Delegate { //! \param[in] report The report to upload. The caller is responsible for //! calling CrashReportDatabase::GetReportForUploading() before calling //! this method, and for calling - //! CrashReportDatabase::RecordUploadAttempt() after calling this method. + //! CrashReportDatabase::RecordUploadComplete() after calling this method. //! \param[out] response_body If the upload attempt is successful, this will //! be set to the response body sent by the server. Breakpad-type servers //! provide the crash ID assigned by the server in the response body. //! //! \return A member of UploadResult indicating the result of the upload //! attempt. - UploadResult UploadReport(const CrashReportDatabase::Report* report, + UploadResult UploadReport(const CrashReportDatabase::UploadReport* report, std::string* response_body); // WorkerThread::Delegate: From 7faa2ef8987230bcdd1f9c7814ee41afdb222b5e Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 15 Feb 2018 10:38:36 -0800 Subject: [PATCH 158/326] Get CrashpadInfo address via a .note, rather than dynamic symtab Embeds the address of g_crashpad_info into a .note section (which is readable by the generic code to read notes in ElfImageReader). Unfortunately because the note section is in libclient.a, it would normally be dropped at link time. To avoid that, GetCrashpadInfo() has a reference *back* to that section, which in turn forces the linker to include it, allowing the note reader to find it at runtime. Previously, it was necessary to have the embedder of "client" figure out how to cause `g_crashpad_info` to appear in the final module's dynamic symbol table. With this new approach, there's no manual configuration necessary, as it's not necessary for the symbol to be exported. This is currently only implemented in the Linux module reader (and I believe the current set of enabled tests aren't exercising it?) but it will also be done this way for the Fuchsia implementation of ModuleSnapshot. Bug: crashpad:196 Change-Id: I599db5903bc98303130d11ad850ba9ceed3b801a Reviewed-on: https://chromium-review.googlesource.com/912284 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai Reviewed-by: Joshua Peraza --- client/BUILD.gn | 4 ++ client/client.gyp | 5 +++ client/crashpad_info.cc | 19 +++++----- client/crashpad_info_note.S | 46 +++++++++++++++++++++++ snapshot/elf/elf_image_reader_test.cc | 11 +++--- snapshot/elf/elf_image_reader_test_note.S | 6 ++- snapshot/linux/module_snapshot_linux.cc | 29 +++++++++----- util/BUILD.gn | 1 + util/misc/elf_note_types.h | 34 +++++++++++++++++ util/util.gyp | 1 + 10 files changed, 130 insertions(+), 26 deletions(-) create mode 100644 client/crashpad_info_note.S create mode 100644 util/misc/elf_note_types.h diff --git a/client/BUILD.gn b/client/BUILD.gn index 92027a5b..e5d15472 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -47,6 +47,10 @@ static_library("client") { sources += [ "crashpad_client_linux.cc" ] } + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ "crashpad_info_note.S" ] + } + if (crashpad_is_win) { sources += [ "crash_report_database_win.cc", diff --git a/client/client.gyp b/client/client.gyp index 8fb1890b..5140813a 100644 --- a/client/client.gyp +++ b/client/client.gyp @@ -62,6 +62,11 @@ ], }, }], + ['OS=="linux" or OS=="android"', { + 'sources': [ + 'crashpad_info_note.S', + ], + }], ], 'direct_dependent_settings': { 'include_dirs': [ diff --git a/client/crashpad_info.cc b/client/crashpad_info.cc index b545a3cd..098b819a 100644 --- a/client/crashpad_info.cc +++ b/client/crashpad_info.cc @@ -52,12 +52,6 @@ static_assert(std::is_standard_layout::value, // because it’s POD, no code should need to run to initialize this under // release-mode optimization. -// Platforms that use ELF objects need to locate this structure via the dynamic -// symbol table, so avoid name mangling. -#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) -extern "C" { -#endif - #if defined(OS_POSIX) __attribute__(( @@ -96,12 +90,19 @@ __declspec(allocate("CPADinfo")) CrashpadInfo g_crashpad_info; -#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) -} // extern "C" -#endif +extern "C" int* CRASHPAD_NOTE_REFERENCE; // static CrashpadInfo* CrashpadInfo::GetCrashpadInfo() { +#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) + // This otherwise-unused reference is used so that any module that + // references GetCrashpadInfo() will also include the note in the + // .note.crashpad.info section. That note in turn contains the address of + // g_crashpad_info. This allows the module reader to find the CrashpadInfo + // structure without requiring the use of the dynamic symbol table. + static volatile int* pointer_to_note_section = CRASHPAD_NOTE_REFERENCE; + (void)pointer_to_note_section; +#endif return &g_crashpad_info; } diff --git a/client/crashpad_info_note.S b/client/crashpad_info_note.S new file mode 100644 index 00000000..fc65f8e2 --- /dev/null +++ b/client/crashpad_info_note.S @@ -0,0 +1,46 @@ +// Copyright 2018 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. + +// This note section is used on ELF platforms to give ElfImageReader a method +// of finding the instance of CrashpadInfo g_crashpad_info without requiring +// that symbol to be in the dynamic symbol table. + +#include "util/misc/elf_note_types.h" + +// namespace crashpad { +// CrashpadInfo g_crashpad_info; +// } // namespace crashpad +#define CRASHPAD_INFO_SYMBOL _ZN8crashpad15g_crashpad_infoE + +#define NOTE_ALIGN 4 + + // This section must be "a"llocated so that it appears in the final binary at + // runtime, and "w"ritable so that the relocation to CRASHPAD_INFO_SYMBOL can + // be performed. + .section .note.crashpad.info,"aw",%note + .balign NOTE_ALIGN + .globl CRASHPAD_NOTE_REFERENCE + .type CRASHPAD_NOTE_REFERENCE, %object +CRASHPAD_NOTE_REFERENCE: + .long name_end - name // namesz + .long desc_end - desc // descsz + .long CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO // type +name: + .asciz CRASHPAD_ELF_NOTE_NAME +name_end: + .balign NOTE_ALIGN +desc: + .quad CRASHPAD_INFO_SYMBOL +desc_end: + .size CRASHPAD_NOTE_REFERENCE, .-CRASHPAD_NOTE_REFERENCE diff --git a/snapshot/elf/elf_image_reader_test.cc b/snapshot/elf/elf_image_reader_test.cc index dc13ad6f..91cce068 100644 --- a/snapshot/elf/elf_image_reader_test.cc +++ b/snapshot/elf/elf_image_reader_test.cc @@ -27,6 +27,7 @@ #include "test/test_paths.h" #include "util/file/file_io.h" #include "util/misc/address_types.h" +#include "util/misc/elf_note_types.h" #include "util/misc/from_pointer_cast.h" #include "util/process/process_memory_native.h" @@ -163,14 +164,14 @@ void ReadThisExecutableInTarget(ProcessType process, ElfImageReader::NoteReader::Result::kNoMoreNotes); // Find the note defined in elf_image_reader_test_note.S. - constexpr char kCrashpadNoteName[] = "Crashpad"; - constexpr ElfImageReader::NoteReader::NoteType kCrashpadNoteType = 1; constexpr uint32_t kCrashpadNoteDesc = 42; - notes = reader.NotesWithNameAndType(kCrashpadNoteName, kCrashpadNoteType, -1); + notes = reader.NotesWithNameAndType( + CRASHPAD_ELF_NOTE_NAME, CRASHPAD_ELF_NOTE_TYPE_SNAPSHOT_TEST, -1); ASSERT_EQ(notes->NextNote(¬e_name, ¬e_type, ¬e_desc), ElfImageReader::NoteReader::Result::kSuccess); - EXPECT_EQ(note_name, kCrashpadNoteName); - EXPECT_EQ(note_type, kCrashpadNoteType); + EXPECT_EQ(note_name, CRASHPAD_ELF_NOTE_NAME); + EXPECT_EQ(note_type, + implicit_cast(CRASHPAD_ELF_NOTE_TYPE_SNAPSHOT_TEST)); EXPECT_EQ(note_desc.size(), sizeof(kCrashpadNoteDesc)); EXPECT_EQ(*reinterpret_cast(¬e_desc[0]), kCrashpadNoteDesc); diff --git a/snapshot/elf/elf_image_reader_test_note.S b/snapshot/elf/elf_image_reader_test_note.S index e41a5205..9ab03389 100644 --- a/snapshot/elf/elf_image_reader_test_note.S +++ b/snapshot/elf/elf_image_reader_test_note.S @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "util/misc/elf_note_types.h" + #define NOTE_ALIGN 4 .section .note.crashpad.test,"a",%note .balign NOTE_ALIGN @@ -19,9 +21,9 @@ testnote: .long name_end - name // namesz .long desc_end - desc // descsz - .long 1 // type + .long CRASHPAD_ELF_NOTE_TYPE_SNAPSHOT_TEST // type name: - .ascii "Crashpad\0" + .asciz CRASHPAD_ELF_NOTE_NAME name_end: .balign NOTE_ALIGN desc: diff --git a/snapshot/linux/module_snapshot_linux.cc b/snapshot/linux/module_snapshot_linux.cc index 0ddbebfa..11896137 100644 --- a/snapshot/linux/module_snapshot_linux.cc +++ b/snapshot/linux/module_snapshot_linux.cc @@ -18,6 +18,7 @@ #include "base/files/file_path.h" #include "snapshot/crashpad_types/image_annotation_reader.h" +#include "util/misc/elf_note_types.h" namespace crashpad { namespace internal { @@ -45,17 +46,25 @@ bool ModuleSnapshotLinux::Initialize( elf_reader_ = process_reader_module.elf_reader; type_ = process_reader_module.type; + // The data payload is only sizeof(VMAddress) in the note, but add a bit to + // account for the name, header, and padding. + constexpr ssize_t kMaxNoteSize = 256; + std::unique_ptr notes = + elf_reader_->NotesWithNameAndType(CRASHPAD_ELF_NOTE_NAME, + CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO, + kMaxNoteSize); + std::string desc; VMAddress info_address; - VMSize info_size; - if (elf_reader_->GetDynamicSymbol( - "g_crashpad_info", &info_address, &info_size)) { - ProcessMemoryRange range; - if (range.Initialize(*elf_reader_->Memory()) && - range.RestrictRange(info_address, info_size)) { - auto info = std::make_unique(); - if (info->Initialize(&range, info_address)) { - crashpad_info_ = std::move(info); - } + if (notes->NextNote(nullptr, nullptr, &desc) == + ElfImageReader::NoteReader::Result::kSuccess) { + info_address = *reinterpret_cast(&desc[0]); + } + + ProcessMemoryRange range; + if (range.Initialize(*elf_reader_->Memory())) { + auto info = std::make_unique(); + if (info->Initialize(&range, info_address)) { + crashpad_info_ = std::move(info); } } diff --git a/util/BUILD.gn b/util/BUILD.gn index a1c1309a..e39212cc 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -85,6 +85,7 @@ static_library("util") { "misc/as_underlying_type.h", "misc/capture_context.h", "misc/clock.h", + "misc/elf_note_types.h", "misc/from_pointer_cast.h", "misc/implicit_cast.h", "misc/initialization_state.h", diff --git a/util/misc/elf_note_types.h b/util/misc/elf_note_types.h new file mode 100644 index 00000000..77c9043b --- /dev/null +++ b/util/misc/elf_note_types.h @@ -0,0 +1,34 @@ +// Copyright 2018 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_UTIL_MISC_ELF_NOTE_TYPES_H_ +#define CRASHPAD_UTIL_MISC_ELF_NOTE_TYPES_H_ + +// This header defines types of ELF "notes" that are embedded sections. These +// can be read by ElfImageReader in the snapshot library, and are created in +// client modules. All notes used by Crashpad use the name "Crashpad" and one of +// the types defined here. Note that this file is #included into .S files, so +// must be relatively plain (no C++ features). + +#define CRASHPAD_ELF_NOTE_NAME "Crashpad" + +// Used by ElfImageReader for testing purposes. +#define CRASHPAD_ELF_NOTE_TYPE_SNAPSHOT_TEST 1 + +// Used by the client library to stash a pointer to the CrashpadInfo structure +// for retrieval by the module snapshot. 'OFNI' == 0x4f464e49 which appears as +// "INFO" in readelf -x. +#define CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO 0x4f464e49 + +#endif // CRASHPAD_UTIL_MISC_ELF_NOTE_TYPES_H_ diff --git a/util/util.gyp b/util/util.gyp index 877fa980..15925444 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -133,6 +133,7 @@ 'misc/clock_mac.cc', 'misc/clock_posix.cc', 'misc/clock_win.cc', + 'misc/elf_note_types.h', 'misc/from_pointer_cast.h', 'misc/implicit_cast.h', 'misc/initialization_state.h', From 8d0d999d9245ebc073db49ceadc9decec2705584 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 15 Feb 2018 11:10:10 -0800 Subject: [PATCH 159/326] Add a cross-platform database implementation This CL, based on https://chromium-review.googlesource.com/c/crashpad/crashpad/+/689745 adds a cross-platform database implementation side-by-side with the existing macOS and Windows implementations. The generic implementation is used for Linux, Android and Fuchsia. The database uses the directory structure from the macOS implementation, but stores report metadata in companion files for each report, rather than using filesystem attributes. The database uses lockfiles (companion files opened with O_EXCL) to protect report access because they are widely supported across filesystems. Lost lockfiles are removed after 3 days, along with any reports or metadata they were protecting. Bug: crashpad:206 Change-Id: I086e9001350e4446dd2f8c12fd3817377f509d3e Reviewed-on: https://chromium-review.googlesource.com/919527 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- client/BUILD.gn | 5 +- client/client.gyp | 1 + client/client_test.gyp | 3 + client/crash_report_database.h | 14 +- client/crash_report_database_fuchsia.cc | 35 - client/crash_report_database_generic.cc | 841 ++++++++++++++++++++++++ client/crash_report_database_test.cc | 72 ++ handler/prune_crash_reports_thread.cc | 1 + 8 files changed, 935 insertions(+), 37 deletions(-) delete mode 100644 client/crash_report_database_fuchsia.cc create mode 100644 client/crash_report_database_generic.cc diff --git a/client/BUILD.gn b/client/BUILD.gn index e5d15472..a9262b96 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -61,11 +61,14 @@ static_library("client") { if (crashpad_is_fuchsia) { sources += [ - "crash_report_database_fuchsia.cc", "crashpad_client_fuchsia.cc", ] } + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ "crash_report_database_generic.cc" ] + } + public_configs = [ "..:crashpad_config" ] deps = [ diff --git a/client/client.gyp b/client/client.gyp index 5140813a..ed50ba50 100644 --- a/client/client.gyp +++ b/client/client.gyp @@ -35,6 +35,7 @@ 'annotation_list.h', 'crash_report_database.cc', 'crash_report_database.h', + 'crash_report_database_generic.cc', 'crash_report_database_mac.mm', 'crash_report_database_win.cc', 'crashpad_client.h', diff --git a/client/client_test.gyp b/client/client_test.gyp index 75f67238..ab2626a0 100644 --- a/client/client_test.gyp +++ b/client/client_test.gyp @@ -51,6 +51,9 @@ '../handler/handler.gyp:crashpad_handler_console', ], }], + ['OS=="linux" or OS=="android"', + {'dependencies!': ['../handler/handler.gyp:crashpad_handler']}, + ], ], }, ], diff --git a/client/crash_report_database.h b/client/crash_report_database.h index afde2817..efa7a770 100644 --- a/client/crash_report_database.h +++ b/client/crash_report_database.h @@ -115,7 +115,7 @@ class CrashReportDatabase { const UUID& ReportID() { return uuid_; } private: - friend class CrashReportDatabase; + friend class CrashReportDatabaseGeneric; friend class CrashReportDatabaseMac; friend class CrashReportDatabaseWin; @@ -142,6 +142,7 @@ class CrashReportDatabase { private: friend class CrashReportDatabase; + friend class CrashReportDatabaseGeneric; friend class CrashReportDatabaseMac; friend class CrashReportDatabaseWin; @@ -348,6 +349,17 @@ class CrashReportDatabase { //! \return The operation status code. virtual OperationStatus RequestUpload(const UUID& uuid) = 0; + //! \brief Cleans the database of expired lockfiles, metadata without report + //! files, and report files without metadata. + //! + //! This method does nothing on the macOS and Windows implementations of the + //! database. + //! + //! \param[in] lockfile_ttl The number of seconds at which lockfiles or new + //! report files are considered expired. + //! \return The number of reports cleaned. + virtual int CleanDatabase(time_t lockfile_ttl) { return 0; } + protected: CrashReportDatabase() {} diff --git a/client/crash_report_database_fuchsia.cc b/client/crash_report_database_fuchsia.cc deleted file mode 100644 index 0a7157c8..00000000 --- a/client/crash_report_database_fuchsia.cc +++ /dev/null @@ -1,35 +0,0 @@ -// 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 "client/crash_report_database.h" - -#include "base/logging.h" - -namespace crashpad { - -// static -std::unique_ptr CrashReportDatabase::Initialize( - const base::FilePath& path) { - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return std::unique_ptr(); -} - -// static -std::unique_ptr -CrashReportDatabase::InitializeWithoutCreating(const base::FilePath& path) { - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return std::unique_ptr(); -} - -} // namespace crashpad diff --git a/client/crash_report_database_generic.cc b/client/crash_report_database_generic.cc new file mode 100644 index 00000000..5091d1b8 --- /dev/null +++ b/client/crash_report_database_generic.cc @@ -0,0 +1,841 @@ +// Copyright 2018 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 "client/crash_report_database.h" + +#include +#include + +#include + +#include "base/logging.h" +#include "build/build_config.h" +#include "client/settings.h" +#include "util/file/directory_reader.h" +#include "util/file/filesystem.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +namespace { + +// Reads from the current file position to EOF and returns as a string of bytes. +bool ReadRestOfFileAsString(FileHandle handle, std::string* contents) { + char buffer[4096]; + FileOperationResult rv; + std::string local_contents; + while ((rv = ReadFile(handle, buffer, sizeof(buffer))) > 0) { + local_contents.append(buffer, rv); + } + if (rv < 0) { + PLOG(ERROR) << "ReadFile"; + return false; + } + contents->swap(local_contents); + return true; +} + +base::FilePath ReplaceFinalExtension( + const base::FilePath& path, + const base::FilePath::StringType extension) { + return base::FilePath(path.RemoveFinalExtension().value() + extension); +} + +using OperationStatus = CrashReportDatabase::OperationStatus; + +constexpr base::FilePath::CharType kSettings[] = + FILE_PATH_LITERAL("settings.dat"); + +constexpr base::FilePath::CharType kCrashReportExtension[] = + FILE_PATH_LITERAL(".dmp"); +constexpr base::FilePath::CharType kMetadataExtension[] = + FILE_PATH_LITERAL(".meta"); +constexpr base::FilePath::CharType kLockExtension[] = + FILE_PATH_LITERAL(".lock"); + +constexpr base::FilePath::CharType kNewDirectory[] = FILE_PATH_LITERAL("new"); +constexpr base::FilePath::CharType kPendingDirectory[] = + FILE_PATH_LITERAL("pending"); +constexpr base::FilePath::CharType kCompletedDirectory[] = + FILE_PATH_LITERAL("completed"); + +constexpr const base::FilePath::CharType* kReportDirectories[] = { + kNewDirectory, + kPendingDirectory, + kCompletedDirectory, +}; + +enum { + //! \brief Corresponds to uploaded bit of the report state. + kAttributeUploaded = 1 << 0, + + //! \brief Corresponds to upload_explicity_requested bit of the report state. + kAttributeUploadExplicitlyRequested = 1 << 1, +}; + +struct ReportMetadata { + static constexpr int32_t kVersion = 1; + + int32_t version = kVersion; + int32_t upload_attempts = 0; + int64_t last_upload_attempt_time = 0; + time_t creation_time = 0; + uint8_t attributes = 0; +}; + +// A lock held while using database resources. +class ScopedLockFile { + public: + ScopedLockFile() = default; + ~ScopedLockFile() = default; + + ScopedLockFile& operator=(ScopedLockFile&& other) { + lock_file_.reset(other.lock_file_.release()); + return *this; + } + + // Attempt to acquire a lock for the report at report_path. + // Return `true` on success, otherwise `false`. + bool ResetAcquire(const base::FilePath& report_path) { + lock_file_.reset(); + + base::FilePath lock_path(report_path.RemoveFinalExtension().value() + + kLockExtension); + ScopedFileHandle lock_fd(LoggingOpenFileForWrite( + lock_path, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)); + if (!lock_fd.is_valid()) { + return false; + } + lock_file_.reset(lock_path); + + time_t timestamp = time(nullptr); + if (!LoggingWriteFile(lock_fd.get(), ×tamp, sizeof(timestamp))) { + return false; + } + + return true; + } + + // Returns `true` if the lock is held. + bool is_valid() const { return lock_file_.is_valid(); } + + // Returns `true` if the lockfile at lock_path has expired. + static bool IsExpired(const base::FilePath& lock_path, time_t lockfile_ttl) { + time_t now = time(nullptr); + + timespec filetime; + if (FileModificationTime(lock_path, &filetime) && + filetime.tv_sec > now + lockfile_ttl) { + return false; + } + + ScopedFileHandle lock_fd(LoggingOpenFileForReadAndWrite( + lock_path, FileWriteMode::kReuseOrFail, FilePermissions::kOwnerOnly)); + if (!lock_fd.is_valid()) { + return false; + } + + time_t timestamp; + if (!LoggingReadFileExactly(lock_fd.get(), ×tamp, sizeof(timestamp))) { + return false; + } + + return now >= timestamp + lockfile_ttl; + } + + private: + ScopedRemoveFile lock_file_; +}; + +} // namespace + +class CrashReportDatabaseGeneric : public CrashReportDatabase { + public: + CrashReportDatabaseGeneric(); + ~CrashReportDatabaseGeneric() override; + + bool Initialize(const base::FilePath& path, bool may_create); + + // CrashReportDatabase: + Settings* GetSettings() override; + OperationStatus PrepareNewCrashReport( + std::unique_ptr* report) override; + OperationStatus FinishedWritingCrashReport(std::unique_ptr report, + UUID* uuid) override; + OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override; + OperationStatus GetPendingReports(std::vector* reports) override; + OperationStatus GetCompletedReports(std::vector* reports) override; + OperationStatus GetReportForUploading( + const UUID& uuid, + std::unique_ptr* report) override; + OperationStatus SkipReportUpload(const UUID& uuid, + Metrics::CrashSkippedReason reason) override; + OperationStatus DeleteReport(const UUID& uuid) override; + OperationStatus RequestUpload(const UUID& uuid) override; + int CleanDatabase(time_t lockfile_ttl) override; + + private: + struct LockfileUploadReport : public UploadReport { + ScopedLockFile lock_file; + }; + + enum ReportState : int32_t { + kUninitialized = -1, + + // Being created by a caller of PrepareNewCrashReport(). + kNew, + + // Created by FinishedWritingCrashReport(), but not yet uploaded. + kPending, + + // Upload completed or skipped. + kCompleted, + + // Specifies either kPending or kCompleted. + kSearchable, + }; + + // CrashReportDatabase: + OperationStatus RecordUploadAttempt(UploadReport* report, + bool successful, + const std::string& id) override; + + // Builds a filepath for the report with the specified uuid and state. + base::FilePath ReportPath(const UUID& uuid, ReportState state); + + // Locates the report with id uuid and returns its file path in path and a + // lock for the report in lock_file. This method succeeds as long as the + // report file exists and the lock can be acquired. No validation is done on + // the existence or content of the metadata file. + OperationStatus LocateAndLockReport(const UUID& uuid, + ReportState state, + base::FilePath* path, + ScopedLockFile* lock_file); + + // Locates, locks, and reads the metadata for the report with the specified + // uuid and state. This method will fail and may remove reports if invalid + // metadata is detected. state may be kPending, kCompleted, or kSearchable. + OperationStatus CheckoutReport(const UUID& uuid, + ReportState state, + base::FilePath* path, + ScopedLockFile* lock_file, + Report* report); + + // Reads metadata for all reports in state and returns it in reports. + OperationStatus ReportsInState(ReportState state, + std::vector* reports); + + // Cleans lone metadata, reports, or expired locks in a particular state. + int CleanReportsInState(ReportState state, time_t lockfile_ttl); + + // Reads the metadata for a report from path and returns it in report. + static bool ReadMetadata(const base::FilePath& path, Report* report); + + // Wraps ReadMetadata and removes the report from the database on failure. + static bool CleaningReadMetadata(const base::FilePath& path, Report* report); + + // Writes metadata for a new report to the filesystem at path. + static bool WriteNewMetadata(const base::FilePath& path); + + // Writes the metadata for report to the filesystem at path. + static bool WriteMetadata(const base::FilePath& path, const Report& report); + + base::FilePath base_dir_; + Settings settings_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseGeneric); +}; + +CrashReportDatabaseGeneric::CrashReportDatabaseGeneric() = default; + +CrashReportDatabaseGeneric::~CrashReportDatabaseGeneric() = default; + +bool CrashReportDatabaseGeneric::Initialize(const base::FilePath& path, + bool may_create) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + base_dir_ = path; + + if (!IsDirectory(base_dir_, true) && + !(may_create && + LoggingCreateDirectory(base_dir_, FilePermissions::kOwnerOnly, true))) { + return false; + } + + for (size_t i = 0; i < arraysize(kReportDirectories); ++i) { + if (!LoggingCreateDirectory(base_dir_.Append(kReportDirectories[i]), + FilePermissions::kOwnerOnly, + true)) { + return false; + } + } + + if (!settings_.Initialize(base_dir_.Append(kSettings))) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +// static +std::unique_ptr CrashReportDatabase::Initialize( + const base::FilePath& path) { + auto database = std::make_unique(); + return database->Initialize(path, true) ? std::move(database) : nullptr; +} + +// static +std::unique_ptr +CrashReportDatabase::InitializeWithoutCreating(const base::FilePath& path) { + auto database = std::make_unique(); + return database->Initialize(path, false) ? std::move(database) : nullptr; +} + +Settings* CrashReportDatabaseGeneric::GetSettings() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &settings_; +} + +OperationStatus CrashReportDatabaseGeneric::PrepareNewCrashReport( + std::unique_ptr* report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + auto new_report = std::make_unique(); + if (!new_report->Initialize(base_dir_.Append(kNewDirectory), + kCrashReportExtension)) { + return kFileSystemError; + } + + report->reset(new_report.release()); + return kNoError; +} + +OperationStatus CrashReportDatabaseGeneric::FinishedWritingCrashReport( + std::unique_ptr report, + UUID* uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + base::FilePath path = ReportPath(report->ReportID(), kPending); + ScopedLockFile lock_file; + if (!lock_file.ResetAcquire(path)) { + return kBusyError; + } + + if (!WriteNewMetadata(ReplaceFinalExtension(path, kMetadataExtension))) { + return kDatabaseError; + } + + FileOffset size = report->Writer()->Seek(0, SEEK_END); + + report->Writer()->Close(); + if (!MoveFileOrDirectory(report->file_remover_.get(), path)) { + return kFileSystemError; + } + // We've moved the report to pending, so it no longer needs to be removed. + ignore_result(report->file_remover_.release()); + + *uuid = report->ReportID(); + + Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated); + Metrics::CrashReportSize(size); + + return kNoError; +} + +OperationStatus CrashReportDatabaseGeneric::LookUpCrashReport(const UUID& uuid, + Report* report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + ScopedLockFile lock_file; + base::FilePath path; + return CheckoutReport(uuid, kSearchable, &path, &lock_file, report); +} + +OperationStatus CrashReportDatabaseGeneric::GetPendingReports( + std::vector* reports) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return ReportsInState(kPending, reports); +} + +OperationStatus CrashReportDatabaseGeneric::GetCompletedReports( + std::vector* reports) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return ReportsInState(kCompleted, reports); +} + +OperationStatus CrashReportDatabaseGeneric::GetReportForUploading( + const UUID& uuid, + std::unique_ptr* report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + auto upload_report = std::make_unique(); + + base::FilePath path; + OperationStatus os = CheckoutReport( + uuid, kPending, &path, &upload_report->lock_file, upload_report.get()); + if (os != kNoError) { + return os; + } + + if (!upload_report->Initialize(path, this)) { + return kFileSystemError; + } + + report->reset(upload_report.release()); + return kNoError; +} + +OperationStatus CrashReportDatabaseGeneric::SkipReportUpload( + const UUID& uuid, + Metrics::CrashSkippedReason reason) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + Metrics::CrashUploadSkipped(reason); + + base::FilePath path; + ScopedLockFile lock_file; + Report report; + OperationStatus os = + CheckoutReport(uuid, kPending, &path, &lock_file, &report); + if (os != kNoError) { + return os; + } + + base::FilePath completed_path(ReportPath(uuid, kCompleted)); + ScopedLockFile completed_lock_file; + if (!completed_lock_file.ResetAcquire(completed_path)) { + return kBusyError; + } + + report.upload_explicitly_requested = false; + if (!WriteMetadata(completed_path, report)) { + return kDatabaseError; + } + + if (!MoveFileOrDirectory(path, completed_path)) { + return kFileSystemError; + } + + if (!LoggingRemoveFile(ReplaceFinalExtension(path, kMetadataExtension))) { + return kDatabaseError; + } + + return kNoError; +} + +OperationStatus CrashReportDatabaseGeneric::DeleteReport(const UUID& uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + base::FilePath path; + ScopedLockFile lock_file; + OperationStatus os = + LocateAndLockReport(uuid, kSearchable, &path, &lock_file); + if (os != kNoError) { + return os; + } + + if (!LoggingRemoveFile(path)) { + return kFileSystemError; + } + + if (!LoggingRemoveFile(ReplaceFinalExtension(path, kMetadataExtension))) { + return kDatabaseError; + } + + return kNoError; +} + +OperationStatus CrashReportDatabaseGeneric::RequestUpload(const UUID& uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + base::FilePath path; + ScopedLockFile lock_file; + Report report; + OperationStatus os = + CheckoutReport(uuid, kSearchable, &path, &lock_file, &report); + if (os != kNoError) { + return os; + } + + if (report.uploaded) { + return kCannotRequestUpload; + } + + report.upload_explicitly_requested = true; + base::FilePath pending_path = ReportPath(uuid, kPending); + if (!MoveFileOrDirectory(path, pending_path)) { + return kFileSystemError; + } + + if (!WriteMetadata(pending_path, report)) { + return kDatabaseError; + } + + if (pending_path != path) { + if (!LoggingRemoveFile(ReplaceFinalExtension(path, kMetadataExtension))) { + return kDatabaseError; + } + } + + Metrics::CrashReportPending(Metrics::PendingReportReason::kUserInitiated); + return kNoError; +} + +int CrashReportDatabaseGeneric::CleanDatabase(time_t lockfile_ttl) { + int removed = 0; + time_t now = time(nullptr); + + DirectoryReader reader; + const base::FilePath new_dir(base_dir_.Append(kNewDirectory)); + if (reader.Open(new_dir)) { + base::FilePath filename; + DirectoryReader::Result result; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath filepath(new_dir.Append(filename)); + timespec filetime; + if (!FileModificationTime(filepath, &filetime)) { + continue; + } + if (filetime.tv_sec <= now - lockfile_ttl) { + if (LoggingRemoveFile(filepath)) { + ++removed; + } + } + } + } + + removed += CleanReportsInState(kPending, lockfile_ttl); + removed += CleanReportsInState(kCompleted, lockfile_ttl); + return removed; +} + +OperationStatus CrashReportDatabaseGeneric::RecordUploadAttempt( + UploadReport* report, + bool successful, + const std::string& id) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + Metrics::CrashUploadAttempted(successful); + time_t now = time(nullptr); + + report->id = id; + report->uploaded = successful; + report->last_upload_attempt_time = now; + ++report->upload_attempts; + + base::FilePath report_path(report->file_path); + + ScopedLockFile lock_file; + if (successful) { + report->upload_explicitly_requested = false; + + base::FilePath completed_report_path = ReportPath(report->uuid, kCompleted); + + if (!lock_file.ResetAcquire(completed_report_path)) { + return kBusyError; + } + + report->Reader()->Close(); + if (!MoveFileOrDirectory(report_path, completed_report_path)) { + return kFileSystemError; + } + + LoggingRemoveFile(ReplaceFinalExtension(report_path, kMetadataExtension)); + report_path = completed_report_path; + } + + if (!WriteMetadata(report_path, *report)) { + return kDatabaseError; + } + + if (!settings_.SetLastUploadAttemptTime(now)) { + return kDatabaseError; + } + + return kNoError; +} + +base::FilePath CrashReportDatabaseGeneric::ReportPath(const UUID& uuid, + ReportState state) { + DCHECK_NE(state, kUninitialized); + DCHECK_NE(state, kSearchable); + +#if defined(OS_WIN) + const std::wstring uuid_string = uuid.ToString16(); +#else + const std::string uuid_string = uuid.ToString(); +#endif + + return base_dir_.Append(kReportDirectories[state]) + .Append(uuid_string + kCrashReportExtension); +} + +OperationStatus CrashReportDatabaseGeneric::LocateAndLockReport( + const UUID& uuid, + ReportState desired_state, + base::FilePath* path, + ScopedLockFile* lock_file) { + std::vector searchable_states; + if (desired_state == kSearchable) { + searchable_states.push_back(kPending); + searchable_states.push_back(kCompleted); + } else { + DCHECK(desired_state == kPending || desired_state == kCompleted); + searchable_states.push_back(desired_state); + } + + for (const ReportState state : searchable_states) { + base::FilePath local_path(ReportPath(uuid, state)); + ScopedLockFile local_lock; + if (!local_lock.ResetAcquire(local_path)) { + return kBusyError; + } + + if (!IsRegularFile(local_path)) { + continue; + } + + *path = local_path; + *lock_file = std::move(local_lock); + return kNoError; + } + + return kReportNotFound; +} + +OperationStatus CrashReportDatabaseGeneric::CheckoutReport( + const UUID& uuid, + ReportState state, + base::FilePath* path, + ScopedLockFile* lock_file, + Report* report) { + ScopedLockFile local_lock; + base::FilePath local_path; + OperationStatus os = + LocateAndLockReport(uuid, state, &local_path, &local_lock); + if (os != kNoError) { + return os; + } + + if (!CleaningReadMetadata(local_path, report)) { + return kDatabaseError; + } + + *path = local_path; + *lock_file = std::move(local_lock); + return kNoError; +} + +OperationStatus CrashReportDatabaseGeneric::ReportsInState( + ReportState state, + std::vector* reports) { + DCHECK(reports->empty()); + DCHECK_NE(state, kUninitialized); + DCHECK_NE(state, kSearchable); + DCHECK_NE(state, kNew); + + const base::FilePath dir_path(base_dir_.Append(kReportDirectories[state])); + DirectoryReader reader; + if (!reader.Open(dir_path)) { + return kDatabaseError; + } + + base::FilePath filename; + DirectoryReader::Result result; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath::StringType extension(filename.FinalExtension()); + if (extension.compare(kCrashReportExtension) != 0) { + continue; + } + + const base::FilePath filepath(dir_path.Append(filename)); + ScopedLockFile lock_file; + if (!lock_file.ResetAcquire(filepath)) { + continue; + } + + Report report; + if (!CleaningReadMetadata(filepath, &report)) { + continue; + } + reports->push_back(report); + reports->back().file_path = filepath; + } + return kNoError; +} + +int CrashReportDatabaseGeneric::CleanReportsInState(ReportState state, + time_t lockfile_ttl) { + const base::FilePath dir_path(base_dir_.Append(kReportDirectories[state])); + DirectoryReader reader; + if (!reader.Open(dir_path)) { + return 0; + } + + int removed = 0; + base::FilePath filename; + DirectoryReader::Result result; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath::StringType extension(filename.FinalExtension()); + const base::FilePath filepath(dir_path.Append(filename)); + + // Remove any report files without metadata. + if (extension.compare(kCrashReportExtension) == 0) { + const base::FilePath metadata_path( + ReplaceFinalExtension(filepath, kMetadataExtension)); + ScopedLockFile report_lock; + if (report_lock.ResetAcquire(filepath) && !IsRegularFile(metadata_path) && + LoggingRemoveFile(filepath)) { + ++removed; + } + continue; + } + + // Remove any metadata files without report files. + if (extension.compare(kMetadataExtension) == 0) { + const base::FilePath report_path( + ReplaceFinalExtension(filepath, kCrashReportExtension)); + ScopedLockFile report_lock; + if (report_lock.ResetAcquire(report_path) && + !IsRegularFile(report_path) && LoggingRemoveFile(filepath)) { + ++removed; + } + continue; + } + + // Remove any expired locks only if we can remove the report and metadata. + if (extension.compare(kLockExtension) == 0 && + ScopedLockFile::IsExpired(filepath, lockfile_ttl)) { + const base::FilePath no_ext(filepath.RemoveFinalExtension()); + const base::FilePath report_path(no_ext.value() + kCrashReportExtension); + const base::FilePath metadata_path(no_ext.value() + kMetadataExtension); + if ((IsRegularFile(report_path) && !LoggingRemoveFile(report_path)) || + (IsRegularFile(metadata_path) && !LoggingRemoveFile(metadata_path))) { + continue; + } + + if (LoggingRemoveFile(filepath)) { + ++removed; + } + continue; + } + } + + return removed; +} + +// static +bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path, + Report* report) { + const base::FilePath metadata_path( + ReplaceFinalExtension(path, kMetadataExtension)); + + ScopedFileHandle handle(LoggingOpenFileForRead(metadata_path)); + if (!handle.is_valid()) { + return false; + } + + if (!report->uuid.InitializeFromString( + path.BaseName().RemoveFinalExtension().value())) { + LOG(ERROR) << "Couldn't interpret report uuid"; + return false; + } + + ReportMetadata metadata; + if (!LoggingReadFileExactly(handle.get(), &metadata, sizeof(metadata))) { + return false; + } + + if (metadata.version != ReportMetadata::kVersion) { + LOG(ERROR) << "metadata version mismatch"; + return false; + } + + if (!ReadRestOfFileAsString(handle.get(), &report->id)) { + return false; + } + + report->upload_attempts = metadata.upload_attempts; + report->last_upload_attempt_time = metadata.last_upload_attempt_time; + report->creation_time = metadata.creation_time; + report->uploaded = (metadata.attributes & kAttributeUploaded) != 0; + report->upload_explicitly_requested = + (metadata.attributes & kAttributeUploadExplicitlyRequested) != 0; + report->file_path = path; + return true; +} + +// static +bool CrashReportDatabaseGeneric::CleaningReadMetadata( + const base::FilePath& path, + Report* report) { + if (ReadMetadata(path, report)) { + return true; + } + + LoggingRemoveFile(path); + LoggingRemoveFile(ReplaceFinalExtension(path, kMetadataExtension)); + return false; +} + +// static +bool CrashReportDatabaseGeneric::WriteNewMetadata(const base::FilePath& path) { + const base::FilePath metadata_path( + ReplaceFinalExtension(path, kMetadataExtension)); + + ScopedFileHandle handle(LoggingOpenFileForWrite(metadata_path, + FileWriteMode::kCreateOrFail, + FilePermissions::kOwnerOnly)); + if (!handle.is_valid()) { + return false; + } + + ReportMetadata metadata; + metadata.creation_time = time(nullptr); + + return LoggingWriteFile(handle.get(), &metadata, sizeof(metadata)); +} + +// static +bool CrashReportDatabaseGeneric::WriteMetadata(const base::FilePath& path, + const Report& report) { + const base::FilePath metadata_path( + ReplaceFinalExtension(path, kMetadataExtension)); + + ScopedFileHandle handle( + LoggingOpenFileForWrite(metadata_path, + FileWriteMode::kTruncateOrCreate, + FilePermissions::kOwnerOnly)); + if (!handle.is_valid()) { + return false; + } + + ReportMetadata metadata; + metadata.creation_time = report.creation_time; + metadata.last_upload_attempt_time = report.last_upload_attempt_time; + metadata.upload_attempts = report.upload_attempts; + metadata.attributes = + (report.uploaded ? kAttributeUploaded : 0) | + (report.upload_explicitly_requested ? kAttributeUploadExplicitlyRequested + : 0); + + return LoggingWriteFile(handle.get(), &metadata, sizeof(metadata)) && + LoggingWriteFile(handle.get(), report.id.c_str(), report.id.size()); +} + +} // namespace crashpad diff --git a/client/crash_report_database_test.cc b/client/crash_report_database_test.cc index 31087b93..7760b4a1 100644 --- a/client/crash_report_database_test.cc +++ b/client/crash_report_database_test.cc @@ -14,10 +14,12 @@ #include "client/crash_report_database.h" +#include "build/build_config.h" #include "client/settings.h" #include "gtest/gtest.h" #include "test/errors.h" #include "test/file.h" +#include "test/filesystem.h" #include "test/scoped_temp_dir.h" #include "util/file/file_io.h" #include "util/file/filesystem.h" @@ -667,6 +669,76 @@ TEST_F(CrashReportDatabaseTest, RequestUpload) { CrashReportDatabase::kCannotRequestUpload); } +// This test uses knowledge of the database format to break it, so it only +// applies to the unfified database implementation. +#if !defined(OS_MACOSX) && !defined(OS_WIN) +TEST_F(CrashReportDatabaseTest, CleanBrokenDatabase) { + // Remove report files if metadata goes missing. + CrashReportDatabase::Report report; + ASSERT_NO_FATAL_FAILURE(CreateCrashReport(&report)); + + const base::FilePath metadata( + report.file_path.RemoveFinalExtension().value() + + FILE_PATH_LITERAL(".meta")); + ASSERT_TRUE(PathExists(report.file_path)); + ASSERT_TRUE(PathExists(metadata)); + + ASSERT_TRUE(LoggingRemoveFile(metadata)); + EXPECT_EQ(db()->CleanDatabase(0), 1); + + EXPECT_FALSE(PathExists(report.file_path)); + EXPECT_FALSE(PathExists(metadata)); + + // Remove metadata files if reports go missing. + ASSERT_NO_FATAL_FAILURE(CreateCrashReport(&report)); + const base::FilePath metadata2( + report.file_path.RemoveFinalExtension().value() + + FILE_PATH_LITERAL(".meta")); + ASSERT_TRUE(PathExists(report.file_path)); + ASSERT_TRUE(PathExists(metadata2)); + + ASSERT_TRUE(LoggingRemoveFile(report.file_path)); + EXPECT_EQ(db()->CleanDatabase(0), 1); + + EXPECT_FALSE(PathExists(report.file_path)); + EXPECT_FALSE(PathExists(metadata2)); + + // Remove stale new files. + std::unique_ptr new_report; + EXPECT_EQ(db()->PrepareNewCrashReport(&new_report), + CrashReportDatabase::kNoError); + new_report->Writer()->Close(); + EXPECT_EQ(db()->CleanDatabase(0), 1); + + // Remove stale lock files and their associated reports. + ASSERT_NO_FATAL_FAILURE(CreateCrashReport(&report)); + const base::FilePath metadata3( + report.file_path.RemoveFinalExtension().value() + + FILE_PATH_LITERAL(".meta")); + ASSERT_TRUE(PathExists(report.file_path)); + ASSERT_TRUE(PathExists(metadata3)); + + const base::FilePath lockpath( + report.file_path.RemoveFinalExtension().value() + + FILE_PATH_LITERAL(".lock")); + ScopedFileHandle handle(LoggingOpenFileForWrite( + lockpath, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)); + ASSERT_TRUE(handle.is_valid()); + + time_t expired_timestamp = time(nullptr) - 60 * 60 * 24 * 3; + + ASSERT_TRUE(LoggingWriteFile( + handle.get(), &expired_timestamp, sizeof(expired_timestamp))); + ASSERT_TRUE(LoggingCloseFile(handle.get())); + ignore_result(handle.release()); + + EXPECT_EQ(db()->CleanDatabase(0), 1); + + EXPECT_FALSE(PathExists(report.file_path)); + EXPECT_FALSE(PathExists(metadata3)); +} +#endif // !OS_MACOSX && !OS_WIN + } // namespace } // namespace test } // namespace crashpad diff --git a/handler/prune_crash_reports_thread.cc b/handler/prune_crash_reports_thread.cc index 722275f5..7876c2fe 100644 --- a/handler/prune_crash_reports_thread.cc +++ b/handler/prune_crash_reports_thread.cc @@ -38,6 +38,7 @@ void PruneCrashReportThread::Stop() { } void PruneCrashReportThread::DoWork(const WorkerThread* thread) { + database_->CleanDatabase(60 * 60 * 24 * 3); PruneCrashReportDatabase(database_, condition_.get()); } From b43858c9903f7887c710644515fcc5024e2ff612 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 15 Feb 2018 13:21:00 -0800 Subject: [PATCH 160/326] fuchsia: Start of ModuleSnapshot and ProcessReader implementations Adds beginning ProcessReader implementation for Fuchsia which currently only reads modules from the target process. ModuleSnapshotFuchsia implemented enough to pull out CrashpadInfo, which in turn is passed through ProcessSnapshotFuchsia, which is enough to get CrashpadInfoClientOptions.OneModule to pass. Bug: crashpad:196 Change-Id: I92b82696c464a5ba2e0db2c75aa46fd74b0fa364 Reviewed-on: https://chromium-review.googlesource.com/910324 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- snapshot/BUILD.gn | 4 + snapshot/fuchsia/module_snapshot_fuchsia.cc | 182 +++++++++++++++++++ snapshot/fuchsia/module_snapshot_fuchsia.h | 100 ++++++++++ snapshot/fuchsia/process_reader.cc | 159 ++++++++++++++++ snapshot/fuchsia/process_reader.h | 87 +++++++++ snapshot/fuchsia/process_snapshot_fuchsia.cc | 74 +++++++- snapshot/fuchsia/process_snapshot_fuchsia.h | 13 ++ 7 files changed, 616 insertions(+), 3 deletions(-) create mode 100644 snapshot/fuchsia/module_snapshot_fuchsia.cc create mode 100644 snapshot/fuchsia/module_snapshot_fuchsia.h create mode 100644 snapshot/fuchsia/process_reader.cc create mode 100644 snapshot/fuchsia/process_reader.h diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 785f7970..f4a592b1 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -178,6 +178,10 @@ static_library("snapshot") { if (crashpad_is_fuchsia) { sources += [ + "fuchsia/module_snapshot_fuchsia.cc", + "fuchsia/module_snapshot_fuchsia.h", + "fuchsia/process_reader.cc", + "fuchsia/process_reader.h", "fuchsia/process_snapshot_fuchsia.cc", "fuchsia/process_snapshot_fuchsia.h", ] diff --git a/snapshot/fuchsia/module_snapshot_fuchsia.cc b/snapshot/fuchsia/module_snapshot_fuchsia.cc new file mode 100644 index 00000000..6d57bf7f --- /dev/null +++ b/snapshot/fuchsia/module_snapshot_fuchsia.cc @@ -0,0 +1,182 @@ +// Copyright 2018 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/fuchsia/module_snapshot_fuchsia.h" + +#include "base/logging.h" +#include "client/crashpad_info.h" +#include "snapshot/crashpad_types/image_annotation_reader.h" +#include "util/misc/elf_note_types.h" + +namespace crashpad { +namespace internal { + +ModuleSnapshotFuchsia::ModuleSnapshotFuchsia() = default; + +ModuleSnapshotFuchsia::~ModuleSnapshotFuchsia() = default; + +bool ModuleSnapshotFuchsia::Initialize( + ProcessReader* process_reader, + const ProcessReader::Module& process_reader_module) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_reader_ = process_reader; + name_ = process_reader_module.name; + elf_image_reader_ = process_reader_module.reader; + if (!elf_image_reader_) { + return false; + } + + // The data payload is only sizeof(VMAddress) in the note, but add a bit to + // account for the name, header, and padding. + constexpr ssize_t kMaxNoteSize = 256; + std::unique_ptr notes = + elf_image_reader_->NotesWithNameAndType( + CRASHPAD_ELF_NOTE_NAME, + CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO, + kMaxNoteSize); + std::string desc; + VMAddress info_address; + if (notes->NextNote(nullptr, nullptr, &desc) == + ElfImageReader::NoteReader::Result::kSuccess) { + info_address = *reinterpret_cast(&desc[0]); + + ProcessMemoryRange range; + if (range.Initialize(*elf_image_reader_->Memory())) { + auto info = std::make_unique(); + if (info->Initialize(&range, info_address)) { + crashpad_info_ = std::move(info); + } + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ModuleSnapshotFuchsia::GetCrashpadOptions( + CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!crashpad_info_) { + return false; + } + + options->crashpad_handler_behavior = + crashpad_info_->CrashpadHandlerBehavior(); + options->system_crash_reporter_forwarding = + crashpad_info_->SystemCrashReporterForwarding(); + options->gather_indirectly_referenced_memory = + crashpad_info_->GatherIndirectlyReferencedMemory(); + options->indirectly_referenced_memory_cap = + crashpad_info_->IndirectlyReferencedMemoryCap(); + return true; +} + +std::string ModuleSnapshotFuchsia::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return name_; +} + +uint64_t ModuleSnapshotFuchsia::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return elf_image_reader_->Address(); +} + +uint64_t ModuleSnapshotFuchsia::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return elf_image_reader_->Address(); +} + +time_t ModuleSnapshotFuchsia::Timestamp() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return 0; +} + +void ModuleSnapshotFuchsia::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; +} + +void ModuleSnapshotFuchsia::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; +} + +ModuleSnapshot::ModuleType ModuleSnapshotFuchsia::GetModuleType() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return kModuleTypeUnknown; +} + +void ModuleSnapshotFuchsia::UUIDAndAge(crashpad::UUID* uuid, + uint32_t* age) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 +} + +std::string ModuleSnapshotFuchsia::DebugFileName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::string(); +} + +std::vector ModuleSnapshotFuchsia::AnnotationsVector() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::vector(); +} + +std::map ModuleSnapshotFuchsia::AnnotationsSimpleMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::map(); +} + +std::vector ModuleSnapshotFuchsia::AnnotationObjects() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::vector(); +} + +std::set> ModuleSnapshotFuchsia::ExtraMemoryRanges() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::set>(); +} + +std::vector +ModuleSnapshotFuchsia::CustomMinidumpStreams() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/fuchsia/module_snapshot_fuchsia.h b/snapshot/fuchsia/module_snapshot_fuchsia.h new file mode 100644 index 00000000..b7831441 --- /dev/null +++ b/snapshot/fuchsia/module_snapshot_fuchsia.h @@ -0,0 +1,100 @@ +// Copyright 2018 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_FUCHSIA_MODULE_SNAPSHOT_FUCHSIA_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_MODULE_SNAPSHOT_FUCHSIA_H_ + +#include +#include + +#include +#include +#include +#include + +#include "base/macros.h" +#include "client/crashpad_info.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/crashpad_types/crashpad_info_reader.h" +#include "snapshot/elf/elf_image_reader.h" +#include "snapshot/fuchsia/process_reader.h" +#include "snapshot/module_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A ModuleSnapshot of a code module (binary image) loaded into a +//! running (or crashed) process on a Fuchsia system. +class ModuleSnapshotFuchsia final : public ModuleSnapshot { + public: + ModuleSnapshotFuchsia(); + ~ModuleSnapshotFuchsia() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReader for the process containing the + //! module. + //! \param[in] process_reader_module The module within the ProcessReader for + //! which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(ProcessReader* process_reader, + const ProcessReader::Module& process_reader_module); + + //! \brief Returns options from the module’s CrashpadInfo structure. + //! + //! \param[out] options Options set in the module’s CrashpadInfo structure. + //! + //! \return `true` if there were options returned. Otherwise `false`. + bool GetCrashpadOptions(CrashpadInfoClientOptions* options); + + // 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 UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector AnnotationsVector() const override; + std::map AnnotationsSimpleMap() const override; + std::vector AnnotationObjects() const override; + std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; + + private: + std::string name_; + ElfImageReader* elf_image_reader_; // weak + ProcessReader* process_reader_; // weak + std::unique_ptr crashpad_info_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotFuchsia); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_MODULE_SNAPSHOT_FUCHSIA_H_ diff --git a/snapshot/fuchsia/process_reader.cc b/snapshot/fuchsia/process_reader.cc new file mode 100644 index 00000000..df19667d --- /dev/null +++ b/snapshot/fuchsia/process_reader.cc @@ -0,0 +1,159 @@ +// Copyright 2018 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/fuchsia/process_reader.h" + +#include +#include + +namespace crashpad { + +ProcessReader::Module::Module() = default; + +ProcessReader::Module::~Module() = default; + +ProcessReader::ProcessReader() = default; + +ProcessReader::~ProcessReader() = default; + +bool ProcessReader::Initialize(zx_handle_t process) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_ = process; + + process_memory_.reset(new ProcessMemoryFuchsia()); + process_memory_->Initialize(process_); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const std::vector& ProcessReader::Modules() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!initialized_modules_) { + InitializeModules(); + } + + return modules_; +} + +void ProcessReader::InitializeModules() { + DCHECK(!initialized_modules_); + DCHECK(modules_.empty()); + + initialized_modules_ = true; + + // TODO(scottmg): does some of this, but doesn't + // expose any of the data that's necessary to fill out a Module after it + // retrieves (some of) the data into internal structures. It may be worth + // trying to refactor/upstream some of this into Fuchsia. + + std::string app_name("app:"); + { + char name[ZX_MAX_NAME_LEN]; + zx_status_t status = + zx_object_get_property(process_, ZX_PROP_NAME, name, sizeof(name)); + if (status != ZX_OK) { + LOG(ERROR) << "zx_object_get_property ZX_PROP_NAME"; + return; + } + + app_name += name; + } + + // Starting from the ld.so's _dl_debug_addr, read the link_map structure and + // walk the list to fill out modules_. + + uintptr_t debug_address; + zx_status_t status = zx_object_get_property(process_, + ZX_PROP_PROCESS_DEBUG_ADDR, + &debug_address, + sizeof(debug_address)); + if (status != ZX_OK || debug_address == 0) { + LOG(ERROR) << "zx_object_get_property ZX_PROP_PROCESS_DEBUG_ADDR"; + return; + } + + constexpr auto k_r_debug_map_offset = offsetof(r_debug, r_map); + uintptr_t map; + if (!process_memory_->Read( + debug_address + k_r_debug_map_offset, sizeof(map), &map)) { + LOG(ERROR) << "read link_map"; + return; + } + + int i = 0; + constexpr int kMaxDso = 1000; // Stop after an unreasonably large number. + while (map != 0) { + if (++i >= kMaxDso) { + LOG(ERROR) << "possibly circular dso list, terminating"; + return; + } + + constexpr auto k_link_map_addr_offset = offsetof(link_map, l_addr); + zx_vaddr_t base; + if (!process_memory_->Read( + map + k_link_map_addr_offset, sizeof(base), &base)) { + LOG(ERROR) << "Read base"; + // Could theoretically continue here, but realistically if any part of + // link_map fails to read, things are looking bad, so just abort. + break; + } + + constexpr auto k_link_map_next_offset = offsetof(link_map, l_next); + zx_vaddr_t next; + if (!process_memory_->Read( + map + k_link_map_next_offset, sizeof(next), &next)) { + LOG(ERROR) << "Read next"; + break; + } + + constexpr auto k_link_map_name_offset = offsetof(link_map, l_name); + zx_vaddr_t name_address; + if (!process_memory_->Read(map + k_link_map_name_offset, + sizeof(name_address), + &name_address)) { + LOG(ERROR) << "Read name address"; + break; + } + + std::string dsoname; + if (!process_memory_->ReadCString(name_address, &dsoname)) { + // In this case, it could be reasonable to continue on to the next module + // as this data isn't strictly in the link_map. + LOG(ERROR) << "ReadCString name"; + } + + Module module; + module.name = dsoname.empty() ? app_name : dsoname; + + std::unique_ptr reader(new ElfImageReader()); + + std::unique_ptr process_memory_range( + new ProcessMemoryRange()); + // TODO(scottmg): Could this be limited range? + process_memory_range->Initialize(process_memory_.get(), true); + process_memory_ranges_.push_back(std::move(process_memory_range)); + + reader->Initialize(*process_memory_ranges_.back(), base); + module.reader = reader.get(); + module_readers_.push_back(std::move(reader)); + modules_.push_back(module); + + map = next; + } +} + +} // namespace crashpad diff --git a/snapshot/fuchsia/process_reader.h b/snapshot/fuchsia/process_reader.h new file mode 100644 index 00000000..332afc37 --- /dev/null +++ b/snapshot/fuchsia/process_reader.h @@ -0,0 +1,87 @@ +// Copyright 2018 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_FUCHSIA_PROCESS_READER_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_READER_H_ + +#include +#include + +#include "base/macros.h" +#include "build/build_config.h" +#include "snapshot/elf/elf_image_reader.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/process/process_memory_fuchsia.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +//! \brief Accesses information about another process, identified by a Fuchsia +//! process. +class ProcessReader { + public: + //! \brief Contains information about a module loaded into a process. + struct Module { + Module(); + ~Module(); + + //! \brief The `ZX_PROP_NAME` of the module. Will be prepended with "app:" + //! for the main executable. + std::string name; + + //! \brief An image reader for the module. + //! + //! The lifetime of this ElfImageReader is scoped to the lifetime of the + //! ProcessReader that created it. + //! + //! This field may be `nullptr` if a reader could not be created for the + //! module. + ElfImageReader* reader; + }; + + ProcessReader(); + ~ProcessReader(); + + //! \brief Initializes this object. This method must be called before any + //! other. + //! + //! \param[in] process A process handle with permissions to read properties + //! and memory from the target process. + //! + //! \return `true` on success, indicating that this object will respond + //! validly to further method calls. `false` on failure. On failure, no + //! further method calls should be made. + bool Initialize(zx_handle_t process); + + //! \return The modules loaded in the process. The first element (at index + //! `0`) corresponds to the main executable. + const std::vector& Modules(); + + private: + void InitializeModules(); + + std::vector modules_; + std::vector> module_readers_; + std::vector> process_memory_ranges_; + std::unique_ptr process_memory_; + zx_handle_t process_; + bool initialized_modules_ = false; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcessReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_READER_H_ diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc index acd5449e..1505f1b2 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -23,92 +23,160 @@ ProcessSnapshotFuchsia::ProcessSnapshotFuchsia() {} ProcessSnapshotFuchsia::~ProcessSnapshotFuchsia() {} bool ProcessSnapshotFuchsia::Initialize(zx_handle_t process) { - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return false; + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (!process_reader_.Initialize(process)) { + return false; + } + + InitializeModules(); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; } void ProcessSnapshotFuchsia::GetCrashpadOptions( CrashpadInfoClientOptions* options) { - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + CrashpadInfoClientOptions local_options; + + for (const auto& module : modules_) { + CrashpadInfoClientOptions module_options; + module->GetCrashpadOptions(&module_options); + + if (local_options.crashpad_handler_behavior == TriState::kUnset) { + local_options.crashpad_handler_behavior = + module_options.crashpad_handler_behavior; + } + if (local_options.system_crash_reporter_forwarding == TriState::kUnset) { + local_options.system_crash_reporter_forwarding = + module_options.system_crash_reporter_forwarding; + } + if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) { + local_options.gather_indirectly_referenced_memory = + module_options.gather_indirectly_referenced_memory; + local_options.indirectly_referenced_memory_cap = + module_options.indirectly_referenced_memory_cap; + } + + // If non-default values have been found for all options, the loop can end + // early. + if (local_options.crashpad_handler_behavior != TriState::kUnset && + local_options.system_crash_reporter_forwarding != TriState::kUnset && + local_options.gather_indirectly_referenced_memory != TriState::kUnset) { + break; + } + } + + *options = local_options; } pid_t ProcessSnapshotFuchsia::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return 0; } pid_t ProcessSnapshotFuchsia::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return 0; } void ProcessSnapshotFuchsia::SnapshotTime(timeval* snapshot_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 } void ProcessSnapshotFuchsia::ProcessStartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 } void ProcessSnapshotFuchsia::ProcessCPUTimes(timeval* user_time, timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 } void ProcessSnapshotFuchsia::ReportID(UUID* report_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 } void ProcessSnapshotFuchsia::ClientID(UUID* client_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 } const std::map& ProcessSnapshotFuchsia::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return annotations_simple_map_; } const SystemSnapshot* ProcessSnapshotFuchsia::System() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return nullptr; } std::vector ProcessSnapshotFuchsia::Threads() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return std::vector(); } std::vector ProcessSnapshotFuchsia::Modules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return std::vector(); } std::vector ProcessSnapshotFuchsia::UnloadedModules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return std::vector(); } const ExceptionSnapshot* ProcessSnapshotFuchsia::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return nullptr; } std::vector ProcessSnapshotFuchsia::MemoryMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return std::vector(); } std::vector ProcessSnapshotFuchsia::Handles() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return std::vector(); } std::vector ProcessSnapshotFuchsia::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return std::vector(); } +void ProcessSnapshotFuchsia::InitializeModules() { + const std::vector& process_reader_modules = + process_reader_.Modules(); + for (const ProcessReader::Module& process_reader_module : + process_reader_modules) { + auto module = std::make_unique(); + if (module->Initialize(&process_reader_, process_reader_module)) { + modules_.push_back(std::move(module)); + } + } +} + } // namespace crashpad diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.h b/snapshot/fuchsia/process_snapshot_fuchsia.h index 5ae6fd31..fa0167d3 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.h +++ b/snapshot/fuchsia/process_snapshot_fuchsia.h @@ -17,10 +17,17 @@ #include +#include +#include + #include "base/macros.h" #include "snapshot/crashpad_info_client_options.h" +#include "snapshot/elf/elf_image_reader.h" +#include "snapshot/fuchsia/module_snapshot_fuchsia.h" +#include "snapshot/fuchsia/process_reader.h" #include "snapshot/process_snapshot.h" #include "snapshot/unloaded_module_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" namespace crashpad { @@ -66,7 +73,13 @@ class ProcessSnapshotFuchsia : public ProcessSnapshot { std::vector ExtraMemory() const override; private: + // Initializes modules_ on behalf of Initialize(). + void InitializeModules(); + + std::vector> modules_; + ProcessReader process_reader_; std::map annotations_simple_map_; + InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotFuchsia); }; From 6667fa25595a62c7a2fed51cf4a56df702e324ae Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 15 Feb 2018 13:31:10 -0800 Subject: [PATCH 161/326] fuchsia: Fixes to TestPaths - Don't specify a directory in .so load, this allows CrashpadInfoClientOptions.TwoModules to pass, as it is able to find its .so. - Set expected exe name to "app" on Fuchsia, as that's what all binaries are called when packaged. This fixes Paths.Executable. Bug: crashpad:196 Change-Id: I6b0a663734e93b76412a5bdba045e73dcfe7e4cf Reviewed-on: https://chromium-review.googlesource.com/922871 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- test/test_paths.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/test_paths.cc b/test/test_paths.cc index f6471d08..0d7db061 100644 --- a/test/test_paths.cc +++ b/test/test_paths.cc @@ -127,6 +127,10 @@ base::FilePath TestPaths::Executable() { // static base::FilePath TestPaths::ExpectedExecutableBasename( const base::FilePath::StringType& name) { +#if defined(OS_FUCHSIA) + // Apps in Fuchsia packages are always named "app". + return base::FilePath("app"); +#else // OS_FUCHSIA #if defined(CRASHPAD_IS_IN_CHROMIUM) base::FilePath::StringType executable_name( FILE_PATH_LITERAL("crashpad_tests")); @@ -139,6 +143,7 @@ base::FilePath TestPaths::ExpectedExecutableBasename( #endif // OS_WIN return base::FilePath(executable_name); +#endif // OS_FUCHSIA } // static @@ -195,7 +200,11 @@ base::FilePath TestPaths::BuildArtifact( #endif // OS_WIN #if defined(OS_FUCHSIA) - directory = base::FilePath(FILE_PATH_LITERAL("/pkg/lib")); + // TODO(scottmg): .so files are currently deployed into /boot/lib, where + // they'll be found (without a path) by the loader. Application packaging + // infrastructure is in progress, so this will likely change again in the + // future. + directory = base::FilePath(); #endif break; } From 90cde8e30f7d56e46ce7ba8c57524632591a1632 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 15 Feb 2018 13:33:54 -0800 Subject: [PATCH 162/326] Disable upload on Android Crash report upload is currently the responsibility of the embedding client (e.g. Chrome) on Android. Bug: crashpad:30 Change-Id: Ia658ec327783bd6d2ea6d7e279e942f458dd12ef Reviewed-on: https://chromium-review.googlesource.com/922877 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- handler/crash_report_upload_thread.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/handler/crash_report_upload_thread.cc b/handler/crash_report_upload_thread.cc index 8d7149ea..290c5a3f 100644 --- a/handler/crash_report_upload_thread.cc +++ b/handler/crash_report_upload_thread.cc @@ -241,6 +241,12 @@ void CrashReportUploadThread::ProcessPendingReport( CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport( const CrashReportDatabase::UploadReport* report, std::string* response_body) { +#if defined(OS_ANDROID) + // TODO(jperaza): This method can be enabled on Android after HTTPTransport is + // implemented and Crashpad takes over upload responsibilty on Android. + NOTREACHED(); + return UploadResult::kPermanentFailure; +#else std::map parameters; FileReader* reader = report->Reader(); @@ -322,6 +328,7 @@ CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport( } return UploadResult::kSuccess; +#endif // OS_ANDROID } void CrashReportUploadThread::DoWork(const WorkerThread* thread) { From 4094c2628d330181a4dd41883aba9a49816b379f Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 15 Feb 2018 12:20:48 -0800 Subject: [PATCH 163/326] Address review comments for 8d0d999 Change-Id: I697a0768d992ffa4ee35dded191960e4adbd69cf Reviewed-on: https://chromium-review.googlesource.com/922728 Reviewed-by: Robert Sesek Commit-Queue: Joshua Peraza --- client/crash_report_database_generic.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/crash_report_database_generic.cc b/client/crash_report_database_generic.cc index 5091d1b8..fc1d04e0 100644 --- a/client/crash_report_database_generic.cc +++ b/client/crash_report_database_generic.cc @@ -156,6 +156,8 @@ class ScopedLockFile { private: ScopedRemoveFile lock_file_; + + DISALLOW_COPY_AND_ASSIGN(ScopedLockFile); }; } // namespace @@ -273,8 +275,8 @@ bool CrashReportDatabaseGeneric::Initialize(const base::FilePath& path, return false; } - for (size_t i = 0; i < arraysize(kReportDirectories); ++i) { - if (!LoggingCreateDirectory(base_dir_.Append(kReportDirectories[i]), + for (const base::FilePath::CharType* subdir : kReportDirectories) { + if (!LoggingCreateDirectory(base_dir_.Append(subdir), FilePermissions::kOwnerOnly, true)) { return false; From a4d7fb4cc39080f0a05c2534f06af64d62013222 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 15 Feb 2018 11:45:36 -0800 Subject: [PATCH 164/326] Use .long for pointers on 32-bit platforms Placing a 32-bit pointer directly into a .quad results in either an unsupported relocation error at link time (ARM) or an inability to load the executable (x86). Also, only attempt to read a module's CrashpadInfo if an info address note was found. Change-Id: I053af3d77eed70af66248be88547656d2b29878a Reviewed-on: https://chromium-review.googlesource.com/922397 Reviewed-by: Scott Graham Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- client/crashpad_info_note.S | 11 +++++++++++ snapshot/linux/module_snapshot_linux.cc | 12 ++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/client/crashpad_info_note.S b/client/crashpad_info_note.S index fc65f8e2..94066fa4 100644 --- a/client/crashpad_info_note.S +++ b/client/crashpad_info_note.S @@ -16,6 +16,7 @@ // of finding the instance of CrashpadInfo g_crashpad_info without requiring // that symbol to be in the dynamic symbol table. +#include "build/build_config.h" #include "util/misc/elf_note_types.h" // namespace crashpad { @@ -41,6 +42,16 @@ name: name_end: .balign NOTE_ALIGN desc: +#if defined(ARCH_CPU_64_BITS) .quad CRASHPAD_INFO_SYMBOL +#else +#if defined(ARCH_CPU_LITTLE_ENDIAN) + .long CRASHPAD_INFO_SYMBOL + .long 0 +#else + .long 0 + .long CRASHPAD_INFO_SYMBOL +#endif // ARCH_CPU_LITTLE_ENDIAN +#endif // ARCH_CPU_64_BITS desc_end: .size CRASHPAD_NOTE_REFERENCE, .-CRASHPAD_NOTE_REFERENCE diff --git a/snapshot/linux/module_snapshot_linux.cc b/snapshot/linux/module_snapshot_linux.cc index 11896137..17a03c1f 100644 --- a/snapshot/linux/module_snapshot_linux.cc +++ b/snapshot/linux/module_snapshot_linux.cc @@ -58,13 +58,13 @@ bool ModuleSnapshotLinux::Initialize( if (notes->NextNote(nullptr, nullptr, &desc) == ElfImageReader::NoteReader::Result::kSuccess) { info_address = *reinterpret_cast(&desc[0]); - } - ProcessMemoryRange range; - if (range.Initialize(*elf_reader_->Memory())) { - auto info = std::make_unique(); - if (info->Initialize(&range, info_address)) { - crashpad_info_ = std::move(info); + ProcessMemoryRange range; + if (range.Initialize(*elf_reader_->Memory())) { + auto info = std::make_unique(); + if (info->Initialize(&range, info_address)) { + crashpad_info_ = std::move(info); + } } } From 5e5b927b38ca7fe75e6e683796b55f1d75266418 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 15 Feb 2018 14:33:41 -0800 Subject: [PATCH 165/326] Build crashpad_client_linux.cc on Android Bug: crashpad:30 Change-Id: I754468766c594c8de3cde6134645041f99864398 Reviewed-on: https://chromium-review.googlesource.com/922934 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- client/client.gyp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/client.gyp b/client/client.gyp index ed50ba50..4ef14675 100644 --- a/client/client.gyp +++ b/client/client.gyp @@ -69,6 +69,13 @@ ], }], ], + 'target_conditions': [ + ['OS=="android"', { + 'sources/': [ + ['include', '^crashpad_client_linux\\.cc$'], + ], + }], + ], 'direct_dependent_settings': { 'include_dirs': [ '..', From eec1e17ab5588680a3e1d4904f5ba497e24f496b Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 15 Feb 2018 15:58:08 -0800 Subject: [PATCH 166/326] Fix two bugs in memset()ing CrashpadInfo on size mismatch In trying to clear out the end of info when the alleged size is smaller than the current structure size, we didn't handle the opposite case. We need to continue the rest of Read() to initialize members, but need to make sure not to pass a very large (negative -> size_t) length to memset(). Additionally, I believe it meant to memset from the end of the alleged size, to the end of the local structure, rather than from the beginning of the structure. This repro'd on Fuchsia, but would affect all platforms that use it. Bug: crashpad:196, crashpad:30 Change-Id: I9c35c834010b5cb26d54156ce8f9bc538dcbf96c Reviewed-on: https://chromium-review.googlesource.com/923094 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- snapshot/crashpad_types/crashpad_info_reader.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/snapshot/crashpad_types/crashpad_info_reader.cc b/snapshot/crashpad_types/crashpad_info_reader.cc index ade9931b..dfc438fc 100644 --- a/snapshot/crashpad_types/crashpad_info_reader.cc +++ b/snapshot/crashpad_types/crashpad_info_reader.cc @@ -81,7 +81,11 @@ class CrashpadInfoReader::InfoContainerSpecific : public InfoContainer { return false; } - memset(reinterpret_cast(&info), 0, sizeof(info) - info.size); + if (sizeof(info) > info.size) { + memset(reinterpret_cast(&info) + info.size, + 0, + sizeof(info) - info.size); + } UnsetIfNotValidTriState(&info.crashpad_handler_behavior); UnsetIfNotValidTriState(&info.system_crash_reporter_forwarding); From 4717300fa4cefadeabef64346ba65aa8759d43b8 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 15 Feb 2018 17:22:30 -0800 Subject: [PATCH 167/326] Reset CrashpadInfo after CrashpadInfoReader tests Not resetting these was causing CrashpadInfoClientOptions tests to fail on Fuchsia, because dlclose() [legally] doesn't do anything, so modifying the current binaries CrashpadInfo caused the expected values from child .sos to be ignored. That could be worked around in that test too, but it's probably better to clean up the global state in this test anyway. Bug: crashpad:196 Change-Id: Ia8119ac7c554bea81e8373e2547faf192c629122 Reviewed-on: https://chromium-review.googlesource.com/923178 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- snapshot/crashpad_info_client_options_test.cc | 38 ++++---------- .../crashpad_info_reader_test.cc | 5 ++ test/BUILD.gn | 1 + test/scoped_unset_crashpad_info.h | 49 +++++++++++++++++++ test/test.gyp | 1 + 5 files changed, 66 insertions(+), 28 deletions(-) create mode 100644 test/scoped_unset_crashpad_info.h diff --git a/snapshot/crashpad_info_client_options_test.cc b/snapshot/crashpad_info_client_options_test.cc index 1fe38acf..382bf8b3 100644 --- a/snapshot/crashpad_info_client_options_test.cc +++ b/snapshot/crashpad_info_client_options_test.cc @@ -23,6 +23,7 @@ #include "gtest/gtest.h" #include "test/errors.h" #include "test/scoped_module_handle.h" +#include "test/scoped_unset_crashpad_info.h" #include "test/test_paths.h" #if defined(OS_MACOSX) @@ -57,25 +58,6 @@ TEST(CrashpadInfoClientOptions, TriStateFromCrashpadInfo) { TriState::kUnset); } -class ScopedUnsetCrashpadInfoOptions { - public: - explicit ScopedUnsetCrashpadInfoOptions(CrashpadInfo* crashpad_info) - : crashpad_info_(crashpad_info) { - } - - ~ScopedUnsetCrashpadInfoOptions() { - crashpad_info_->set_crashpad_handler_behavior(TriState::kUnset); - crashpad_info_->set_system_crash_reporter_forwarding(TriState::kUnset); - crashpad_info_->set_gather_indirectly_referenced_memory(TriState::kUnset, - 0); - } - - private: - CrashpadInfo* crashpad_info_; - - DISALLOW_COPY_AND_ASSIGN(ScopedUnsetCrashpadInfoOptions); -}; - CrashpadInfoClientOptions SelfProcessSnapshotAndGetCrashpadOptions() { #if defined(OS_MACOSX) ProcessSnapshotMac process_snapshot; @@ -109,7 +91,7 @@ TEST(CrashpadInfoClientOptions, OneModule) { ASSERT_TRUE(crashpad_info); { - ScopedUnsetCrashpadInfoOptions unset(crashpad_info); + ScopedUnsetCrashpadInfo unset(crashpad_info); crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); @@ -121,7 +103,7 @@ TEST(CrashpadInfoClientOptions, OneModule) { } { - ScopedUnsetCrashpadInfoOptions unset(crashpad_info); + ScopedUnsetCrashpadInfo unset(crashpad_info); crashpad_info->set_system_crash_reporter_forwarding(TriState::kDisabled); @@ -133,7 +115,7 @@ TEST(CrashpadInfoClientOptions, OneModule) { } { - ScopedUnsetCrashpadInfoOptions unset(crashpad_info); + ScopedUnsetCrashpadInfo unset(crashpad_info); crashpad_info->set_gather_indirectly_referenced_memory(TriState::kEnabled, 1234); @@ -188,8 +170,8 @@ TEST(CrashpadInfoClientOptions, TwoModules) { ASSERT_TRUE(remote_crashpad_info); { - ScopedUnsetCrashpadInfoOptions unset_local(local_crashpad_info); - ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); + ScopedUnsetCrashpadInfo unset_local(local_crashpad_info); + ScopedUnsetCrashpadInfo unset_remote(remote_crashpad_info); // When only one module sets a value, it applies to the entire process. remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); @@ -211,8 +193,8 @@ TEST(CrashpadInfoClientOptions, TwoModules) { } { - ScopedUnsetCrashpadInfoOptions unset_local(local_crashpad_info); - ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); + ScopedUnsetCrashpadInfo unset_local(local_crashpad_info); + ScopedUnsetCrashpadInfo unset_remote(remote_crashpad_info); // When only one module sets a value, it applies to the entire process. remote_crashpad_info->set_system_crash_reporter_forwarding( @@ -281,7 +263,7 @@ TEST_P(CrashpadInfoSizes_ClientOptions, DifferentlySizedStruct) { ASSERT_TRUE(remote_crashpad_info); { - ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); + ScopedUnsetCrashpadInfo unset_remote(remote_crashpad_info); // Make sure that a change in the remote structure can be read back out, // even though it’s a different size. @@ -296,7 +278,7 @@ TEST_P(CrashpadInfoSizes_ClientOptions, DifferentlySizedStruct) { } { - ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); + ScopedUnsetCrashpadInfo unset_remote(remote_crashpad_info); // Make sure that the portion of the remote structure lying beyond its // declared size reads as zero. diff --git a/snapshot/crashpad_types/crashpad_info_reader_test.cc b/snapshot/crashpad_types/crashpad_info_reader_test.cc index 73564177..dc592517 100644 --- a/snapshot/crashpad_types/crashpad_info_reader_test.cc +++ b/snapshot/crashpad_types/crashpad_info_reader_test.cc @@ -14,6 +14,8 @@ #include "snapshot/crashpad_types/crashpad_info_reader.h" +#include + #include #include @@ -25,6 +27,7 @@ #include "gtest/gtest.h" #include "test/multiprocess_exec.h" #include "test/process_type.h" +#include "test/scoped_unset_crashpad_info.h" #include "util/file/file_io.h" #include "util/misc/from_pointer_cast.h" #include "util/process/process_memory_native.h" @@ -47,6 +50,7 @@ class CrashpadInfoTestDataSetup { public: CrashpadInfoTestDataSetup() { CrashpadInfo* info = CrashpadInfo::GetCrashpadInfo(); + unset_.reset(new ScopedUnsetCrashpadInfo(info)); info->set_extra_memory_ranges(&extra_memory_); info->set_simple_annotations(&simple_annotations_); @@ -69,6 +73,7 @@ class CrashpadInfoTestDataSetup { } private: + std::unique_ptr unset_; SimpleAddressRangeBag extra_memory_; SimpleStringDictionary simple_annotations_; AnnotationList annotation_list_; diff --git a/test/BUILD.gn b/test/BUILD.gn index 0ddde6e1..0a17a9f8 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -40,6 +40,7 @@ static_library("test") { "scoped_module_handle.h", "scoped_temp_dir.cc", "scoped_temp_dir.h", + "scoped_unset_crashpad_info.h", "test_paths.cc", "test_paths.h", ] diff --git a/test/scoped_unset_crashpad_info.h b/test/scoped_unset_crashpad_info.h new file mode 100644 index 00000000..dd2695c9 --- /dev/null +++ b/test/scoped_unset_crashpad_info.h @@ -0,0 +1,49 @@ +// Copyright 2018 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_TEST_SCOPED_UNSET_CRASHPAD_INFO_H_ +#define CRASHPAD_TEST_SCOPED_UNSET_CRASHPAD_INFO_H_ + +#include "base/macros.h" +#include "client/crashpad_info.h" + +namespace crashpad { +namespace test { + +//! \brief Resets members of CrashpadInfo to default state when destroyed. +class ScopedUnsetCrashpadInfo { + public: + explicit ScopedUnsetCrashpadInfo(CrashpadInfo* crashpad_info) + : crashpad_info_(crashpad_info) {} + + ~ScopedUnsetCrashpadInfo() { + crashpad_info_->set_crashpad_handler_behavior(TriState::kUnset); + crashpad_info_->set_system_crash_reporter_forwarding(TriState::kUnset); + crashpad_info_->set_gather_indirectly_referenced_memory(TriState::kUnset, + 0); + crashpad_info_->set_extra_memory_ranges(nullptr); + crashpad_info_->set_simple_annotations(nullptr); + crashpad_info_->set_annotations_list(nullptr); + } + + private: + CrashpadInfo* crashpad_info_; + + DISALLOW_COPY_AND_ASSIGN(ScopedUnsetCrashpadInfo); +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_SCOPED_UNSET_CRASHPAD_INFO_H_ diff --git a/test/test.gyp b/test/test.gyp index a3721efd..bb793c88 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -71,6 +71,7 @@ 'scoped_temp_dir.h', 'scoped_temp_dir_posix.cc', 'scoped_temp_dir_win.cc', + 'scoped_unset_crashpad_info.h', 'test_paths.cc', 'test_paths.h', 'win/child_launcher.cc', From f9d160ffc6bbf50e4272567c2f5326398c43afbe Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 16 Feb 2018 03:41:01 +0000 Subject: [PATCH 168/326] Revert "Reset CrashpadInfo after CrashpadInfoReader tests" This reverts commit 4717300fa4cefadeabef64346ba65aa8759d43b8. Reason for revert: When used in with the size-testing fake CrashpadInfo's, this can overwrite past the end of them. Original change's description: > Reset CrashpadInfo after CrashpadInfoReader tests > > Not resetting these was causing CrashpadInfoClientOptions tests to fail > on Fuchsia, because dlclose() [legally] doesn't do anything, so > modifying the current binaries CrashpadInfo caused the expected values > from child .sos to be ignored. That could be worked around in that test > too, but it's probably better to clean up the global state in this test > anyway. > > Bug: crashpad:196 > Change-Id: Ia8119ac7c554bea81e8373e2547faf192c629122 > Reviewed-on: https://chromium-review.googlesource.com/923178 > Commit-Queue: Scott Graham > Reviewed-by: Joshua Peraza TBR=scottmg@chromium.org,jperaza@chromium.org Change-Id: Ia6d8db1ba24c82bb9346210ac8b66d80f42a6925 No-Presubmit: true No-Tree-Checks: true No-Try: true Bug: crashpad:196 Reviewed-on: https://chromium-review.googlesource.com/923541 Reviewed-by: Scott Graham Commit-Queue: Scott Graham --- snapshot/crashpad_info_client_options_test.cc | 38 ++++++++++---- .../crashpad_info_reader_test.cc | 5 -- test/BUILD.gn | 1 - test/scoped_unset_crashpad_info.h | 49 ------------------- test/test.gyp | 1 - 5 files changed, 28 insertions(+), 66 deletions(-) delete mode 100644 test/scoped_unset_crashpad_info.h diff --git a/snapshot/crashpad_info_client_options_test.cc b/snapshot/crashpad_info_client_options_test.cc index 382bf8b3..1fe38acf 100644 --- a/snapshot/crashpad_info_client_options_test.cc +++ b/snapshot/crashpad_info_client_options_test.cc @@ -23,7 +23,6 @@ #include "gtest/gtest.h" #include "test/errors.h" #include "test/scoped_module_handle.h" -#include "test/scoped_unset_crashpad_info.h" #include "test/test_paths.h" #if defined(OS_MACOSX) @@ -58,6 +57,25 @@ TEST(CrashpadInfoClientOptions, TriStateFromCrashpadInfo) { TriState::kUnset); } +class ScopedUnsetCrashpadInfoOptions { + public: + explicit ScopedUnsetCrashpadInfoOptions(CrashpadInfo* crashpad_info) + : crashpad_info_(crashpad_info) { + } + + ~ScopedUnsetCrashpadInfoOptions() { + crashpad_info_->set_crashpad_handler_behavior(TriState::kUnset); + crashpad_info_->set_system_crash_reporter_forwarding(TriState::kUnset); + crashpad_info_->set_gather_indirectly_referenced_memory(TriState::kUnset, + 0); + } + + private: + CrashpadInfo* crashpad_info_; + + DISALLOW_COPY_AND_ASSIGN(ScopedUnsetCrashpadInfoOptions); +}; + CrashpadInfoClientOptions SelfProcessSnapshotAndGetCrashpadOptions() { #if defined(OS_MACOSX) ProcessSnapshotMac process_snapshot; @@ -91,7 +109,7 @@ TEST(CrashpadInfoClientOptions, OneModule) { ASSERT_TRUE(crashpad_info); { - ScopedUnsetCrashpadInfo unset(crashpad_info); + ScopedUnsetCrashpadInfoOptions unset(crashpad_info); crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); @@ -103,7 +121,7 @@ TEST(CrashpadInfoClientOptions, OneModule) { } { - ScopedUnsetCrashpadInfo unset(crashpad_info); + ScopedUnsetCrashpadInfoOptions unset(crashpad_info); crashpad_info->set_system_crash_reporter_forwarding(TriState::kDisabled); @@ -115,7 +133,7 @@ TEST(CrashpadInfoClientOptions, OneModule) { } { - ScopedUnsetCrashpadInfo unset(crashpad_info); + ScopedUnsetCrashpadInfoOptions unset(crashpad_info); crashpad_info->set_gather_indirectly_referenced_memory(TriState::kEnabled, 1234); @@ -170,8 +188,8 @@ TEST(CrashpadInfoClientOptions, TwoModules) { ASSERT_TRUE(remote_crashpad_info); { - ScopedUnsetCrashpadInfo unset_local(local_crashpad_info); - ScopedUnsetCrashpadInfo unset_remote(remote_crashpad_info); + ScopedUnsetCrashpadInfoOptions unset_local(local_crashpad_info); + ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); // When only one module sets a value, it applies to the entire process. remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); @@ -193,8 +211,8 @@ TEST(CrashpadInfoClientOptions, TwoModules) { } { - ScopedUnsetCrashpadInfo unset_local(local_crashpad_info); - ScopedUnsetCrashpadInfo unset_remote(remote_crashpad_info); + ScopedUnsetCrashpadInfoOptions unset_local(local_crashpad_info); + ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); // When only one module sets a value, it applies to the entire process. remote_crashpad_info->set_system_crash_reporter_forwarding( @@ -263,7 +281,7 @@ TEST_P(CrashpadInfoSizes_ClientOptions, DifferentlySizedStruct) { ASSERT_TRUE(remote_crashpad_info); { - ScopedUnsetCrashpadInfo unset_remote(remote_crashpad_info); + ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); // Make sure that a change in the remote structure can be read back out, // even though it’s a different size. @@ -278,7 +296,7 @@ TEST_P(CrashpadInfoSizes_ClientOptions, DifferentlySizedStruct) { } { - ScopedUnsetCrashpadInfo unset_remote(remote_crashpad_info); + ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); // Make sure that the portion of the remote structure lying beyond its // declared size reads as zero. diff --git a/snapshot/crashpad_types/crashpad_info_reader_test.cc b/snapshot/crashpad_types/crashpad_info_reader_test.cc index dc592517..73564177 100644 --- a/snapshot/crashpad_types/crashpad_info_reader_test.cc +++ b/snapshot/crashpad_types/crashpad_info_reader_test.cc @@ -14,8 +14,6 @@ #include "snapshot/crashpad_types/crashpad_info_reader.h" -#include - #include #include @@ -27,7 +25,6 @@ #include "gtest/gtest.h" #include "test/multiprocess_exec.h" #include "test/process_type.h" -#include "test/scoped_unset_crashpad_info.h" #include "util/file/file_io.h" #include "util/misc/from_pointer_cast.h" #include "util/process/process_memory_native.h" @@ -50,7 +47,6 @@ class CrashpadInfoTestDataSetup { public: CrashpadInfoTestDataSetup() { CrashpadInfo* info = CrashpadInfo::GetCrashpadInfo(); - unset_.reset(new ScopedUnsetCrashpadInfo(info)); info->set_extra_memory_ranges(&extra_memory_); info->set_simple_annotations(&simple_annotations_); @@ -73,7 +69,6 @@ class CrashpadInfoTestDataSetup { } private: - std::unique_ptr unset_; SimpleAddressRangeBag extra_memory_; SimpleStringDictionary simple_annotations_; AnnotationList annotation_list_; diff --git a/test/BUILD.gn b/test/BUILD.gn index 0a17a9f8..0ddde6e1 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -40,7 +40,6 @@ static_library("test") { "scoped_module_handle.h", "scoped_temp_dir.cc", "scoped_temp_dir.h", - "scoped_unset_crashpad_info.h", "test_paths.cc", "test_paths.h", ] diff --git a/test/scoped_unset_crashpad_info.h b/test/scoped_unset_crashpad_info.h deleted file mode 100644 index dd2695c9..00000000 --- a/test/scoped_unset_crashpad_info.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018 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_TEST_SCOPED_UNSET_CRASHPAD_INFO_H_ -#define CRASHPAD_TEST_SCOPED_UNSET_CRASHPAD_INFO_H_ - -#include "base/macros.h" -#include "client/crashpad_info.h" - -namespace crashpad { -namespace test { - -//! \brief Resets members of CrashpadInfo to default state when destroyed. -class ScopedUnsetCrashpadInfo { - public: - explicit ScopedUnsetCrashpadInfo(CrashpadInfo* crashpad_info) - : crashpad_info_(crashpad_info) {} - - ~ScopedUnsetCrashpadInfo() { - crashpad_info_->set_crashpad_handler_behavior(TriState::kUnset); - crashpad_info_->set_system_crash_reporter_forwarding(TriState::kUnset); - crashpad_info_->set_gather_indirectly_referenced_memory(TriState::kUnset, - 0); - crashpad_info_->set_extra_memory_ranges(nullptr); - crashpad_info_->set_simple_annotations(nullptr); - crashpad_info_->set_annotations_list(nullptr); - } - - private: - CrashpadInfo* crashpad_info_; - - DISALLOW_COPY_AND_ASSIGN(ScopedUnsetCrashpadInfo); -}; - -} // namespace test -} // namespace crashpad - -#endif // CRASHPAD_TEST_SCOPED_UNSET_CRASHPAD_INFO_H_ diff --git a/test/test.gyp b/test/test.gyp index bb793c88..a3721efd 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -71,7 +71,6 @@ 'scoped_temp_dir.h', 'scoped_temp_dir_posix.cc', 'scoped_temp_dir_win.cc', - 'scoped_unset_crashpad_info.h', 'test_paths.cc', 'test_paths.h', 'win/child_launcher.cc', From 6798ba912e96d83dd2f4b16538fa0cf3c2db7346 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 15 Feb 2018 19:48:30 -0800 Subject: [PATCH 169/326] Reset CrashpadInfo after CrashpadInfoReader tests In preference to (the reverted) https://chromium-review.googlesource.com/c/crashpad/crashpad/+/923178 this does not share implementation with the tests in snapshot/crashpad_info_client_options_test.cc. This is not done because those tests use faked CrashpadInfo structures that are intentionally differently sized than the current defintion of CrashpadInfo, meaning that the scoped reset could overwrite past the end of the structure. Not resetting these was causing CrashpadInfoClientOptions tests to fail on Fuchsia, because dlclose() [legally] doesn't do anything, so modifying the current binaries CrashpadInfo caused the expected values from child .sos to be ignored. That could be worked around in that test too, but it's probably better to clean up the global state in this test anyway. Bug: crashpad:196 Change-Id: Ia3f81f1d5872b5ef7d543fcc68b56af4c0b6ca0a Reviewed-on: https://chromium-review.googlesource.com/923561 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- .../crashpad_info_reader_test.cc | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/snapshot/crashpad_types/crashpad_info_reader_test.cc b/snapshot/crashpad_types/crashpad_info_reader_test.cc index 73564177..87bafc68 100644 --- a/snapshot/crashpad_types/crashpad_info_reader_test.cc +++ b/snapshot/crashpad_types/crashpad_info_reader_test.cc @@ -17,6 +17,8 @@ #include #include +#include + #include "build/build_config.h" #include "client/annotation_list.h" #include "client/crashpad_info.h" @@ -43,10 +45,32 @@ constexpr TriState kGatherIndirectlyReferencedMemory = TriState::kUnset; constexpr uint32_t kIndirectlyReferencedMemoryCap = 42; +class ScopedUnsetCrashpadInfo { + public: + explicit ScopedUnsetCrashpadInfo(CrashpadInfo* crashpad_info) + : crashpad_info_(crashpad_info) {} + + ~ScopedUnsetCrashpadInfo() { + crashpad_info_->set_crashpad_handler_behavior(TriState::kUnset); + crashpad_info_->set_system_crash_reporter_forwarding(TriState::kUnset); + crashpad_info_->set_gather_indirectly_referenced_memory(TriState::kUnset, + 0); + crashpad_info_->set_extra_memory_ranges(nullptr); + crashpad_info_->set_simple_annotations(nullptr); + crashpad_info_->set_annotations_list(nullptr); + } + + private: + CrashpadInfo* crashpad_info_; + + DISALLOW_COPY_AND_ASSIGN(ScopedUnsetCrashpadInfo); +}; + class CrashpadInfoTestDataSetup { public: CrashpadInfoTestDataSetup() { CrashpadInfo* info = CrashpadInfo::GetCrashpadInfo(); + unset_.reset(new ScopedUnsetCrashpadInfo(info)); info->set_extra_memory_ranges(&extra_memory_); info->set_simple_annotations(&simple_annotations_); @@ -69,6 +93,7 @@ class CrashpadInfoTestDataSetup { } private: + std::unique_ptr unset_; SimpleAddressRangeBag extra_memory_; SimpleStringDictionary simple_annotations_; AnnotationList annotation_list_; From 0429216f59b10f191eae77904ab1476b3b9bc273 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Fri, 16 Feb 2018 09:13:35 -0800 Subject: [PATCH 170/326] linux: Add CrashReportExceptionHandler Bug: crashpad:30 Change-Id: I2855b34abe34f6d665539de0e4a227c933bd2b8c Reviewed-on: https://chromium-review.googlesource.com/922923 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- handler/BUILD.gn | 2 + handler/handler.gyp | 2 + .../linux/crash_report_exception_handler.cc | 153 ++++++++++++++++++ .../linux/crash_report_exception_handler.h | 86 ++++++++++ handler/win/crash_report_exception_handler.cc | 1 - util/misc/metrics.h | 10 ++ 6 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 handler/linux/crash_report_exception_handler.cc create mode 100644 handler/linux/crash_report_exception_handler.h diff --git a/handler/BUILD.gn b/handler/BUILD.gn index 728290a6..7cf4c3d9 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -39,6 +39,8 @@ static_library("handler") { if (crashpad_is_linux || crashpad_is_android) { sources += [ + "linux/crash_report_exception_handler.cc", + "linux/crash_report_exception_handler.h", "linux/exception_handler_server.cc", "linux/exception_handler_server.h", ] diff --git a/handler/handler.gyp b/handler/handler.gyp index 8263dfde..fb5de2a8 100644 --- a/handler/handler.gyp +++ b/handler/handler.gyp @@ -39,6 +39,8 @@ 'crash_report_upload_thread.h', 'handler_main.cc', 'handler_main.h', + 'linux/crash_report_exception_handler.cc', + 'linux/crash_report_exception_handler.h', 'linux/exception_handler_server.cc', 'linux/exception_handler_server.h', 'mac/crash_report_exception_handler.cc', diff --git a/handler/linux/crash_report_exception_handler.cc b/handler/linux/crash_report_exception_handler.cc new file mode 100644 index 00000000..3aa46981 --- /dev/null +++ b/handler/linux/crash_report_exception_handler.cc @@ -0,0 +1,153 @@ +// Copyright 2018 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 "handler/linux/crash_report_exception_handler.h" + +#include + +#include "base/logging.h" +#include "client/settings.h" +#include "minidump/minidump_file_writer.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/linux/process_snapshot_linux.h" +#include "util/linux/direct_ptrace_connection.h" +#include "util/linux/ptrace_client.h" +#include "util/misc/metrics.h" +#include "util/misc/tri_state.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +CrashReportExceptionHandler::CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map* process_annotations, + const UserStreamDataSources* user_stream_data_sources) + : database_(database), + upload_thread_(upload_thread), + process_annotations_(process_annotations), + user_stream_data_sources_(user_stream_data_sources) {} + +CrashReportExceptionHandler::~CrashReportExceptionHandler() = default; + +bool CrashReportExceptionHandler::HandleException( + pid_t client_process_id, + VMAddress exception_info_address) { + Metrics::ExceptionEncountered(); + + DirectPtraceConnection connection; + if (!connection.Initialize(client_process_id)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kDirectPtraceFailed); + return false; + } + + return HandleExceptionWithConnection(&connection, exception_info_address); +} + +bool CrashReportExceptionHandler::HandleExceptionWithBroker( + pid_t client_process_id, + VMAddress exception_info_address, + int broker_sock) { + Metrics::ExceptionEncountered(); + + PtraceClient client; + if (client.Initialize(broker_sock, client_process_id)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kBrokeredPtraceFailed); + return false; + } + + return HandleExceptionWithConnection(&client, exception_info_address); +} + +bool CrashReportExceptionHandler::HandleExceptionWithConnection( + PtraceConnection* connection, + VMAddress exception_info_address) { + ProcessSnapshotLinux process_snapshot; + if (!process_snapshot.Initialize(connection)) { + Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed); + return false; + } + + if (!process_snapshot.InitializeException(exception_info_address)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kExceptionInitializationFailed); + return false; + } + + Metrics::ExceptionCode(process_snapshot.Exception()->Exception()); + + CrashpadInfoClientOptions client_options; + process_snapshot.GetCrashpadOptions(&client_options); + if (client_options.crashpad_handler_behavior != TriState::kDisabled) { + UUID client_id; + Settings* const settings = database_->GetSettings(); + if (settings) { + // If GetSettings() or GetClientID() fails, something else will log a + // message and client_id will be left at its default value, all zeroes, + // which is appropriate. + settings->GetClientID(&client_id); + } + + process_snapshot.SetClientID(client_id); + process_snapshot.SetAnnotationsSimpleMap(*process_annotations_); + + std::unique_ptr new_report; + CrashReportDatabase::OperationStatus database_status = + database_->PrepareNewCrashReport(&new_report); + if (database_status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "PrepareNewCrashReport failed"; + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kPrepareNewCrashReportFailed); + return false; + } + + process_snapshot.SetReportID(new_report->ReportID()); + + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(&process_snapshot); + AddUserExtensionStreams( + user_stream_data_sources_, &process_snapshot, &minidump); + + if (!minidump.WriteEverything(new_report->Writer())) { + LOG(ERROR) << "WriteEverything failed"; + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kMinidumpWriteFailed); + return false; + } + + UUID uuid; + database_status = + database_->FinishedWritingCrashReport(std::move(new_report), &uuid); + if (database_status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "FinishedWritingCrashReport failed"; + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kFinishedWritingCrashReportFailed); + return false; + } + + if (upload_thread_) { + upload_thread_->ReportPending(uuid); + } else { + database_->SkipReportUpload( + uuid, Metrics::CrashSkippedReason::kUploadsDisabled); + } + } + + Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess); + return true; +} + +} // namespace crashpad diff --git a/handler/linux/crash_report_exception_handler.h b/handler/linux/crash_report_exception_handler.h new file mode 100644 index 00000000..ca14f81a --- /dev/null +++ b/handler/linux/crash_report_exception_handler.h @@ -0,0 +1,86 @@ +// Copyright 2018 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_HANDLER_LINUX_CRASH_REPORT_EXCEPTION_HANDLER_H_ +#define CRASHPAD_HANDLER_LINUX_CRASH_REPORT_EXCEPTION_HANDLER_H_ + +#include +#include + +#include "base/macros.h" +#include "client/crash_report_database.h" +#include "handler/crash_report_upload_thread.h" +#include "handler/linux/exception_handler_server.h" +#include "handler/user_stream_data_source.h" +#include "util/linux/ptrace_connection.h" +#include "util/misc/address_types.h" + +namespace crashpad { + +//! \brief An exception handler that writes crash reports for exceptions +//! to a CrashReportDatabase. +class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { + public: + //! \brief Creates a new object that will store crash reports in \a database. + //! + //! \param[in] database The database to store crash reports in. Weak. + //! \param[in] upload_thread The upload thread to notify when a new crash + //! report is written into \a database. Report upload is skipped if this + //! value is `nullptr`. + //! \param[in] process_annotations A map of annotations to insert as + //! process-level annotations into each crash report that is written. Do + //! not confuse this with module-level annotations, which are under the + //! control of the crashing process, and are used to implement Chrome’s + //! “crash keys.” Process-level annotations are those that are beyond the + //! control of the crashing process, which must reliably be set even if + //! the process crashes before it’s able to establish its own annotations. + //! To interoperate with Breakpad servers, the recommended practice is to + //! specify values for the `"prod"` and `"ver"` keys as process + //! annotations. + //! \param[in] user_stream_data_sources Data sources to be used to extend + //! crash reports. For each crash report that is written, the data sources + //! are called in turn. These data sources may contribute additional + //! minidump streams. `nullptr` if not required. + CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map* process_annotations, + const UserStreamDataSources* user_stream_data_sources); + + ~CrashReportExceptionHandler(); + + // ExceptionHandlerServer::Delegate: + + bool HandleException(pid_t client_process_id, + VMAddress exception_info_address) override; + + bool HandleExceptionWithBroker(pid_t client_process_id, + VMAddress exception_info_address, + int broker_sock) override; + + private: + bool HandleExceptionWithConnection(PtraceConnection* connection, + VMAddress exception_info_address); + + CrashReportDatabase* database_; // weak + CrashReportUploadThread* upload_thread_; // weak + const std::map* process_annotations_; // weak + const UserStreamDataSources* user_stream_data_sources_; // weak + + DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler); +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_LINUX_CRASH_REPORT_EXCEPTION_HANDLER_H_ diff --git a/handler/win/crash_report_exception_handler.cc b/handler/win/crash_report_exception_handler.cc index b1ea8446..726dcb39 100644 --- a/handler/win/crash_report_exception_handler.cc +++ b/handler/win/crash_report_exception_handler.cc @@ -60,7 +60,6 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException( ProcessSuspensionState::kSuspended, exception_information_address, debug_critical_section_address)) { - LOG(WARNING) << "ProcessSnapshotWin::Initialize failed"; Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed); return kTerminationCodeSnapshotFailed; } diff --git a/util/misc/metrics.h b/util/misc/metrics.h index fbbf3ecb..54ccb843 100644 --- a/util/misc/metrics.h +++ b/util/misc/metrics.h @@ -119,6 +119,16 @@ class Metrics { //! \brief There was a database error in attempt to complete the report. kFinishedWritingCrashReportFailed = 7, + //! \brief An attempt to directly `ptrace` the target failed. + //! + //! This value is only used on Linux/Android. + kDirectPtraceFailed = 8, + + //! \brief An attempt to `ptrace` via a PtraceBroker failed. + //! + //! This value is only used on Linux/Android. + kBrokeredPtraceFailed = 9, + //! \brief The number of values in this enumeration; not a valid value. kMaxValue }; From f38af628c9dc8bef6cf34cd69037c67092e13649 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 15 Feb 2018 22:12:59 -0800 Subject: [PATCH 171/326] fuchsia: Don't fail rename if source == dest Fuchsia errors out in rename() when source == dest. I believe this is incorrect according to http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html, but it's also relatively easy to work around in our code, and this fixes CrashReportDatabaseTest.RequestUpload. This is ZX-1729 upstream. Bug: crashpad:196 Change-Id: I27473183b04484e146a7bd9e87e60be3aeff1932 Reviewed-on: https://chromium-review.googlesource.com/923708 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- client/crash_report_database_test.cc | 10 +++++----- util/file/filesystem_posix.cc | 8 ++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/client/crash_report_database_test.cc b/client/crash_report_database_test.cc index 7760b4a1..55bcf3cd 100644 --- a/client/crash_report_database_test.cc +++ b/client/crash_report_database_test.cc @@ -628,21 +628,21 @@ TEST_F(CrashReportDatabaseTest, RequestUpload) { ASSERT_EQ(pending_reports.size(), 2u); // Check individual reports. - const CrashReportDatabase::Report* expicitly_requested_report; + const CrashReportDatabase::Report* explicitly_requested_report; const CrashReportDatabase::Report* pending_report; if (pending_reports[0].uuid == report_0_uuid) { pending_report = &pending_reports[0]; - expicitly_requested_report = &pending_reports[1]; + explicitly_requested_report = &pending_reports[1]; } else { pending_report = &pending_reports[1]; - expicitly_requested_report = &pending_reports[0]; + explicitly_requested_report = &pending_reports[0]; } EXPECT_EQ(pending_report->uuid, report_0_uuid); EXPECT_FALSE(pending_report->upload_explicitly_requested); - EXPECT_EQ(expicitly_requested_report->uuid, report_1_uuid); - EXPECT_TRUE(expicitly_requested_report->upload_explicitly_requested); + EXPECT_EQ(explicitly_requested_report->uuid, report_1_uuid); + EXPECT_TRUE(explicitly_requested_report->upload_explicitly_requested); // Explicitly requested reports will not have upload_explicitly_requested bit // after getting skipped. diff --git a/util/file/filesystem_posix.cc b/util/file/filesystem_posix.cc index b1f19f62..c2234dae 100644 --- a/util/file/filesystem_posix.cc +++ b/util/file/filesystem_posix.cc @@ -65,6 +65,14 @@ bool LoggingCreateDirectory(const base::FilePath& path, bool MoveFileOrDirectory(const base::FilePath& source, const base::FilePath& dest) { +#if defined(OS_FUCHSIA) + // Fuchsia fails and sets errno to EINVAL if source and dest are the same. + // Upstream bug is ZX-1729. + if (!source.empty() && source == dest) { + return true; + } +#endif // OS_FUCHSIA + if (rename(source.value().c_str(), dest.value().c_str()) != 0) { PLOG(ERROR) << "rename " << source.value().c_str() << ", " << dest.value().c_str(); From 04036023934373c1fc4bbd8e29da70d7bb4a46b8 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 16 Feb 2018 09:55:34 -0800 Subject: [PATCH 172/326] Fix CrashpadInfoSizes_ClientOptions/CrashpadInfoSizes_ClientOptions These tests needed to be updated to expose CrashpadInfo in the same way as the main CrashpadInfo g_crashpad_info is found on Linux/Android/Fuchsia. Unfortunately, while the tests pass on Fuchsia when run in isolation, the implementation of dlclose() on Fuchsia currently does nothing. So, if the full test suite is run, there's interference between the test modules (i.e. the values in _small vs. the values in _large), so the tests fail. I filed ZX-1728 upstream about this to see if it might be implemented, or if the test will need to spawn a clean child to do the module load tests in. Bug: crashpad:196 Change-Id: I9ee01b142a29c508c6967dc83da824afa254d379 Reviewed-on: https://chromium-review.googlesource.com/923182 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- client/crashpad_info.cc | 8 ++-- snapshot/BUILD.gn | 22 ++++++--- snapshot/crashpad_info_size_test_module.cc | 13 ++--- snapshot/crashpad_info_size_test_note.S | 56 ++++++++++++++++++++++ snapshot/snapshot_test.gyp | 26 ++++++++++ 5 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 snapshot/crashpad_info_size_test_note.S diff --git a/client/crashpad_info.cc b/client/crashpad_info.cc index 098b819a..7c1316e8 100644 --- a/client/crashpad_info.cc +++ b/client/crashpad_info.cc @@ -59,10 +59,6 @@ __attribute__(( // Put the structure in a well-known section name where it can be easily // found without having to consult the symbol table. section(SEG_DATA ",crashpad_info"), - - // There's no need to expose this as a public symbol from the symbol table. - // All accesses from the outside can locate the well-known section name. - visibility("hidden"), #endif #if defined(ADDRESS_SANITIZER) @@ -74,6 +70,10 @@ __attribute__(( aligned(64), #endif // defined(ADDRESS_SANITIZER) + // There's no need to expose this as a public symbol from the symbol table. + // All accesses from the outside can locate the well-known section name. + visibility("hidden"), + // The “used” attribute prevents the structure from being dead-stripped. used)) diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index f4a592b1..0fe97d3f 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -412,10 +412,15 @@ loadable_module("crashpad_snapshot_test_module_large") { sources = [ "crashpad_info_size_test_module.cc", ] + + deps = [] + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ "crashpad_info_size_test_note.S" ] + deps += [ "../util" ] + } + defines = [ "CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE" ] - deps = [ - "../third_party/mini_chromium:base", - ] + deps += [ "../third_party/mini_chromium:base" ] } loadable_module("crashpad_snapshot_test_module_small") { @@ -423,10 +428,15 @@ loadable_module("crashpad_snapshot_test_module_small") { sources = [ "crashpad_info_size_test_module.cc", ] + + deps = [] + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ "crashpad_info_size_test_note.S" ] + deps += [ "../util" ] + } + defines = [ "CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL" ] - deps = [ - "../third_party/mini_chromium:base", - ] + deps += [ "../third_party/mini_chromium:base" ] } if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { diff --git a/snapshot/crashpad_info_size_test_module.cc b/snapshot/crashpad_info_size_test_module.cc index d39fada3..53f4a3ff 100644 --- a/snapshot/crashpad_info_size_test_module.cc +++ b/snapshot/crashpad_info_size_test_module.cc @@ -23,7 +23,6 @@ #endif // OS_MACOSX namespace crashpad { -namespace { #if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL) == \ defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE) @@ -70,16 +69,12 @@ struct TestCrashpadInfo { __attribute__(( #if defined(OS_MACOSX) section(SEG_DATA ",crashpad_info"), -#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) - section("crashpad_info"), -#else -#error Port #endif #if defined(ADDRESS_SANITIZER) aligned(64), #endif // defined(ADDRESS_SANITIZER) - used, - visibility("hidden"))) + visibility("hidden"), + used)) #elif defined(OS_WIN) #pragma section("CPADinfo", read, write) __declspec(allocate("CPADinfo")) @@ -106,7 +101,6 @@ TestCrashpadInfo g_test_crashpad_info = {'CPad', #endif // CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE }; -} // namespace } // namespace crashpad extern "C" { @@ -119,6 +113,9 @@ __declspec(dllexport) #error Port #endif // OS_POSIX crashpad::TestCrashpadInfo* TestModule_GetCrashpadInfo() { + // Note that there's no need to do the back-reference here to the note on + // POSIX like CrashpadInfo::GetCrashpadInfo() because the note .S file is + // directly included into this test binary. return &crashpad::g_test_crashpad_info; } diff --git a/snapshot/crashpad_info_size_test_note.S b/snapshot/crashpad_info_size_test_note.S new file mode 100644 index 00000000..a355a831 --- /dev/null +++ b/snapshot/crashpad_info_size_test_note.S @@ -0,0 +1,56 @@ +// Copyright 2018 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. + +// This note section is used on ELF platforms to give ElfImageReader a method +// of finding the instance of CrashpadInfo g_crashpad_info without requiring +// that symbol to be in the dynamic symbol table. + +#include "build/build_config.h" +#include "util/misc/elf_note_types.h" + +// namespace crashpad { +// CrashpadInfo g_test_crashpad_info; +// } // namespace crashpad +#define TEST_CRASHPAD_INFO_SYMBOL _ZN8crashpad20g_test_crashpad_infoE + +#define NOTE_ALIGN 4 + + // This section must be "a"llocated so that it appears in the final binary at + // runtime, and "w"ritable so that the relocation to TEST_CRASHPAD_INFO_SYMBOL + // can be performed. + .section .note.crashpad.info,"aw",%note + .balign NOTE_ALIGN + .type info_size_test_note, %object +info_size_test_note: + .long name_end - name // namesz + .long desc_end - desc // descsz + .long CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO // type +name: + .asciz CRASHPAD_ELF_NOTE_NAME +name_end: + .balign NOTE_ALIGN +desc: +#if defined(ARCH_CPU_64_BITS) + .quad TEST_CRASHPAD_INFO_SYMBOL +#else +#if defined(ARCH_CPU_LITTLE_ENDIAN) + .long TEST_CRASHPAD_INFO_SYMBOL + .long 0 +#else + .long 0 + .long TEST_CRASHPAD_INFO_SYMBOL +#endif // ARCH_CPU_LITTLE_ENDIAN +#endif // ARCH_CPU_64_BITS +desc_end: + .size info_size_test_note, .-info_size_test_note diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index 277bf6e8..d8c0c08f 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -182,6 +182,19 @@ 'sources': [ 'crashpad_info_size_test_module.cc', ], + 'include_dirs': [ + '..', + ], + 'conditions': [ + ['OS=="linux" or OS=="android"', { + 'sources': [ + 'crashpad_info_size_test_note.S', + ], + 'dependencies': [ + '../util/util.gyp:crashpad_util', + ], + }], + ], }, { 'target_name': 'crashpad_snapshot_test_module_small', @@ -195,6 +208,19 @@ 'sources': [ 'crashpad_info_size_test_module.cc', ], + 'include_dirs': [ + '..', + ], + 'conditions': [ + ['OS=="linux" or OS=="android"', { + 'sources': [ + 'crashpad_info_size_test_note.S', + ], + 'dependencies': [ + '../util/util.gyp:crashpad_util', + ], + }], + ], }, { 'target_name': 'crashpad_snapshot_test_both_dt_hash_styles', From 10222b12362c94aad2fbe2dfb4b277eab3275446 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 16 Feb 2018 09:48:15 -0800 Subject: [PATCH 173/326] fuchsia: Disable TimeZone.Basic test as timezones are non-functional TZ related functionality isn't working in Fuchsia right now https://fuchsia.googlesource.com/zircon/+/master/third_party/ulib/musl/src/time/__tz.c#9 so this test has no chance of working. Disable for now. Bug: crashpad:196 Change-Id: I77c3d38e5f5fc98f2e7bf8c493df269eb142ce75 Reviewed-on: https://chromium-review.googlesource.com/923201 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- snapshot/BUILD.gn | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 0fe97d3f..0f3c5bdb 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -338,7 +338,11 @@ source_set("snapshot_test") { "win/process_snapshot_win_test.cc", "win/system_snapshot_win_test.cc", ] - } else { + } else if (!crashpad_is_fuchsia) { + # Timezones are currently non-functional on Fuchsia: + # https://fuchsia.googlesource.com/zircon/+/master/third_party/ulib/musl/src/time/__tz.c#9 + # https://crashpad.chromium.org/bug/196. Relevant upstream bugs are ZX-337 + # and ZX-1731. sources += [ "posix/timezone_test.cc" ] } From 5cb869392eeda631f4c07c5ca1a141b5c8572b82 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 16 Feb 2018 12:24:09 -0800 Subject: [PATCH 174/326] fuchsia: Compile out LoggingLock/UnlockFile, add DCHECKs to Settings Fuchsia does not currently support any sort of file locking. Until a lock server can be implemented, compile out the calls to flock(). In the one current non-test user of locking (Settings) add a pseudo-implementation that will DCHECK if there is ever contention on the lock. Bug: crashpad:217, crashpad:196 Change-Id: Ifdf7e00886ad7e7778745f1ae8f0ce2a86f0ae3b Reviewed-on: https://chromium-review.googlesource.com/924312 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- client/settings.cc | 71 +++++++++++++++++++++++++++++++++++--- client/settings.h | 41 ++++++++++++++++++++-- util/file/file_io.h | 7 ++++ util/file/file_io_posix.cc | 15 ++------ util/file/file_io_test.cc | 7 ++++ 5 files changed, 122 insertions(+), 19 deletions(-) diff --git a/client/settings.cc b/client/settings.cc index 8d4dbec8..20bd2581 100644 --- a/client/settings.cc +++ b/client/settings.cc @@ -20,10 +20,55 @@ #include "base/logging.h" #include "base/posix/eintr_wrapper.h" +#include "util/file/filesystem.h" #include "util/numeric/in_range_cast.h" namespace crashpad { +#if defined(OS_FUCHSIA) + +Settings::ScopedLockedFileHandle::ScopedLockedFileHandle() + : handle_(kInvalidFileHandle), lockfile_path_() { + } + +Settings::ScopedLockedFileHandle::ScopedLockedFileHandle( + FileHandle handle, + const base::FilePath& lockfile_path) + : handle_(handle), lockfile_path_(lockfile_path) { +} + +Settings::ScopedLockedFileHandle::ScopedLockedFileHandle( + ScopedLockedFileHandle&& other) + : handle_(other.handle_), lockfile_path_(other.lockfile_path_) { + other.handle_ = kInvalidFileHandle; + other.lockfile_path_ = base::FilePath(); +} + +Settings::ScopedLockedFileHandle& Settings::ScopedLockedFileHandle::operator=( + ScopedLockedFileHandle&& other) { + handle_ = other.handle_; + lockfile_path_ = other.lockfile_path_; + + other.handle_ = kInvalidFileHandle; + other.lockfile_path_ = base::FilePath(); + return *this; +} + +Settings::ScopedLockedFileHandle::~ScopedLockedFileHandle() { + Destroy(); +} + +void Settings::ScopedLockedFileHandle::Destroy() { + if (handle_ != kInvalidFileHandle) { + CheckedCloseFile(handle_); + } + if (!lockfile_path_.empty()) { + DCHECK(LoggingRemoveFile(lockfile_path_)); + } +} + +#else // OS_FUCHSIA + namespace internal { // static @@ -36,6 +81,8 @@ void ScopedLockedFileHandleTraits::Free(FileHandle handle) { } // namespace internal +#endif // OS_FUCHSIA + struct Settings::Data { static const uint32_t kSettingsMagic = 'CPds'; static const uint32_t kSettingsVersion = 1; @@ -142,18 +189,33 @@ bool Settings::SetLastUploadAttemptTime(time_t time) { // static Settings::ScopedLockedFileHandle Settings::MakeScopedLockedFileHandle( FileHandle file, - FileLocking locking) { + FileLocking locking, + const base::FilePath& file_path) { ScopedFileHandle scoped(file); +#if defined(OS_FUCHSIA) + base::FilePath lockfile_path(file_path.value() + ".__lock__"); + if (scoped.is_valid()) { + ScopedFileHandle lockfile_scoped( + LoggingOpenFileForWrite(lockfile_path, + FileWriteMode::kCreateOrFail, + FilePermissions::kWorldReadable)); + // This is a lightweight attempt to try to catch racy behavior. + DCHECK(lockfile_scoped.is_valid()); + return ScopedLockedFileHandle(scoped.release(), lockfile_path); + } + return ScopedLockedFileHandle(scoped.release(), base::FilePath()); +#else if (scoped.is_valid()) { if (!LoggingLockFile(scoped.get(), locking)) scoped.reset(); } return ScopedLockedFileHandle(scoped.release()); +#endif } Settings::ScopedLockedFileHandle Settings::OpenForReading() { - return MakeScopedLockedFileHandle(LoggingOpenFileForRead(file_path()), - FileLocking::kShared); + return MakeScopedLockedFileHandle( + LoggingOpenFileForRead(file_path()), FileLocking::kShared, file_path()); } Settings::ScopedLockedFileHandle Settings::OpenForReadingAndWriting( @@ -169,7 +231,8 @@ Settings::ScopedLockedFileHandle Settings::OpenForReadingAndWriting( file_path(), mode, FilePermissions::kWorldReadable); } - return MakeScopedLockedFileHandle(handle, FileLocking::kExclusive); + return MakeScopedLockedFileHandle( + handle, FileLocking::kExclusive, file_path()); } bool Settings::OpenAndReadSettings(Data* out_data) { diff --git a/client/settings.h b/client/settings.h index 488080c1..a2b0c746 100644 --- a/client/settings.h +++ b/client/settings.h @@ -22,6 +22,7 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "base/scoped_generic.h" +#include "build/build_config.h" #include "util/file/file_io.h" #include "util/misc/initialization_state.h" #include "util/misc/uuid.h" @@ -114,11 +115,45 @@ class Settings { struct Data; // This must be constructed with MakeScopedLockedFileHandle(). It both unlocks - // and closes the file on destruction. + // and closes the file on destruction. Note that on Fuchsia, this handle DOES + // NOT offer correct operation, only an attempt to DCHECK if racy behavior is + // detected. +#if defined(OS_FUCHSIA) + struct ScopedLockedFileHandle { + public: + ScopedLockedFileHandle(); + ScopedLockedFileHandle(FileHandle handle, + const base::FilePath& lockfile_path); + ScopedLockedFileHandle(ScopedLockedFileHandle&& other); + ScopedLockedFileHandle& operator=(ScopedLockedFileHandle&& other); + ~ScopedLockedFileHandle(); + + // These mirror the non-Fuchsia ScopedLockedFileHandle via ScopedGeneric so + // that calling code can pretend this implementation is the same. + bool is_valid() const { return handle_ != kInvalidFileHandle; } + FileHandle get() { return handle_; } + void reset() { + Destroy(); + handle_ = kInvalidFileHandle; + lockfile_path_ = base::FilePath(); + } + + private: + void Destroy(); + + FileHandle handle_; + base::FilePath lockfile_path_; + + DISALLOW_COPY_AND_ASSIGN(ScopedLockedFileHandle); + }; +#else // OS_FUCHSIA using ScopedLockedFileHandle = base::ScopedGeneric; - static ScopedLockedFileHandle MakeScopedLockedFileHandle(FileHandle file, - FileLocking locking); +#endif // OS_FUCHSIA + static ScopedLockedFileHandle MakeScopedLockedFileHandle( + FileHandle file, + FileLocking locking, + const base::FilePath& file_path); // Opens the settings file for reading. On error, logs a message and returns // the invalid handle. diff --git a/util/file/file_io.h b/util/file/file_io.h index 044a0a69..050c0749 100644 --- a/util/file/file_io.h +++ b/util/file/file_io.h @@ -404,6 +404,11 @@ FileHandle LoggingOpenFileForReadAndWrite(const base::FilePath& path, FileWriteMode mode, FilePermissions permissions); +// Fuchsia does not currently support any sort of file locking. See +// https://crashpad.chromium.org/bug/196 and +// https://crashpad.chromium.org/bug/217. +#if !defined(OS_FUCHSIA) + //! \brief Locks the given \a file using `flock()` on POSIX or `LockFileEx()` on //! Windows. //! @@ -433,6 +438,8 @@ bool LoggingLockFile(FileHandle file, FileLocking locking); //! \return `true` on success, or `false` and a message will be logged. bool LoggingUnlockFile(FileHandle file); +#endif // !OS_FUCHSIA + //! \brief Wraps `lseek()` or `SetFilePointerEx()`. Logs an error if the //! operation fails. //! diff --git a/util/file/file_io_posix.cc b/util/file/file_io_posix.cc index bb6e87ea..f3116169 100644 --- a/util/file/file_io_posix.cc +++ b/util/file/file_io_posix.cc @@ -157,18 +157,7 @@ FileHandle LoggingOpenFileForReadAndWrite(const base::FilePath& path, return fd; } -#if defined(OS_FUCHSIA) -int flock(int, int) { - // TODO(scottmg): https://crashpad.chromium.org/bug/196: - // This was removed from the libc of Fuchsia recently. A new implementation of - // Settings is being worked on that doesn't require flock(), but until then, - // it's more useful to have it link, but fail at runtime than it is to exclude - // a lot of code (Settings, which requires excludes the CrashReportDatabase, - // which requires excluding a variety of tests, the handler, and so on.). - NOTREACHED(); - return ENOSYS; -} -#endif // OS_FUCHSIA +#if !defined(OS_FUCHSIA) bool LoggingLockFile(FileHandle file, FileLocking locking) { int operation = (locking == FileLocking::kShared) ? LOCK_SH : LOCK_EX; @@ -183,6 +172,8 @@ bool LoggingUnlockFile(FileHandle file) { return rv == 0; } +#endif // !OS_FUCHSIA + FileOffset LoggingSeekFile(FileHandle file, FileOffset offset, int whence) { off_t rv = lseek(file, offset, whence); PLOG_IF(ERROR, rv < 0) << "lseek"; diff --git a/util/file/file_io_test.cc b/util/file/file_io_test.cc index fdcf7e9a..4446c320 100644 --- a/util/file/file_io_test.cc +++ b/util/file/file_io_test.cc @@ -523,6 +523,11 @@ TEST(FileIO, FileShareMode_Write_Write) { FileShareModeTest(ReadOrWrite::kWrite, ReadOrWrite::kWrite); } +// Fuchsia does not currently support any sort of file locking. See +// https://crashpad.chromium.org/bug/196 and +// https://crashpad.chromium.org/bug/217. +#if !defined(OS_FUCHSIA) + TEST(FileIO, MultipleSharedLocks) { ScopedTempDir temp_dir; base::FilePath shared_file = @@ -648,6 +653,8 @@ TEST(FileIO, SharedVsExclusives) { LockingTest(FileLocking::kShared, FileLocking::kExclusive); } +#endif // !OS_FUCHSIA + TEST(FileIO, FileSizeByHandle) { EXPECT_EQ(LoggingFileSizeByHandle(kInvalidFileHandle), -1); From 4b7895615808940ff9c51d445c2c7902d80b3d01 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 16 Feb 2018 12:12:43 -0800 Subject: [PATCH 175/326] Add .hidden to CRASHPAD_NOTE_REFERENCE This ensures the symbol is not exposed in the binaries final symbol table. .globl needs to be kept so that it can still be linked against (in this case, by crashpad_info.cc.). (Tested on Fuchsia, hopefully functional elsewhere...) Bug: crashpad:196 Change-Id: I8c6b26cdd742a1c040779884fd97a8a34068dbdc Reviewed-on: https://chromium-review.googlesource.com/924337 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- client/crashpad_info_note.S | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/crashpad_info_note.S b/client/crashpad_info_note.S index 94066fa4..8084db94 100644 --- a/client/crashpad_info_note.S +++ b/client/crashpad_info_note.S @@ -31,7 +31,10 @@ // be performed. .section .note.crashpad.info,"aw",%note .balign NOTE_ALIGN + # .globl indicates that it's available to link against other .o files. .hidden + # indicates that it will not appear in the executable's symbol table. .globl CRASHPAD_NOTE_REFERENCE + .hidden CRASHPAD_NOTE_REFERENCE .type CRASHPAD_NOTE_REFERENCE, %object CRASHPAD_NOTE_REFERENCE: .long name_end - name // namesz From 8ee14eef08f39549b1a301a8c5c2e2a445bb6cbd Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 16 Feb 2018 13:49:30 -0800 Subject: [PATCH 176/326] fuchsia: Fix some packaging when run isn't from Crashpad source root - Use gn --root rather than setting the cwd when running GN so it can find //.gn. - Use source-relative path for assets when building the target path, see for example the failure in: https://build.chromium.org/p/client.crashpad/builders/crashpad_fuchsia_x64_rel/builds/77/steps/run%20tests/logs/stdio Bug: crashpad:196 Change-Id: If95636fcb826c22d9d9543cad02f815780621414 Reviewed-on: https://chromium-review.googlesource.com/923436 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- build/run_tests.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/build/run_tests.py b/build/run_tests.py index 6c2dacf4..6e6c9108 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -70,10 +70,11 @@ def _BinaryDirTargetOS(binary_dir): if gn_path: # Look for a GN “target_os”. - popen = subprocess.Popen( - [gn_path, 'args', binary_dir, '--list=target_os', '--short'], - shell=IS_WINDOWS_HOST, stdout=subprocess.PIPE, stderr=open(os.devnull), - cwd=CRASHPAD_DIR) + popen = subprocess.Popen([gn_path, '--root=' + CRASHPAD_DIR, + 'args', binary_dir, + '--list=target_os', '--short'], + shell=IS_WINDOWS_HOST, + stdout=subprocess.PIPE, stderr=open(os.devnull)) value = popen.communicate()[0] if popen.returncode == 0: match = re.match('target_os = "(.*)"$', value.decode('utf-8')) @@ -310,13 +311,13 @@ def _GetFuchsiaSDKRoot(): def _GenerateFuchsiaRuntimeDepsFiles(binary_dir, tests): """Ensures a /.runtime_deps file exists for each test.""" - targets_file = os.path.abspath(os.path.join(binary_dir, 'targets.txt')) + targets_file = os.path.join(binary_dir, 'targets.txt') with open(targets_file, 'wb') as f: f.write('//:' + '\n//:'.join(tests) + '\n') gn_path = _FindGNFromBinaryDir(binary_dir) subprocess.check_call( - [gn_path, 'gen', binary_dir, '--runtime-deps-list-file=' + targets_file], - cwd=CRASHPAD_DIR) + [gn_path, '--root=' + CRASHPAD_DIR, 'gen', binary_dir, + '--runtime-deps-list-file=' + targets_file]) def _HandleOutputFromFuchsiaLogListener(process, done_message): @@ -373,7 +374,8 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line): staging_root = test_root + '/pkg' # Make a staging directory tree on the target. - directories_to_create = [tmp_root, '%s/bin' % staging_root, + directories_to_create = [tmp_root, + '%s/bin' % staging_root, '%s/assets' % staging_root] netruncmd(['mkdir', '-p'] + directories_to_create) @@ -396,7 +398,8 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line): target_path = os.path.join( staging_root, 'bin', local_path[len(binary_dir)+1:]) else: - target_path = os.path.join(staging_root, 'assets', local_path) + relative_path = os.path.relpath(local_path, CRASHPAD_DIR) + target_path = os.path.join(staging_root, 'assets', relative_path) netcp_path = os.path.join(sdk_root, 'tools', 'netcp') subprocess.check_call([netcp_path, local_path, device_name + ':' + target_path], From 4a9d422652a13b9226eb264909672deb9ef886a7 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 16 Feb 2018 14:11:26 -0800 Subject: [PATCH 177/326] Turn fuchsia trybots on by default All currently compiled-in/enabled tests should be reliably passing now, so add Fuchsia bots to the CQ. (Of course, there's a lot of functionality still compiled out). Bug: crashpad:196 Change-Id: I1544798afefd6a505200c9ae38253d73eb6497ef Reviewed-on: https://chromium-review.googlesource.com/923425 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- infra/config/cq.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg index 83be592f..42b56d9a 100644 --- a/infra/config/cq.cfg +++ b/infra/config/cq.cfg @@ -37,6 +37,8 @@ verifiers { builders { name: "crashpad_try_mac_rel" } builders { name: "crashpad_try_win_dbg" } builders { name: "crashpad_try_win_rel" } + builders { name: "crashpad_try_fuchsia_x64_dbg" } + builders { name: "crashpad_try_fuchsia_x64_rel" } # https://crbug.com/743139 - disabled until we can move these to swarming, # at which point we can just remove them. #builders { name: "crashpad_try_win_x86_dbg" } From ec33c25797f9bdf05fb5abe420b21a50e982193c Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 16 Feb 2018 17:01:01 -0800 Subject: [PATCH 178/326] fuchsia: Don't include sys/resource.h, recently removed from SDK Bug: crashpad:196 Change-Id: Id4a16a1d44d99b658c78900a15db231ba14b0714 Reviewed-on: https://chromium-review.googlesource.com/924747 Reviewed-by: Scott Graham Commit-Queue: Scott Graham --- test/multiprocess_exec_test_child.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/multiprocess_exec_test_child.cc b/test/multiprocess_exec_test_child.cc index f2761ea5..8c77015c 100644 --- a/test/multiprocess_exec_test_child.cc +++ b/test/multiprocess_exec_test_child.cc @@ -23,7 +23,9 @@ #include "build/build_config.h" #if defined(OS_POSIX) +#if !defined(OS_FUCHSIA) #include +#endif // !OS_FUCHSIA #include #elif defined(OS_WIN) #include From 8b738cd24d59b2863b0f7d1104597d7a51c5fd52 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 19 Feb 2018 10:25:51 -0800 Subject: [PATCH 179/326] Don't include crash_report_database_generic.cc on Win/Mac Reported by Mihnea Craciun at https://groups.google.com/a/chromium.org/forum/?utm_medium=email&utm_source=footer#!msg/crashpad-dev/IvAnF1bisFg/mkmai0vvBgAJ. Bug: crashpad:30 Change-Id: Ia1bca6e832062d1e454285ac0b3c97b56760c417 Reviewed-on: https://chromium-review.googlesource.com/925449 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- client/client.gyp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/client.gyp b/client/client.gyp index 4ef14675..ecea124b 100644 --- a/client/client.gyp +++ b/client/client.gyp @@ -35,7 +35,6 @@ 'annotation_list.h', 'crash_report_database.cc', 'crash_report_database.h', - 'crash_report_database_generic.cc', 'crash_report_database_mac.mm', 'crash_report_database_win.cc', 'crashpad_client.h', @@ -66,6 +65,7 @@ ['OS=="linux" or OS=="android"', { 'sources': [ 'crashpad_info_note.S', + 'crash_report_database_generic.cc', ], }], ], From d2a866978b89508f9397266bfe0253755383899f Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 19 Feb 2018 11:11:38 -0800 Subject: [PATCH 180/326] Makes 'all' build on Linux I can never remember which targets are buildable; this makes just ninja -C out/lin work, without too much fuss. I think this means we could turn on trybots too, as I think all the tests that are built also run. Bug: crashpad:30 Change-Id: I4759bb799dabf977c5b072691f28d00bf92bbebc Reviewed-on: https://chromium-review.googlesource.com/924564 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- handler/BUILD.gn | 7 ++----- handler/handler.gyp | 2 +- handler/handler_main.cc | 15 +++++++++++---- tools/generate_dump.cc | 8 ++++++++ 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/handler/BUILD.gn b/handler/BUILD.gn index 7cf4c3d9..c817393b 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -18,6 +18,8 @@ static_library("handler") { sources = [ "crash_report_upload_thread.cc", "crash_report_upload_thread.h", + "handler_main.cc", + "handler_main.h", "minidump_to_upload_parameters.cc", "minidump_to_upload_parameters.h", "prune_crash_reports_thread.cc", @@ -44,11 +46,6 @@ static_library("handler") { "linux/exception_handler_server.cc", "linux/exception_handler_server.h", ] - } else { - sources += [ - "handler_main.cc", - "handler_main.h", - ] } if (crashpad_is_win) { diff --git a/handler/handler.gyp b/handler/handler.gyp index fb5de2a8..f29706cd 100644 --- a/handler/handler.gyp +++ b/handler/handler.gyp @@ -59,7 +59,7 @@ 'win/crash_report_exception_handler.h', ], 'conditions': [ - ['OS=="linux" or OS=="android"', { + ['OS=="android"', { 'sources!': [ 'handler_main.cc', ], diff --git a/handler/handler_main.cc b/handler/handler_main.cc index f175fddf..c5e35bfc 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -77,6 +77,9 @@ #elif defined(OS_FUCHSIA) #include "handler/fuchsia/crash_report_exception_handler.h" #include "handler/fuchsia/exception_handler_server.h" +#elif defined(OS_LINUX) +#include "handler/linux/crash_report_exception_handler.h" +#include "handler/linux/exception_handler_server.h" #endif // OS_MACOSX namespace crashpad { @@ -348,14 +351,18 @@ void InstallCrashHandler() { ALLOW_UNUSED_LOCAL(terminate_handler); } -#elif defined(OS_FUCHSIA) +#elif defined(OS_FUCHSIA) || defined(OS_LINUX) void InstallCrashHandler() { - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + // TODO(scottmg): Fuchsia: https://crashpad.chromium.org/bug/196 + // TODO(jperaza): Linux: https://crashpad.chromium.org/bug/30 + NOTREACHED(); } void ReinstallCrashHandler() { - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + // TODO(scottmg): Fuchsia: https://crashpad.chromium.org/bug/196 + // TODO(jperaza): Linux: https://crashpad.chromium.org/bug/30 + NOTREACHED(); } #endif // OS_MACOSX @@ -740,7 +747,7 @@ int HandlerMain(int argc, if (!options.pipe_name.empty()) { exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name)); } -#elif defined(OS_FUCHSIA) +#elif defined(OS_FUCHSIA) || defined(OS_LINUX) ExceptionHandlerServer exception_handler_server; #endif // OS_MACOSX diff --git a/tools/generate_dump.cc b/tools/generate_dump.cc index a470bfd0..c1fbde84 100644 --- a/tools/generate_dump.cc +++ b/tools/generate_dump.cc @@ -47,6 +47,8 @@ #include "util/win/xp_compat.h" #elif defined(OS_FUCHSIA) #include "snapshot/fuchsia/process_snapshot_fuchsia.h" +#elif defined(OS_LINUX) +#include "snapshot/linux/process_snapshot_linux.h" #endif // OS_MACOSX namespace crashpad { @@ -199,6 +201,12 @@ int GenerateDumpMain(int argc, char* argv[]) { if (!process_snapshot.Initialize(ZX_HANDLE_INVALID)) { return EXIT_FAILURE; } +#elif defined(OS_LINUX) + // TODO(jperaza): https://crashpad.chromium.org/bug/30. + ProcessSnapshotLinux process_snapshot; + if (!process_snapshot.Initialize(nullptr)) { + return EXIT_FAILURE; + } #endif // OS_MACOSX FileWriter file_writer; From d8d03172c2788b0f059fae4ea43ff00e1cbd9cef Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 19 Feb 2018 10:55:17 -0800 Subject: [PATCH 181/326] arm: Capture context around pc and registers Includes mini_chromium DEPS roll of one change: 4e3b2c0 fuchsia: Make target flag apply to asm too After this, the Fuchsia ARM64 build compiles. Bug: crashpad:196 Change-Id: I1b749a2b2443303ad86122fbe5c9750300474d79 Reviewed-on: https://chromium-review.googlesource.com/925454 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- DEPS | 2 +- snapshot/capture_memory.cc | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 4db917a5..bd137b66 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'e7e8237132fd133bd3521ddf6966b800db6a7bca', + '4e3b2c0fd5b18832e4221876941111cb12892d3b', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/snapshot/capture_memory.cc b/snapshot/capture_memory.cc index c860285a..4327fbda 100644 --- a/snapshot/capture_memory.cc +++ b/snapshot/capture_memory.cc @@ -94,8 +94,20 @@ void CaptureMemory::PointedToByContext(const CPUContext& context, MaybeCaptureMemoryAround(delegate, context.x86->ebp); MaybeCaptureMemoryAround(delegate, context.x86->eip); } +#elif defined(ARCH_CPU_ARM_FAMILY) + if (context.architecture == kCPUArchitectureARM64) { + MaybeCaptureMemoryAround(delegate, context.arm64->pc); + for (size_t i = 0; i < arraysize(context.arm64->regs); ++i) { + MaybeCaptureMemoryAround(delegate, context.arm64->regs[i]); + } + } else { + MaybeCaptureMemoryAround(delegate, context.arm->pc); + for (size_t i = 0; i < arraysize(context.arm->regs); ++i) { + MaybeCaptureMemoryAround(delegate, context.arm->regs[i]); + } + } #else -#error non-x86 +#error Port. #endif } From ebad8bd925c36721afc4a02c55fb9ddf868a5a68 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 20 Feb 2018 10:55:17 -0800 Subject: [PATCH 182/326] Don't spawn an upload thread if url is empty Also automatically stop upload and prune threads on destruction. Bug: crashpad:30 Change-Id: I45a30944eb3052182da296e00a6d6041691ab772 Reviewed-on: https://chromium-review.googlesource.com/924456 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- handler/crash_report_upload_thread.cc | 19 ++--- handler/crash_report_upload_thread.h | 28 ++++--- handler/handler_main.cc | 73 ++++++++++++------- handler/mac/crash_report_exception_handler.cc | 7 +- handler/mac/crash_report_exception_handler.h | 3 +- handler/prune_crash_reports_thread.h | 9 ++- handler/win/crash_report_exception_handler.cc | 7 +- handler/win/crash_report_exception_handler.h | 3 +- util/BUILD.gn | 1 + util/thread/stoppable.h | 39 ++++++++++ util/util.gyp | 1 + 11 files changed, 135 insertions(+), 55 deletions(-) create mode 100644 util/thread/stoppable.h diff --git a/handler/crash_report_upload_thread.cc b/handler/crash_report_upload_thread.cc index 290c5a3f..715c533a 100644 --- a/handler/crash_report_upload_thread.cc +++ b/handler/crash_report_upload_thread.cc @@ -58,11 +58,18 @@ CrashReportUploadThread::CrashReportUploadThread(CrashReportDatabase* database, : WorkerThread::kIndefiniteWait, this), known_pending_report_uuids_(), - database_(database) {} + database_(database) { + DCHECK(!url_.empty()); +} CrashReportUploadThread::~CrashReportUploadThread() { } +void CrashReportUploadThread::ReportPending(const UUID& report_uuid) { + known_pending_report_uuids_.PushBack(report_uuid); + thread_.DoWorkNow(); +} + void CrashReportUploadThread::Start() { thread_.Start( options_.watch_pending_reports ? 0.0 : WorkerThread::kIndefiniteWait); @@ -72,11 +79,6 @@ void CrashReportUploadThread::Stop() { thread_.Stop(); } -void CrashReportUploadThread::ReportPending(const UUID& report_uuid) { - known_pending_report_uuids_.PushBack(report_uuid); - thread_.DoWorkNow(); -} - void CrashReportUploadThread::ProcessPendingReports() { std::vector known_report_uuids = known_pending_report_uuids_.Drain(); for (const UUID& report_uuid : known_report_uuids) { @@ -140,9 +142,8 @@ void CrashReportUploadThread::ProcessPendingReport( Settings* const settings = database_->GetSettings(); bool uploads_enabled; - if (url_.empty() || - (!report.upload_explicitly_requested && - (!settings->GetUploadsEnabled(&uploads_enabled) || !uploads_enabled))) { + if (!report.upload_explicitly_requested && + (!settings->GetUploadsEnabled(&uploads_enabled) || !uploads_enabled)) { // Don’t attempt an upload if there’s no URL to upload to. Allow upload if // it has been explicitly requested by the user, otherwise, respect the // upload-enabled state stored in the database’s settings. diff --git a/handler/crash_report_upload_thread.h b/handler/crash_report_upload_thread.h index 69e7a3c2..2ec1147d 100644 --- a/handler/crash_report_upload_thread.h +++ b/handler/crash_report_upload_thread.h @@ -22,6 +22,7 @@ #include "client/crash_report_database.h" #include "util/misc/uuid.h" #include "util/stdlib/thread_safe_vector.h" +#include "util/thread/stoppable.h" #include "util/thread/worker_thread.h" namespace crashpad { @@ -39,7 +40,8 @@ namespace crashpad { //! It also catches reports that are added without a ReportPending() signal //! being caught. This may happen if crash reports are added to the database by //! other processes. -class CrashReportUploadThread : public WorkerThread::Delegate { +class CrashReportUploadThread : public WorkerThread::Delegate, + public Stoppable { public: //! \brief Options to be passed to the CrashReportUploadThread constructor. struct Options { @@ -70,11 +72,22 @@ class CrashReportUploadThread : public WorkerThread::Delegate { const Options& options); ~CrashReportUploadThread(); + //! \brief Informs the upload thread that a new pending report has been added + //! to the database. + //! + //! \param[in] report_uuid The unique identifier of the newly added pending + //! report. + //! + //! This method may be called from any thread. + void ReportPending(const UUID& report_uuid); + + // Stoppable: + //! \brief Starts a dedicated upload thread, which executes ThreadMain(). //! //! This method may only be be called on a newly-constructed object or after //! a call to Stop(). - void Start(); + void Start() override; //! \brief Stops the upload thread. //! @@ -88,16 +101,7 @@ class CrashReportUploadThread : public WorkerThread::Delegate { //! //! This method may be called from any thread other than the upload thread. //! It is expected to only be called from the same thread that called Start(). - void Stop(); - - //! \brief Informs the upload thread that a new pending report has been added - //! to the database. - //! - //! \param[in] report_uuid The unique identifier of the newly added pending - //! report. - //! - //! This method may be called from any thread. - void ReportPending(const UUID& report_uuid); + void Stop() override; private: //! \brief The result code from UploadReport(). diff --git a/handler/handler_main.cc b/handler/handler_main.cc index c5e35bfc..451214a4 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -417,6 +417,26 @@ void MonitorSelf(const Options& options) { ReinstallCrashHandler(); } +class ScopedStoppable { + public: + ScopedStoppable() = default; + + ~ScopedStoppable() { + if (stoppable_) { + stoppable_->Stop(); + } + } + + void Reset(Stoppable* stoppable) { stoppable_.reset(stoppable); } + + Stoppable* Get() { return stoppable_.get(); } + + private: + std::unique_ptr stoppable_; + + DISALLOW_COPY_AND_ASSIGN(ScopedStoppable); +}; + } // namespace int HandlerMain(int argc, @@ -770,31 +790,35 @@ int HandlerMain(int argc, return ExitFailure(); } - // TODO(scottmg): options.rate_limit should be removed when we have a - // configurable database setting to control upload limiting. - // See https://crashpad.chromium.org/bug/23. - CrashReportUploadThread::Options upload_thread_options; - upload_thread_options.identify_client_via_url = - options.identify_client_via_url; - upload_thread_options.rate_limit = options.rate_limit; - upload_thread_options.upload_gzip = options.upload_gzip; - upload_thread_options.watch_pending_reports = options.periodic_tasks; - CrashReportUploadThread upload_thread(database.get(), - options.url, - upload_thread_options); - upload_thread.Start(); + ScopedStoppable upload_thread; + if (!options.url.empty()) { + // TODO(scottmg): options.rate_limit should be removed when we have a + // configurable database setting to control upload limiting. + // See https://crashpad.chromium.org/bug/23. + CrashReportUploadThread::Options upload_thread_options; + upload_thread_options.identify_client_via_url = + options.identify_client_via_url; + upload_thread_options.rate_limit = options.rate_limit; + upload_thread_options.upload_gzip = options.upload_gzip; + upload_thread_options.watch_pending_reports = options.periodic_tasks; - std::unique_ptr prune_thread; - if (options.periodic_tasks) { - prune_thread.reset(new PruneCrashReportThread( - database.get(), PruneCondition::GetDefault())); - prune_thread->Start(); + upload_thread.Reset(new CrashReportUploadThread( + database.get(), options.url, upload_thread_options)); + upload_thread.Get()->Start(); } - CrashReportExceptionHandler exception_handler(database.get(), - &upload_thread, - &options.annotations, - user_stream_sources); + ScopedStoppable prune_thread; + if (options.periodic_tasks) { + prune_thread.Reset(new PruneCrashReportThread( + database.get(), PruneCondition::GetDefault())); + prune_thread.Get()->Start(); + } + + CrashReportExceptionHandler exception_handler( + database.get(), + static_cast(upload_thread.Get()), + &options.annotations, + user_stream_sources); #if defined(OS_WIN) if (options.initial_client_data.IsValid()) { @@ -805,11 +829,6 @@ int HandlerMain(int argc, exception_handler_server.Run(&exception_handler); - upload_thread.Stop(); - if (prune_thread) { - prune_thread->Stop(); - } - return EXIT_SUCCESS; } diff --git a/handler/mac/crash_report_exception_handler.cc b/handler/mac/crash_report_exception_handler.cc index 2317df24..edebf87a 100644 --- a/handler/mac/crash_report_exception_handler.cc +++ b/handler/mac/crash_report_exception_handler.cc @@ -187,7 +187,12 @@ kern_return_t CrashReportExceptionHandler::CatchMachException( return KERN_FAILURE; } - upload_thread_->ReportPending(uuid); + if (upload_thread_) { + upload_thread_->ReportPending(uuid); + } else { + database_->SkipReportUpload( + uuid, Metrics::CrashSkippedReason::kUploadsDisabled); + } } if (client_options.system_crash_reporter_forwarding != TriState::kDisabled && diff --git a/handler/mac/crash_report_exception_handler.h b/handler/mac/crash_report_exception_handler.h index cc314f1f..0b44de67 100644 --- a/handler/mac/crash_report_exception_handler.h +++ b/handler/mac/crash_report_exception_handler.h @@ -36,7 +36,8 @@ class CrashReportExceptionHandler : public UniversalMachExcServer::Interface { //! //! \param[in] database The database to store crash reports in. Weak. //! \param[in] upload_thread The upload thread to notify when a new crash - //! report is written into \a database. + //! report is written into \a database. Report upload is skipped if this + //! value is `nullptr`. //! \param[in] process_annotations A map of annotations to insert as //! process-level annotations into each crash report that is written. Do //! not confuse this with module-level annotations, which are under the diff --git a/handler/prune_crash_reports_thread.h b/handler/prune_crash_reports_thread.h index 72b69fc6..0fd365b0 100644 --- a/handler/prune_crash_reports_thread.h +++ b/handler/prune_crash_reports_thread.h @@ -18,6 +18,7 @@ #include #include "base/macros.h" +#include "util/thread/stoppable.h" #include "util/thread/worker_thread.h" namespace crashpad { @@ -31,7 +32,7 @@ class PruneCondition; //! After the thread is started, the database is pruned using the condition //! every 24 hours. Upon calling Start(), the thread waits 10 minutes before //! performing the initial prune operation. -class PruneCrashReportThread : public WorkerThread::Delegate { +class PruneCrashReportThread : public WorkerThread::Delegate, public Stoppable { public: //! \brief Constructs a new object. //! @@ -42,6 +43,8 @@ class PruneCrashReportThread : public WorkerThread::Delegate { std::unique_ptr condition); ~PruneCrashReportThread(); + // Stoppable: + //! \brief Starts a dedicated pruning thread. //! //! The thread waits before running the initial prune, so as to not interfere @@ -49,7 +52,7 @@ class PruneCrashReportThread : public WorkerThread::Delegate { //! //! This method may only be be called on a newly-constructed object or after //! a call to Stop(). - void Start(); + void Start() override; //! \brief Stops the pruning thread. //! @@ -58,7 +61,7 @@ class PruneCrashReportThread : public WorkerThread::Delegate { //! //! This method may be called from any thread other than the pruning thread. //! It is expected to only be called from the same thread that called Start(). - void Stop(); + void Stop() override; private: // WorkerThread::Delegate: diff --git a/handler/win/crash_report_exception_handler.cc b/handler/win/crash_report_exception_handler.cc index 726dcb39..6d53d810 100644 --- a/handler/win/crash_report_exception_handler.cc +++ b/handler/win/crash_report_exception_handler.cc @@ -124,7 +124,12 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException( return termination_code; } - upload_thread_->ReportPending(uuid); + if (upload_thread_) { + upload_thread_->ReportPending(uuid); + } else { + database_->SkipReportUpload( + uuid, Metrics::CrashSkippedReason::kUploadsDisabled); + } } Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess); diff --git a/handler/win/crash_report_exception_handler.h b/handler/win/crash_report_exception_handler.h index e1fb725d..c2781de3 100644 --- a/handler/win/crash_report_exception_handler.h +++ b/handler/win/crash_report_exception_handler.h @@ -37,7 +37,8 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { //! //! \param[in] database The database to store crash reports in. Weak. //! \param[in] upload_thread The upload thread to notify when a new crash - //! report is written into \a database. + //! report is written into \a database. Report upload is skipped if this + //! value is `nullptr`. //! \param[in] process_annotations A map of annotations to insert as //! process-level annotations into each crash report that is written. Do //! not confuse this with module-level annotations, which are under the diff --git a/util/BUILD.gn b/util/BUILD.gn index e39212cc..0cdaef82 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -144,6 +144,7 @@ static_library("util") { "string/split_string.cc", "string/split_string.h", "synchronization/semaphore.h", + "thread/stoppable.h", "thread/thread.cc", "thread/thread.h", "thread/thread_log_messages.cc", diff --git a/util/thread/stoppable.h b/util/thread/stoppable.h new file mode 100644 index 00000000..e7a51277 --- /dev/null +++ b/util/thread/stoppable.h @@ -0,0 +1,39 @@ +// Copyright 2018 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_UTIL_THREAD_STOPPABLE_H_ +#define CRASHPAD_UTIL_THREAD_STOPPABLE_H_ + +#include "base/macros.h" + +namespace crashpad { + +//! \brief An interface for operations that may be Started and Stopped. +class Stoppable { + public: + virtual ~Stoppable() = default; + + //! \brief Starts the operation. + virtual void Start() = 0; + + //! \brief Stops the operation. + virtual void Stop() = 0; + + protected: + Stoppable() = default; +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_THREAD_STOPPABLE_H_ diff --git a/util/util.gyp b/util/util.gyp index 15925444..58cc27ec 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -228,6 +228,7 @@ 'synchronization/semaphore_posix.cc', 'synchronization/semaphore_win.cc', 'synchronization/semaphore.h', + 'thread/stoppable.h', 'thread/thread.cc', 'thread/thread.h', 'thread/thread_log_messages.cc', From 0520fdff1edc07c715b378c25a0230f11d507029 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 20 Feb 2018 13:07:16 -0800 Subject: [PATCH 183/326] linux: Move ScopedPrSetPtracer to util/ CrashpadClient will need ScopedPrSetPtracer when launching a handler process in response to a crash. Bug: crashpad:30 Change-Id: I35bc784b948349ca771f9cd65ef1089e626976bb Reviewed-on: https://chromium-review.googlesource.com/927352 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- .../linux/exception_handler_server_test.cc | 4 ++-- test/BUILD.gn | 2 -- test/test.gyp | 2 -- util/BUILD.gn | 2 ++ {test => util}/linux/scoped_pr_set_ptracer.cc | 17 +++++++--------- {test => util}/linux/scoped_pr_set_ptracer.h | 20 +++++++++++-------- util/linux/scoped_ptrace_attach_test.cc | 8 ++++---- util/util.gyp | 2 ++ 8 files changed, 29 insertions(+), 28 deletions(-) rename {test => util}/linux/scoped_pr_set_ptracer.cc (70%) rename {test => util}/linux/scoped_pr_set_ptracer.h (69%) diff --git a/handler/linux/exception_handler_server_test.cc b/handler/linux/exception_handler_server_test.cc index fb6c21a9..5b0c8cea 100644 --- a/handler/linux/exception_handler_server_test.cc +++ b/handler/linux/exception_handler_server_test.cc @@ -20,11 +20,11 @@ #include "base/logging.h" #include "gtest/gtest.h" #include "test/errors.h" -#include "test/linux/scoped_pr_set_ptracer.h" #include "test/multiprocess.h" #include "util/linux/direct_ptrace_connection.h" #include "util/linux/exception_handler_client.h" #include "util/linux/ptrace_client.h" +#include "util/linux/scoped_pr_set_ptracer.h" #include "util/synchronization/semaphore.h" #include "util/thread/thread.h" @@ -205,7 +205,7 @@ class ExceptionHandlerServerTest : public testing::Test { // If the current ptrace_scope is restricted, the broker needs to be set // as the ptracer for this process. Setting this process as its own // ptracer allows the broker to inherit this condition. - ScopedPrSetPtracer set_ptracer(getpid()); + ScopedPrSetPtracer set_ptracer(getpid(), /* may_log= */ true); ExceptionHandlerClient client(server_test_->SockToHandler()); ASSERT_EQ(client.RequestCrashDump(info), 0); diff --git a/test/BUILD.gn b/test/BUILD.gn index 0ddde6e1..0808096e 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -74,8 +74,6 @@ static_library("test") { "linux/fake_ptrace_connection.h", "linux/get_tls.cc", "linux/get_tls.h", - "linux/scoped_pr_set_ptracer.cc", - "linux/scoped_pr_set_ptracer.h", ] } diff --git a/test/test.gyp b/test/test.gyp index a3721efd..29737088 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -45,8 +45,6 @@ 'linux/fake_ptrace_connection.h', 'linux/get_tls.cc', 'linux/get_tls.h', - 'linux/scoped_pr_set_ptracer.cc', - 'linux/scoped_pr_set_ptracer.h', 'mac/dyld.cc', 'mac/dyld.h', 'mac/exception_swallower.cc', diff --git a/util/BUILD.gn b/util/BUILD.gn index 0cdaef82..342f0f63 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -271,6 +271,8 @@ static_library("util") { "linux/ptrace_connection.h", "linux/ptracer.cc", "linux/ptracer.h", + "linux/scoped_pr_set_ptracer.cc", + "linux/scoped_pr_set_ptracer.h", "linux/scoped_ptrace_attach.cc", "linux/scoped_ptrace_attach.h", "linux/thread_info.cc", diff --git a/test/linux/scoped_pr_set_ptracer.cc b/util/linux/scoped_pr_set_ptracer.cc similarity index 70% rename from test/linux/scoped_pr_set_ptracer.cc rename to util/linux/scoped_pr_set_ptracer.cc index bc9695ae..c7aeefc6 100644 --- a/test/linux/scoped_pr_set_ptracer.cc +++ b/util/linux/scoped_pr_set_ptracer.cc @@ -12,29 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "test/linux/scoped_pr_set_ptracer.h" +#include "util/linux/scoped_pr_set_ptracer.h" #include #include -#include "gtest/gtest.h" -#include "test/errors.h" +#include "base/logging.h" namespace crashpad { -namespace test { -ScopedPrSetPtracer::ScopedPrSetPtracer(pid_t pid) { +ScopedPrSetPtracer::ScopedPrSetPtracer(pid_t pid, bool may_log) + : success_(false), may_log_(may_log) { success_ = prctl(PR_SET_PTRACER, pid, 0, 0, 0) == 0; - if (!success_) { - EXPECT_EQ(errno, EINVAL) << ErrnoMessage("prctl"); - } + PLOG_IF(ERROR, !success_ && may_log && errno != EINVAL) << "prctl"; } ScopedPrSetPtracer::~ScopedPrSetPtracer() { if (success_) { - EXPECT_EQ(prctl(PR_SET_PTRACER, 0, 0, 0, 0), 0) << ErrnoMessage("prctl"); + int res = prctl(PR_SET_PTRACER, 0, 0, 0, 0); + PLOG_IF(ERROR, res != 0 && may_log_) << "prctl"; } } -} // namespace test } // namespace crashpad diff --git a/test/linux/scoped_pr_set_ptracer.h b/util/linux/scoped_pr_set_ptracer.h similarity index 69% rename from test/linux/scoped_pr_set_ptracer.h rename to util/linux/scoped_pr_set_ptracer.h index df9cff78..2bc8677b 100644 --- a/test/linux/scoped_pr_set_ptracer.h +++ b/util/linux/scoped_pr_set_ptracer.h @@ -12,36 +12,40 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CRASHPAD_TEST_LINUX_SCOPED_PR_SET_PTRACER_H_ -#define CRASHPAD_TEST_LINUX_SCOPED_PR_SET_PTRACER_H_ +#ifndef CRASHPAD_UTIL_LINUX_SCOPED_PR_SET_PTRACER_H_ +#define CRASHPAD_UTIL_LINUX_SCOPED_PR_SET_PTRACER_H_ #include #include "base/macros.h" namespace crashpad { -namespace test { class ScopedPrSetPtracer { public: - //! \brief Uses `PR_SET_PTRACER` to set \a pid as the caller's ptracer or - //! expects `EINVAL`. + //! \brief Uses `PR_SET_PTRACER` to set \a pid as the caller's ptracer. //! //! `PR_SET_PTRACER` is only supported if the Yama Linux security module (LSM) //! is enabled. Otherwise, `prctl(PR_SET_PTRACER, ...)` fails with `EINVAL`. //! See linux-4.9.20/security/yama/yama_lsm.c yama_task_prctl() and //! linux-4.9.20/kernel/sys.c [sys_]prctl(). - explicit ScopedPrSetPtracer(pid_t pid); + //! + //! An error message will be logged on failure only if \a may_log is `true` + //! and `prctl` does not fail with `EINVAL`; + //! + //! \param[in] pid The process ID of the process to make the caller's ptracer. + //! \param[in] may_log if `true`, this class may log error messages. + ScopedPrSetPtracer(pid_t pid, bool may_log); ~ScopedPrSetPtracer(); private: bool success_; + bool may_log_; DISALLOW_COPY_AND_ASSIGN(ScopedPrSetPtracer); }; -} // namespace test } // namespace crashpad -#endif // CRASHPAD_TEST_LINUX_SCOPED_PR_SET_PTRACER_H_ +#endif // CRASHPAD_UTIL_LINUX_SCOPED_PR_SET_PTRACER_H_ diff --git a/util/linux/scoped_ptrace_attach_test.cc b/util/linux/scoped_ptrace_attach_test.cc index 78552e77..d009e682 100644 --- a/util/linux/scoped_ptrace_attach_test.cc +++ b/util/linux/scoped_ptrace_attach_test.cc @@ -20,9 +20,9 @@ #include "gtest/gtest.h" #include "test/errors.h" -#include "test/linux/scoped_pr_set_ptracer.h" #include "test/multiprocess.h" #include "util/file/file_io.h" +#include "util/linux/scoped_pr_set_ptracer.h" namespace crashpad { namespace test { @@ -75,7 +75,7 @@ class AttachToChildTest : public AttachTest { } void MultiprocessChild() override { - ScopedPrSetPtracer set_ptracer(getppid()); + ScopedPrSetPtracer set_ptracer(getppid(), /* may_log= */ true); char c = '\0'; CheckedWriteFile(WritePipeHandle(), &c, sizeof(c)); @@ -98,7 +98,7 @@ class AttachToParentResetTest : public AttachTest { private: void MultiprocessParent() override { - ScopedPrSetPtracer set_ptracer(ChildPID()); + ScopedPrSetPtracer set_ptracer(ChildPID(), /* may_log= */ true); char c = '\0'; CheckedWriteFile(WritePipeHandle(), &c, sizeof(c)); @@ -140,7 +140,7 @@ class AttachToParentDestructorTest : public AttachTest { private: void MultiprocessParent() override { - ScopedPrSetPtracer set_ptracer(ChildPID()); + ScopedPrSetPtracer set_ptracer(ChildPID(), /* may_log= */ true); char c = '\0'; CheckedWriteFile(WritePipeHandle(), &c, sizeof(c)); diff --git a/util/util.gyp b/util/util.gyp index 58cc27ec..e0394e2a 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -73,6 +73,8 @@ 'linux/ptrace_connection.h', 'linux/ptracer.cc', 'linux/ptracer.h', + 'linux/scoped_pr_set_ptracer.cc', + 'linux/scoped_pr_set_ptracer.h', 'linux/scoped_ptrace_attach.cc', 'linux/scoped_ptrace_attach.h', 'linux/thread_info.cc', From 38540eaf71cb997779a9c79cdca6b6bc59ea8195 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 20 Feb 2018 15:03:14 -0800 Subject: [PATCH 184/326] Add handler options for Linux/Android Add the options: --trace-parent-with-exception=
which traces the handler's parent process which has an ExceptionInformation struct at
. --initial-client-fd= which starts the handler server with an already connected client on socket . Bug: crashpad:30 Change-Id: Ied9760ca125a16f56173afdc56dff5fcb79d2eea Reviewed-on: https://chromium-review.googlesource.com/922895 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- handler/crashpad_handler.md | 19 ++++ handler/handler.gyp | 7 -- handler/handler_main.cc | 184 ++++++++++++++++++++++++++++-------- 3 files changed, 163 insertions(+), 47 deletions(-) diff --git a/handler/crashpad_handler.md b/handler/crashpad_handler.md index 5eff39ae..003ee2ef 100644 --- a/handler/crashpad_handler.md +++ b/handler/crashpad_handler.md @@ -73,6 +73,13 @@ when run normally from a shell using only the basename (without an explicit stdio will be hooked up as expected to the parent console so that logging output will be visible. +On Linux/Android, the handler may create a crash dump for its parent process +using **--trace-parent-with-exception**. In this mode, the handler process +creates a crash dump for its parent and exits. Alternatively, the handler may +be launched with **--initial-client-fd** which will start the server connected +to an initial client. The server will exit when all connected client sockets are +closed. + It is not normally appropriate to invoke this program directly. Usually, it will be invoked by a Crashpad client using the Crashpad client library, or started by another system service. On macOS, arbitrary programs may be run with a Crashpad @@ -238,6 +245,18 @@ establish the Crashpad client environment before running a program. parent process. This option is only valid on macOS. Use of this option is discouraged. It should not be used absent extraordinary circumstances. + * **--trace-parent-with-exception**=_EXCEPTION-INFORMATION-ADDRESS_ + + Causes the handler process to trace its parent process and exit. The parent + process should have an ExceptionInformation struct at + _EXCEPTION-INFORMATION-ADDRESS_. + + * **--initial-client-fd**=_FD_ + + Starts the excetion handler server with an initial ExceptionHandlerClient + connected on the socket _FD_. The server will exit when all connected client + sockets have been closed. + * **--url**=_URL_ If uploads are enabled, sends crash reports to the Breakpad-type crash report diff --git a/handler/handler.gyp b/handler/handler.gyp index f29706cd..60a6f251 100644 --- a/handler/handler.gyp +++ b/handler/handler.gyp @@ -58,13 +58,6 @@ 'win/crash_report_exception_handler.cc', 'win/crash_report_exception_handler.h', ], - 'conditions': [ - ['OS=="android"', { - 'sources!': [ - 'handler_main.cc', - ], - }], - ], 'target_conditions': [ ['OS=="android"', { 'sources/': [ diff --git a/handler/handler_main.cc b/handler/handler_main.cc index 451214a4..dd7adf65 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -34,6 +34,7 @@ #include "base/logging.h" #include "base/metrics/persistent_histogram_allocator.h" #include "base/scoped_generic.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" @@ -46,6 +47,7 @@ #include "handler/prune_crash_reports_thread.h" #include "tools/tool_support.h" #include "util/file/file_io.h" +#include "util/misc/address_types.h" #include "util/misc/metrics.h" #include "util/misc/paths.h" #include "util/numeric/in_range_cast.h" @@ -54,7 +56,13 @@ #include "util/string/split_string.h" #include "util/synchronization/semaphore.h" -#if defined(OS_MACOSX) +#if defined(OS_LINUX) || defined(OS_ANDROID) +#include + +#include "handler/linux/crash_report_exception_handler.h" +#include "handler/linux/exception_handler_server.h" +#include "util/posix/signals.h" +#elif defined(OS_MACOSX) #include #include @@ -129,6 +137,11 @@ void Usage(const base::FilePath& me) { " --reset-own-crash-exception-port-to-system-default\n" " reset the server's exception handler to default\n" #endif // OS_MACOSX +#if defined(OS_LINUX) || defined(OS_ANDROID) +" --trace-parent-with-exception=EXCEPTION_INFORMATION_ADDRESS\n" +" request a dump for the handler's parent process\n" +" --initial-client-fd=FD a socket connected to a client.\n" +#endif // OS_LINUX || OS_ANDROID " --url=URL send crash reports to this Breakpad server URL,\n" " only if uploads are enabled for the database\n" " --help display this help and exit\n" @@ -148,6 +161,9 @@ struct Options { std::string mach_service; int handshake_fd; bool reset_own_crash_exception_port_to_system_default; +#elif defined(OS_LINUX) || defined(OS_ANDROID) + VMAddress exception_information_address; + int initial_client_fd; #elif defined(OS_WIN) std::string pipe_name; InitialClientData initial_client_data; @@ -214,7 +230,9 @@ class CallMetricsRecordNormalExit { DISALLOW_COPY_AND_ASSIGN(CallMetricsRecordNormalExit); }; -#if defined(OS_MACOSX) +#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_ANDROID) + +Signals::OldActions g_old_crash_signal_handlers; void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) { MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed); @@ -250,7 +268,9 @@ void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) { } Metrics::HandlerCrashed(metrics_code); - Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr); + struct sigaction* old_action = + g_old_crash_signal_handlers.ActionForSignal(sig); + Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, old_action); } void HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) { @@ -258,6 +278,8 @@ void HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) { Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr); } +#if defined(OS_MACOSX) + void ReinstallCrashHandler() { // This is used to re-enable the metrics-recording crash handler after // MonitorSelf() sets up a Crashpad exception handler. On macOS, the @@ -296,6 +318,23 @@ void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) { g_exception_handler_server->Stop(); } +#else + +void ReinstallCrashHandler() { + // This is used to re-enable the metrics-recording crash handler after + // MonitorSelf() sets up a Crashpad signal handler. + Signals::InstallCrashHandlers( + HandleCrashSignal, 0, &g_old_crash_signal_handlers); +} + +void InstallCrashHandler() { + ReinstallCrashHandler(); + + Signals::InstallTerminateHandlers(HandleTerminateSignal, 0, nullptr); +} + +#endif // OS_MACOSX + #elif defined(OS_WIN) LONG(WINAPI* g_original_exception_filter)(EXCEPTION_POINTERS*) = nullptr; @@ -401,6 +440,16 @@ void MonitorSelf(const Options& options) { // instance of crashpad_handler to be writing metrics at a time, and it should // be the primary instance. CrashpadClient crashpad_client; +#if defined(OS_LINUX) || defined(OS_ANDROID) + if (!crashpad_client.StartHandlerAtCrash(executable_path, + options.database, + base::FilePath(), + options.url, + options.annotations, + extra_arguments)) { + return; + } +#else if (!crashpad_client.StartHandler(executable_path, options.database, base::FilePath(), @@ -411,6 +460,7 @@ void MonitorSelf(const Options& options) { false)) { return; } +#endif // Make sure that appropriate metrics will be recorded on crash before this // process is terminated. @@ -477,6 +527,10 @@ int HandlerMain(int argc, #if defined(OS_MACOSX) kOptionResetOwnCrashExceptionPortToSystemDefault, #endif // OS_MACOSX +#if defined(OS_LINUX) || defined(OS_ANDROID) + kOptionTraceParentWithException, + kOptionInitialClientFD, +#endif kOptionURL, // Standard options. @@ -525,6 +579,13 @@ int HandlerMain(int argc, nullptr, kOptionResetOwnCrashExceptionPortToSystemDefault}, #endif // OS_MACOSX +#if defined(OS_LINUX) || defined(OS_ANDROID) + {"trace-parent-with-exception", + required_argument, + nullptr, + kOptionTraceParentWithException}, + {"initial-client-fd", required_argument, nullptr, kOptionInitialClientFD}, +#endif // OS_LINUX || OS_ANDROID {"url", required_argument, nullptr, kOptionURL}, {"help", no_argument, nullptr, kOptionHelp}, {"version", no_argument, nullptr, kOptionVersion}, @@ -539,6 +600,10 @@ int HandlerMain(int argc, options.periodic_tasks = true; options.rate_limit = true; options.upload_gzip = true; +#if defined(OS_LINUX) || defined(OS_ANDROID) + options.exception_information_address = 0; + options.initial_client_fd = kInvalidFileHandle; +#endif int opt; while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) { @@ -628,6 +693,23 @@ int HandlerMain(int argc, break; } #endif // OS_MACOSX +#if defined(OS_LINUX) || defined(OS_ANDROID) + case kOptionTraceParentWithException: { + if (!StringToNumber(optarg, &options.exception_information_address)) { + ToolSupport::UsageHint( + me, "failed to parse --trace-parent-with-exception"); + return ExitFailure(); + } + break; + } + case kOptionInitialClientFD: { + if (!base::StringToInt(optarg, &options.initial_client_fd)) { + ToolSupport::UsageHint(me, "failed to parse --initial-client-fd"); + return ExitFailure(); + } + break; + } +#endif // OS_LINUX || OS_ANDROID case kOptionURL: { options.url = optarg; break; @@ -672,6 +754,14 @@ int HandlerMain(int argc, me, "--initial-client-data and --pipe-name are incompatible"); return ExitFailure(); } +#elif defined(OS_LINUX) || defined(OS_ANDROID) + if (!options.exception_information_address && + options.initial_client_fd == kInvalidFileHandle) { + ToolSupport::UsageHint( + me, + "--exception_information_address or --initial_client_fd is required"); + return ExitFailure(); + } #endif // OS_MACOSX if (options.database.empty()) { @@ -714,6 +804,50 @@ int HandlerMain(int argc, } } + std::unique_ptr database( + CrashReportDatabase::Initialize(options.database)); + if (!database) { + return ExitFailure(); + } + + ScopedStoppable upload_thread; + if (!options.url.empty()) { + // TODO(scottmg): options.rate_limit should be removed when we have a + // configurable database setting to control upload limiting. + // See https://crashpad.chromium.org/bug/23. + CrashReportUploadThread::Options upload_thread_options; + upload_thread_options.identify_client_via_url = + options.identify_client_via_url; + upload_thread_options.rate_limit = options.rate_limit; + upload_thread_options.upload_gzip = options.upload_gzip; + upload_thread_options.watch_pending_reports = options.periodic_tasks; + + upload_thread.Reset(new CrashReportUploadThread( + database.get(), options.url, upload_thread_options)); + upload_thread.Get()->Start(); + } + + CrashReportExceptionHandler exception_handler( + database.get(), + static_cast(upload_thread.Get()), + &options.annotations, + user_stream_sources); + + #if defined(OS_LINUX) || defined(OS_ANDROID) + if (options.exception_information_address) { + return exception_handler.HandleException(getppid(), + options.exception_information_address) ? + EXIT_SUCCESS : ExitFailure(); + } +#endif // OS_LINUX || OS_ANDROID + + ScopedStoppable prune_thread; + if (options.periodic_tasks) { + prune_thread.Reset(new PruneCrashReportThread( + database.get(), PruneCondition::GetDefault())); + prune_thread.Get()->Start(); + } + #if defined(OS_MACOSX) if (options.mach_service.empty()) { // Don’t do this when being run by launchd. See launchd.plist(5). @@ -767,7 +901,7 @@ int HandlerMain(int argc, if (!options.pipe_name.empty()) { exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name)); } -#elif defined(OS_FUCHSIA) || defined(OS_LINUX) +#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) ExceptionHandlerServer exception_handler_server; #endif // OS_MACOSX @@ -784,47 +918,17 @@ int HandlerMain(int argc, Metrics::HandlerLifetimeMilestone(Metrics::LifetimeMilestone::kStarted); - std::unique_ptr database( - CrashReportDatabase::Initialize(options.database)); - if (!database) { - return ExitFailure(); - } - - ScopedStoppable upload_thread; - if (!options.url.empty()) { - // TODO(scottmg): options.rate_limit should be removed when we have a - // configurable database setting to control upload limiting. - // See https://crashpad.chromium.org/bug/23. - CrashReportUploadThread::Options upload_thread_options; - upload_thread_options.identify_client_via_url = - options.identify_client_via_url; - upload_thread_options.rate_limit = options.rate_limit; - upload_thread_options.upload_gzip = options.upload_gzip; - upload_thread_options.watch_pending_reports = options.periodic_tasks; - - upload_thread.Reset(new CrashReportUploadThread( - database.get(), options.url, upload_thread_options)); - upload_thread.Get()->Start(); - } - - ScopedStoppable prune_thread; - if (options.periodic_tasks) { - prune_thread.Reset(new PruneCrashReportThread( - database.get(), PruneCondition::GetDefault())); - prune_thread.Get()->Start(); - } - - CrashReportExceptionHandler exception_handler( - database.get(), - static_cast(upload_thread.Get()), - &options.annotations, - user_stream_sources); - #if defined(OS_WIN) if (options.initial_client_data.IsValid()) { exception_handler_server.InitializeWithInheritedDataForInitialClient( options.initial_client_data, &exception_handler); } +#elif defined(OS_LINUX) || defined(OS_ANDROID) + if (options.initial_client_fd == kInvalidFileHandle || + !exception_handler_server.InitializeWithClient( + ScopedFileHandle(options.initial_client_fd))) { + return ExitFailure(); + } #endif // OS_WIN exception_handler_server.Run(&exception_handler); From 01105719d767d49ccb94cc415ef6da3db8229915 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 20 Feb 2018 16:16:22 -0800 Subject: [PATCH 185/326] linux: add CRASHPAD_SIMULATE_CRASH() Bug: crashpad:30 Change-Id: I135864a0e31119de3a814ee5ab5729336f6284a3 Reviewed-on: https://chromium-review.googlesource.com/927116 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- client/BUILD.gn | 5 +- client/client.gyp | 2 + client/crashpad_client.h | 12 ++++ client/crashpad_client_linux.cc | 114 +++++++++++++++++++++++++------- client/simulate_crash.h | 2 + client/simulate_crash_linux.h | 31 +++++++++ util/misc/capture_context.h | 6 ++ util/posix/signals.h | 3 + 8 files changed, 150 insertions(+), 25 deletions(-) create mode 100644 client/simulate_crash_linux.h diff --git a/client/BUILD.gn b/client/BUILD.gn index a9262b96..9a2f66ea 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -44,7 +44,10 @@ static_library("client") { } if (crashpad_is_linux || crashpad_is_android) { - sources += [ "crashpad_client_linux.cc" ] + sources += [ + "crashpad_client_linux.cc", + "simulate_crash_linux.h", + ] } if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { diff --git a/client/client.gyp b/client/client.gyp index ecea124b..e149a2c1 100644 --- a/client/client.gyp +++ b/client/client.gyp @@ -50,6 +50,7 @@ 'simple_string_dictionary.h', 'simple_address_range_bag.h', 'simulate_crash.h', + 'simulate_crash_linux.h', 'simulate_crash_mac.cc', 'simulate_crash_mac.h', 'simulate_crash_win.h', @@ -73,6 +74,7 @@ ['OS=="android"', { 'sources/': [ ['include', '^crashpad_client_linux\\.cc$'], + ['include', '^simulate_crash_linux\\.h$'], ], }], ], diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 7cf2eb8a..1a48c181 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -24,6 +24,7 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "build/build_config.h" +#include "util/misc/capture_context.h" #if defined(OS_MACOSX) #include "base/mac/scoped_mach_port.h" @@ -168,6 +169,17 @@ class CrashpadClient { const std::map& annotations, const std::vector& arguments, int socket); + + //! \brief Requests that the handler capture a dump even though there hasn't + //! been a crash. + //! + //! TODO(jperaza): Floating point information in the context is zeroed out + //! until CaptureContext() supports collecting that information. + //! + //! \param[in] context A NativeCPUContext, generally captured by + //! CaptureContext() or similar. + static void DumpWithoutCrash(NativeCPUContext* context); + #endif // OS_LINUX || OS_ANDROID || DOXYGEN #if defined(OS_MACOSX) || DOXYGEN diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc index a36d9229..12558965 100644 --- a/client/crashpad_client_linux.cc +++ b/client/crashpad_client_linux.cc @@ -27,6 +27,7 @@ #include "util/file/file_io.h" #include "util/linux/exception_handler_client.h" #include "util/linux/exception_information.h" +#include "util/linux/scoped_pr_set_ptracer.h" #include "util/misc/from_pointer_cast.h" #include "util/posix/double_fork_and_exec.h" #include "util/posix/signals.h" @@ -82,7 +83,7 @@ void BuildHandlerArgvStrings( } } -void ConvertArgvStrings(const std::vector argv_strings, +void ConvertArgvStrings(const std::vector& argv_strings, std::vector* argv) { argv->clear(); argv->reserve(argv_strings.size() + 1); @@ -92,8 +93,22 @@ void ConvertArgvStrings(const std::vector argv_strings, argv->push_back(nullptr); } +class SignalHandler { + public: + virtual void HandleCrashFatal(int signo, + siginfo_t* siginfo, + void* context) = 0; + virtual void HandleCrashNonFatal(int signo, + siginfo_t* siginfo, + void* context) = 0; + + protected: + SignalHandler() = default; + ~SignalHandler() = default; +}; + // Launches a single use handler to snapshot this process. -class LaunchAtCrashHandler { +class LaunchAtCrashHandler : public SignalHandler { public: static LaunchAtCrashHandler* Get() { static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler(); @@ -110,6 +125,37 @@ class LaunchAtCrashHandler { return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr); } + void HandleCrashNonFatal(int signo, + siginfo_t* siginfo, + void* context) override { + exception_information_.siginfo_address = + FromPointerCast( + siginfo); + exception_information_.context_address = + FromPointerCast( + context); + exception_information_.thread_id = syscall(SYS_gettid); + + ScopedPrSetPtracer set_ptracer(getpid(), /* may_log= */ false); + + pid_t pid = fork(); + if (pid < 0) { + return; + } + if (pid == 0) { + execv(argv_[0], const_cast(argv_.data())); + _exit(EXIT_FAILURE); + } + + int status; + waitpid(pid, &status, 0); + } + + void HandleCrashFatal(int signo, siginfo_t* siginfo, void* context) override { + HandleCrashNonFatal(signo, siginfo, context); + Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr); + } + private: LaunchAtCrashHandler() = default; @@ -117,27 +163,7 @@ class LaunchAtCrashHandler { static void HandleCrash(int signo, siginfo_t* siginfo, void* context) { auto state = Get(); - auto exception_information = &state->exception_information_; - - exception_information->siginfo_address = - FromPointerCastsiginfo_address)>( - siginfo); - exception_information->context_address = - FromPointerCastcontext_address)>( - context); - exception_information->thread_id = syscall(SYS_gettid); - - pid_t pid = fork(); - if (pid < 0) { - return; - } - if (pid == 0) { - execv(state->argv_[0], const_cast(state->argv_.data())); - return; - } - - int status; - waitpid(pid, &status, 0); + state->HandleCrashFatal(signo, siginfo, context); } std::vector argv_strings_; @@ -147,6 +173,11 @@ class LaunchAtCrashHandler { DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler); }; +// A pointer to the currently installed crash signal handler. This allows +// the static method CrashpadClient::DumpWithoutCrashing to simulate a crash +// using the currently configured crash handling strategy. +static SignalHandler* g_crash_handler; + } // namespace CrashpadClient::CrashpadClient() {} @@ -181,7 +212,12 @@ bool CrashpadClient::StartHandlerAtCrash( handler, database, metrics_dir, url, annotations, arguments, &argv); auto signal_handler = LaunchAtCrashHandler::Get(); - return signal_handler->Initialize(&argv); + if (signal_handler->Initialize(&argv)) { + DCHECK(!g_crash_handler); + g_crash_handler = signal_handler; + return true; + } + return false; } bool CrashpadClient::StartHandlerForClient( @@ -201,4 +237,34 @@ bool CrashpadClient::StartHandlerForClient( return DoubleForkAndExec(argv, socket, true, nullptr); } +// static +void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) { + if (!g_crash_handler) { + LOG(WARNING) << "No crash handler installed"; + return; + } + +#if defined(ARCH_CPU_X86) + memset(&context->__fpregs_mem, 0, sizeof(context->__fpregs_mem)); + context->__fpregs_mem.status = 0xffff0000; +#elif defined(ARCH_CPU_X86_64) + memset(&context->__fpregs_mem, 0, sizeof(context->__fpregs_mem)); +#elif defined(ARCH_CPU_ARMEL) + memset(context->uc_regspace, 0, sizeof(context->uc_regspace)); +#elif defined(ARCH_CPU_ARM64) + memset(context->uc_mcontext.__reserved, + 0, + sizeof(context->uc_mcontext.__reserved)); +#else +#error Port. +#endif + + siginfo_t siginfo; + siginfo.si_signo = Signals::kSimulatedSigno; + siginfo.si_errno = 0; + siginfo.si_code = 0; + g_crash_handler->HandleCrashNonFatal( + siginfo.si_signo, &siginfo, reinterpret_cast(context)); +} + } // namespace crashpad diff --git a/client/simulate_crash.h b/client/simulate_crash.h index 299fe97c..63e09a17 100644 --- a/client/simulate_crash.h +++ b/client/simulate_crash.h @@ -21,6 +21,8 @@ #include "client/simulate_crash_mac.h" #elif defined(OS_WIN) #include "client/simulate_crash_win.h" +#elif defined(OS_LINUX) || defined(OS_ANDROID) +#include "client/simulate_crash_linux.h" #endif #endif // CRASHPAD_CLIENT_SIMULATE_CRASH_H_ diff --git a/client/simulate_crash_linux.h b/client/simulate_crash_linux.h new file mode 100644 index 00000000..e6c3e487 --- /dev/null +++ b/client/simulate_crash_linux.h @@ -0,0 +1,31 @@ +// Copyright 2018 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_CLIENT_SIMULATE_CRASH_LINUX_H_ +#define CRASHPAD_CLIENT_SIMULATE_CRASH_LINUX_H_ + +#include "client/crashpad_client.h" +#include "util/misc/capture_context.h" + +//! \file + +//! \brief Captures the CPU context and simulates an exception without crashing. +#define CRASHPAD_SIMULATE_CRASH() \ + do { \ + crashpad::NativeCPUContext simulate_crash_cpu_context; \ + crashpad::CaptureContext(&simulate_crash_cpu_context); \ + crashpad::CrashpadClient::DumpWithoutCrash(&simulate_crash_cpu_context); \ + } while (false) + +#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_LINUX_H_ diff --git a/util/misc/capture_context.h b/util/misc/capture_context.h index d12fad6a..5c1838a1 100644 --- a/util/misc/capture_context.h +++ b/util/misc/capture_context.h @@ -37,6 +37,10 @@ using NativeCPUContext = CONTEXT; using NativeCPUContext = ucontext_t; #endif // OS_MACOSX +// No NativeCPUContext defined for Fuchsia yet. +// https://crashpad.chromium.org/bug/196. +#if !defined(OS_FUCHSIA) + //! \brief Saves the CPU context. //! //! The CPU context will be captured as accurately and completely as possible, @@ -76,6 +80,8 @@ using NativeCPUContext = ucontext_t; //! \endcode void CaptureContext(NativeCPUContext* cpu_context); +#endif // !OS_FUCHSIA + } // namespace crashpad #endif // CRASHPAD_UTIL_MISC_CAPTURE_CONTEXT_H_ diff --git a/util/posix/signals.h b/util/posix/signals.h index ade093bf..dc550594 100644 --- a/util/posix/signals.h +++ b/util/posix/signals.h @@ -24,6 +24,9 @@ namespace crashpad { //! \brief Utilities for handling POSIX signals. class Signals { public: + //! \brief A signal number used by Crashpad to simulate signals. + static constexpr int kSimulatedSigno = -1; + //! \brief The type used for `struct sigaction::sa_sigaction`. using Handler = void (*)(int, siginfo_t*, void*); From 3dd85dc12638a6d355067356b8a9819dde11ee67 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 20 Feb 2018 16:10:20 -0800 Subject: [PATCH 186/326] fuchsia: Make ImageAnnotationReader[Test] work Ports the test away from fork() to MultiprocessExec. Requires a Fuchsia SDK that includes the fix in https://fuchsia-review.googlesource.com/c/zircon/+/125081. Bug: crashpad:196, crashpad:215 Change-Id: Ia8d382cebe8d2ffc8d877e5249baf0e58aee248c Reviewed-on: https://chromium-review.googlesource.com/927355 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- snapshot/BUILD.gn | 6 +- .../image_annotation_reader_test.cc | 149 ++++++++++-------- 2 files changed, 89 insertions(+), 66 deletions(-) diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 0f3c5bdb..24de0efd 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -106,8 +106,6 @@ static_library("snapshot") { if (crashpad_is_linux || crashpad_is_android) { sources += [ - "crashpad_types/image_annotation_reader.cc", - "crashpad_types/image_annotation_reader.h", "linux/cpu_context_linux.cc", "linux/cpu_context_linux.h", "linux/debug_rendezvous.cc", @@ -134,6 +132,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", @@ -307,7 +307,6 @@ source_set("snapshot_test") { if (crashpad_is_linux || crashpad_is_android) { sources += [ - "crashpad_types/image_annotation_reader_test.cc", "linux/debug_rendezvous_test.cc", "linux/exception_snapshot_linux_test.cc", "linux/process_reader_test.cc", @@ -320,6 +319,7 @@ source_set("snapshot_test") { if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { 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", diff --git a/snapshot/crashpad_types/image_annotation_reader_test.cc b/snapshot/crashpad_types/image_annotation_reader_test.cc index b0e635ff..63cfcfcc 100644 --- a/snapshot/crashpad_types/image_annotation_reader_test.cc +++ b/snapshot/crashpad_types/image_annotation_reader_test.cc @@ -26,11 +26,12 @@ #include "client/annotation_list.h" #include "client/simple_string_dictionary.h" #include "gtest/gtest.h" -#include "test/multiprocess.h" +#include "test/multiprocess_exec.h" +#include "test/process_type.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" +#include "util/process/process_memory_native.h" namespace crashpad { namespace test { @@ -60,67 +61,64 @@ void ExpectAnnotationList(const std::vector& list, } } -class AnnotationTest { - public: - AnnotationTest() - : expected_simple_map_(), - test_annotations_(), - expected_annotation_list_() { - expected_simple_map_.SetKeyValue("key", "value"); - expected_simple_map_.SetKeyValue("key2", "value2"); +void BuildTestStructures( + std::vector>* annotations_storage, + SimpleStringDictionary* into_map, + AnnotationList* into_annotation_list) { + into_map->SetKeyValue("key", "value"); + into_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::Type::kString, - kAnnotationName, - reinterpret_cast(const_cast(kAnnotationValue)))); - test_annotations_.back()->SetSize(sizeof(kAnnotationValue)); - expected_annotation_list_.Add(test_annotations_.back().get()); + static constexpr char kAnnotationName[] = "test annotation"; + static constexpr char kAnnotationValue[] = "test annotation value"; + annotations_storage->push_back(std::make_unique( + Annotation::Type::kString, + kAnnotationName, + reinterpret_cast(const_cast(kAnnotationValue)))); + annotations_storage->back()->SetSize(sizeof(kAnnotationValue)); + into_annotation_list->Add(annotations_storage->back().get()); - static constexpr char kAnnotationName2[] = "test annotation2"; - static constexpr char kAnnotationValue2[] = "test annotation value2"; - test_annotations_.push_back(std::make_unique( - Annotation::Type::kString, - kAnnotationName2, - reinterpret_cast(const_cast(kAnnotationValue2)))); - test_annotations_.back()->SetSize(sizeof(kAnnotationValue2)); - expected_annotation_list_.Add(test_annotations_.back().get()); - } + static constexpr char kAnnotationName2[] = "test annotation2"; + static constexpr char kAnnotationValue2[] = "test annotation value2"; + annotations_storage->push_back(std::make_unique( + Annotation::Type::kString, + kAnnotationName2, + reinterpret_cast(const_cast(kAnnotationValue2)))); + annotations_storage->back()->SetSize(sizeof(kAnnotationValue2)); + into_annotation_list->Add(annotations_storage->back().get()); +} - ~AnnotationTest() = default; +void ExpectAnnotations(ProcessType process, + bool is_64_bit, + VMAddress simple_map_address, + VMAddress annotation_list_address) { + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); - 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)); - ProcessMemoryRange range; - ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); + SimpleStringDictionary expected_simple_map; + std::vector> storage; + AnnotationList expected_annotations; + BuildTestStructures(&storage, &expected_simple_map, &expected_annotations); - ImageAnnotationReader reader(&range); + ImageAnnotationReader reader(&range); - std::map simple_map; - ASSERT_TRUE(reader.SimpleMap( - FromPointerCast(&expected_simple_map_), &simple_map)); - ExpectSimpleMap(simple_map, expected_simple_map_); + std::map simple_map; + ASSERT_TRUE(reader.SimpleMap(simple_map_address, &simple_map)); + ExpectSimpleMap(simple_map, expected_simple_map); - std::vector annotation_list; - ASSERT_TRUE(reader.AnnotationsList( - FromPointerCast(&expected_annotation_list_), - &annotation_list)); - ExpectAnnotationList(annotation_list, expected_annotation_list_); - } - - private: - SimpleStringDictionary expected_simple_map_; - std::vector> test_annotations_; - AnnotationList expected_annotation_list_; - - DISALLOW_COPY_AND_ASSIGN(AnnotationTest); -}; + std::vector annotation_list; + ASSERT_TRUE( + reader.AnnotationsList(annotation_list_address, &annotation_list)); + ExpectAnnotationList(annotation_list, expected_annotations); +} TEST(ImageAnnotationReader, ReadFromSelf) { - AnnotationTest test; + SimpleStringDictionary map; + std::vector> storage; + AnnotationList annotations; + BuildTestStructures(&storage, &map, &annotations); #if defined(ARCH_CPU_64_BITS) constexpr bool am_64_bit = true; @@ -128,14 +126,35 @@ TEST(ImageAnnotationReader, ReadFromSelf) { constexpr bool am_64_bit = false; #endif - test.ExpectAnnotations(getpid(), am_64_bit); + ExpectAnnotations(GetSelfProcess(), + am_64_bit, + FromPointerCast(&map), + FromPointerCast(&annotations)); } -class ReadFromChildTest : public Multiprocess { - public: - ReadFromChildTest() : Multiprocess(), annotation_test_() {} +CRASHPAD_CHILD_TEST_MAIN(ReadAnnotationsFromChildTestMain) { + SimpleStringDictionary map; + std::vector> storage; + AnnotationList annotations; + BuildTestStructures(&storage, &map, &annotations); - ~ReadFromChildTest() {} + VMAddress simple_map_address = FromPointerCast(&map); + VMAddress annotations_address = FromPointerCast(&annotations); + FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); + CheckedWriteFile(out, &simple_map_address, sizeof(simple_map_address)); + CheckedWriteFile(out, &annotations_address, sizeof(annotations_address)); + + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadFromChildTest : public MultiprocessExec { + public: + ReadFromChildTest() : MultiprocessExec() { + SetChildTestMainFunction("ReadAnnotationsFromChildTestMain"); + } + + ~ReadFromChildTest() = default; private: void MultiprocessParent() { @@ -144,13 +163,17 @@ class ReadFromChildTest : public Multiprocess { #else constexpr bool am_64_bit = false; #endif - annotation_test_.ExpectAnnotations(ChildPID(), am_64_bit); + + VMAddress simple_map_address; + VMAddress annotations_address; + ASSERT_TRUE(ReadFileExactly( + ReadPipeHandle(), &simple_map_address, sizeof(simple_map_address))); + ASSERT_TRUE(ReadFileExactly( + ReadPipeHandle(), &annotations_address, sizeof(annotations_address))); + ExpectAnnotations( + ChildProcess(), am_64_bit, simple_map_address, annotations_address); } - void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } - - AnnotationTest annotation_test_; - DISALLOW_COPY_AND_ASSIGN(ReadFromChildTest); }; From cab259330f2e73c4862d9d71eae70bcfcd44faa6 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 20 Feb 2018 16:10:33 -0800 Subject: [PATCH 187/326] fuchsia: Pass more data out of module snapshot After https://chromium-review.googlesource.com/c/crashpad/crashpad/+/927355 image annotations can be read. Plumb those through ModuleSnapshotFuchsia. Bug: crashpad:196 Change-Id: Iba0730fd88c60cbad8a721ddcaf8f60860f76b77 Reviewed-on: https://chromium-review.googlesource.com/927704 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- snapshot/fuchsia/module_snapshot_fuchsia.cc | 35 ++++++++++++++------ snapshot/fuchsia/module_snapshot_fuchsia.h | 7 ++-- snapshot/fuchsia/process_reader.cc | 9 ++++- snapshot/fuchsia/process_reader.h | 4 +++ snapshot/fuchsia/process_snapshot_fuchsia.cc | 2 +- 5 files changed, 40 insertions(+), 17 deletions(-) diff --git a/snapshot/fuchsia/module_snapshot_fuchsia.cc b/snapshot/fuchsia/module_snapshot_fuchsia.cc index 6d57bf7f..6a90edd0 100644 --- a/snapshot/fuchsia/module_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/module_snapshot_fuchsia.cc @@ -14,8 +14,9 @@ #include "snapshot/fuchsia/module_snapshot_fuchsia.h" +#include + #include "base/logging.h" -#include "client/crashpad_info.h" #include "snapshot/crashpad_types/image_annotation_reader.h" #include "util/misc/elf_note_types.h" @@ -27,13 +28,12 @@ ModuleSnapshotFuchsia::ModuleSnapshotFuchsia() = default; ModuleSnapshotFuchsia::~ModuleSnapshotFuchsia() = default; bool ModuleSnapshotFuchsia::Initialize( - ProcessReader* process_reader, const ProcessReader::Module& process_reader_module) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); - process_reader_ = process_reader; name_ = process_reader_module.name; elf_image_reader_ = process_reader_module.reader; + type_ = process_reader_module.type; if (!elf_image_reader_) { return false; } @@ -128,14 +128,21 @@ void ModuleSnapshotFuchsia::SourceVersion(uint16_t* version_0, ModuleSnapshot::ModuleType ModuleSnapshotFuchsia::GetModuleType() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return kModuleTypeUnknown; + return type_; } void ModuleSnapshotFuchsia::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + *age = 0; + + std::unique_ptr notes = + elf_image_reader_->NotesWithNameAndType( + ELF_NOTE_GNU, NT_GNU_BUILD_ID, 64); + std::string desc; + notes->NextNote(nullptr, nullptr, &desc); + desc.insert(desc.end(), 16 - std::min(desc.size(), size_t{16}), '\0'); + uuid->InitializeFromBytes(reinterpret_cast(&desc[0])); } std::string ModuleSnapshotFuchsia::DebugFileName() const { @@ -153,15 +160,23 @@ std::vector ModuleSnapshotFuchsia::AnnotationsVector() const { std::map ModuleSnapshotFuchsia::AnnotationsSimpleMap() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return std::map(); + std::map annotations; + if (crashpad_info_ && crashpad_info_->SimpleAnnotations()) { + ImageAnnotationReader reader(elf_image_reader_->Memory()); + reader.SimpleMap(crashpad_info_->SimpleAnnotations(), &annotations); + } + return annotations; } std::vector ModuleSnapshotFuchsia::AnnotationObjects() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return std::vector(); + std::vector annotations; + if (crashpad_info_ && crashpad_info_->AnnotationsList()) { + ImageAnnotationReader reader(elf_image_reader_->Memory()); + reader.AnnotationsList(crashpad_info_->AnnotationsList(), &annotations); + } + return annotations; } std::set> ModuleSnapshotFuchsia::ExtraMemoryRanges() diff --git a/snapshot/fuchsia/module_snapshot_fuchsia.h b/snapshot/fuchsia/module_snapshot_fuchsia.h index b7831441..6d66fd23 100644 --- a/snapshot/fuchsia/module_snapshot_fuchsia.h +++ b/snapshot/fuchsia/module_snapshot_fuchsia.h @@ -44,15 +44,12 @@ class ModuleSnapshotFuchsia final : public ModuleSnapshot { //! \brief Initializes the object. //! - //! \param[in] process_reader A ProcessReader for the process containing the - //! module. //! \param[in] process_reader_module The module within the ProcessReader for //! which the snapshot should be created. //! //! \return `true` if the snapshot could be created, `false` otherwise with //! an appropriate message logged. - bool Initialize(ProcessReader* process_reader, - const ProcessReader::Module& process_reader_module); + bool Initialize(const ProcessReader::Module& process_reader_module); //! \brief Returns options from the module’s CrashpadInfo structure. //! @@ -87,8 +84,8 @@ class ModuleSnapshotFuchsia final : public ModuleSnapshot { private: std::string name_; ElfImageReader* elf_image_reader_; // weak - ProcessReader* process_reader_; // weak std::unique_ptr crashpad_info_; + ModuleType type_; InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotFuchsia); diff --git a/snapshot/fuchsia/process_reader.cc b/snapshot/fuchsia/process_reader.cc index df19667d..ffd81635 100644 --- a/snapshot/fuchsia/process_reader.cc +++ b/snapshot/fuchsia/process_reader.cc @@ -137,7 +137,14 @@ void ProcessReader::InitializeModules() { } Module module; - module.name = dsoname.empty() ? app_name : dsoname; + if (dsoname.empty()) { + module.name = app_name; + module.type = ModuleSnapshot::kModuleTypeExecutable; + } else { + module.name = dsoname; + // TODO(scottmg): Handle kModuleTypeDynamicLoader. + module.type = ModuleSnapshot::kModuleTypeSharedLibrary; + } std::unique_ptr reader(new ElfImageReader()); diff --git a/snapshot/fuchsia/process_reader.h b/snapshot/fuchsia/process_reader.h index 332afc37..b3b24e36 100644 --- a/snapshot/fuchsia/process_reader.h +++ b/snapshot/fuchsia/process_reader.h @@ -21,6 +21,7 @@ #include "base/macros.h" #include "build/build_config.h" #include "snapshot/elf/elf_image_reader.h" +#include "snapshot/module_snapshot.h" #include "util/misc/initialization_state_dcheck.h" #include "util/process/process_memory_fuchsia.h" #include "util/process/process_memory_range.h" @@ -48,6 +49,9 @@ class ProcessReader { //! This field may be `nullptr` if a reader could not be created for the //! module. ElfImageReader* reader; + + //! \brief The module's type. + ModuleSnapshot::ModuleType type = ModuleSnapshot::kModuleTypeUnknown; }; ProcessReader(); diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc index 1505f1b2..02f8285d 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -173,7 +173,7 @@ void ProcessSnapshotFuchsia::InitializeModules() { for (const ProcessReader::Module& process_reader_module : process_reader_modules) { auto module = std::make_unique(); - if (module->Initialize(&process_reader_, process_reader_module)) { + if (module->Initialize(process_reader_module)) { modules_.push_back(std::move(module)); } } From 1aae5cedaf1e759fd05270ccb09642224bcfa555 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 20 Feb 2018 16:11:41 -0800 Subject: [PATCH 188/326] Refactor ModuleSnapshot(Linux|Fuchsia) into ModuleSnapshotElf They were largely the same after recent changes, so with a bit at initialization time the whole class can be de-duplicated. Bug: crashpad:196, crashpad:30 Change-Id: I2f5df797dfe36e120090e570273b48ee03f660a5 Reviewed-on: https://chromium-review.googlesource.com/927611 Reviewed-by: Joshua Peraza Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- snapshot/BUILD.gn | 6 +- .../module_snapshot_elf.cc} | 76 ++++--- .../module_snapshot_elf.h} | 27 +-- snapshot/fuchsia/module_snapshot_fuchsia.cc | 197 ------------------ snapshot/fuchsia/module_snapshot_fuchsia.h | 97 --------- snapshot/fuchsia/process_snapshot_fuchsia.cc | 10 +- snapshot/fuchsia/process_snapshot_fuchsia.h | 4 +- snapshot/linux/process_snapshot_linux.cc | 5 +- snapshot/linux/process_snapshot_linux.h | 4 +- snapshot/snapshot.gyp | 4 +- 10 files changed, 64 insertions(+), 366 deletions(-) rename snapshot/{linux/module_snapshot_linux.cc => elf/module_snapshot_elf.cc} (73%) rename snapshot/{linux/module_snapshot_linux.h => elf/module_snapshot_elf.h} (80%) delete mode 100644 snapshot/fuchsia/module_snapshot_fuchsia.cc delete mode 100644 snapshot/fuchsia/module_snapshot_fuchsia.h diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 24de0efd..96df03d6 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -114,8 +114,6 @@ static_library("snapshot") { "linux/exception_snapshot_linux.h", "linux/memory_snapshot_linux.cc", "linux/memory_snapshot_linux.h", - "linux/module_snapshot_linux.cc", - "linux/module_snapshot_linux.h", "linux/process_reader.cc", "linux/process_reader.h", "linux/process_snapshot_linux.cc", @@ -140,6 +138,8 @@ static_library("snapshot") { "elf/elf_image_reader.h", "elf/elf_symbol_table_reader.cc", "elf/elf_symbol_table_reader.h", + "elf/module_snapshot_elf.cc", + "elf/module_snapshot_elf.h", ] } @@ -178,8 +178,6 @@ static_library("snapshot") { if (crashpad_is_fuchsia) { sources += [ - "fuchsia/module_snapshot_fuchsia.cc", - "fuchsia/module_snapshot_fuchsia.h", "fuchsia/process_reader.cc", "fuchsia/process_reader.h", "fuchsia/process_snapshot_fuchsia.cc", diff --git a/snapshot/linux/module_snapshot_linux.cc b/snapshot/elf/module_snapshot_elf.cc similarity index 73% rename from snapshot/linux/module_snapshot_linux.cc rename to snapshot/elf/module_snapshot_elf.cc index 17a03c1f..5c5039ee 100644 --- a/snapshot/linux/module_snapshot_linux.cc +++ b/snapshot/elf/module_snapshot_elf.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "snapshot/linux/module_snapshot_linux.h" +#include "snapshot/elf/module_snapshot_elf.h" #include @@ -23,29 +23,26 @@ namespace crashpad { namespace internal { -ModuleSnapshotLinux::ModuleSnapshotLinux() +ModuleSnapshotElf::ModuleSnapshotElf(const std::string& name, + ElfImageReader* elf_reader, + ModuleSnapshot::ModuleType type) : ModuleSnapshot(), - name_(), - elf_reader_(nullptr), + name_(name), + elf_reader_(elf_reader), crashpad_info_(), - type_(kModuleTypeUnknown), + type_(type), initialized_() {} -ModuleSnapshotLinux::~ModuleSnapshotLinux() = default; +ModuleSnapshotElf::~ModuleSnapshotElf() = default; -bool ModuleSnapshotLinux::Initialize( - const ProcessReader::Module& process_reader_module) { +bool ModuleSnapshotElf::Initialize() { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); - if (!process_reader_module.elf_reader) { + if (!elf_reader_) { LOG(ERROR) << "no elf reader"; return false; } - name_ = process_reader_module.name; - elf_reader_ = process_reader_module.elf_reader; - type_ = process_reader_module.type; - // The data payload is only sizeof(VMAddress) in the note, but add a bit to // account for the name, header, and padding. constexpr ssize_t kMaxNoteSize = 256; @@ -72,8 +69,7 @@ bool ModuleSnapshotLinux::Initialize( return true; } -bool ModuleSnapshotLinux::GetCrashpadOptions( - CrashpadInfoClientOptions* options) { +bool ModuleSnapshotElf::GetCrashpadOptions(CrashpadInfoClientOptions* options) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); if (!crashpad_info_) { @@ -91,27 +87,38 @@ bool ModuleSnapshotLinux::GetCrashpadOptions( return true; } -std::string ModuleSnapshotLinux::Name() const { +std::string ModuleSnapshotElf::Name() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return name_; } -uint64_t ModuleSnapshotLinux::Address() const { +uint64_t ModuleSnapshotElf::Address() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return elf_reader_->Address(); } -uint64_t ModuleSnapshotLinux::Size() const { +uint64_t ModuleSnapshotElf::Size() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return elf_reader_->Size(); } -time_t ModuleSnapshotLinux::Timestamp() const { +time_t ModuleSnapshotElf::Timestamp() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return 0; } -void ModuleSnapshotLinux::FileVersion(uint16_t* version_0, +void ModuleSnapshotElf::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; +} + +void ModuleSnapshotElf::SourceVersion(uint16_t* version_0, uint16_t* version_1, uint16_t* version_2, uint16_t* version_3) const { @@ -122,24 +129,12 @@ void ModuleSnapshotLinux::FileVersion(uint16_t* version_0, *version_3 = 0; } -void ModuleSnapshotLinux::SourceVersion(uint16_t* version_0, - uint16_t* version_1, - uint16_t* version_2, - uint16_t* version_3) const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - *version_0 = 0; - *version_1 = 0; - *version_2 = 0; - *version_3 = 0; -} - -ModuleSnapshot::ModuleType ModuleSnapshotLinux::GetModuleType() const { +ModuleSnapshot::ModuleType ModuleSnapshotElf::GetModuleType() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return type_; } -void ModuleSnapshotLinux::UUIDAndAge(crashpad::UUID* uuid, - uint32_t* age) const { +void ModuleSnapshotElf::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); *age = 0; @@ -151,17 +146,17 @@ void ModuleSnapshotLinux::UUIDAndAge(crashpad::UUID* uuid, uuid->InitializeFromBytes(reinterpret_cast(&desc[0])); } -std::string ModuleSnapshotLinux::DebugFileName() const { +std::string ModuleSnapshotElf::DebugFileName() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return base::FilePath(Name()).BaseName().value(); } -std::vector ModuleSnapshotLinux::AnnotationsVector() const { +std::vector ModuleSnapshotElf::AnnotationsVector() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return std::vector(); } -std::map ModuleSnapshotLinux::AnnotationsSimpleMap() +std::map ModuleSnapshotElf::AnnotationsSimpleMap() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::map annotations; @@ -172,7 +167,7 @@ std::map ModuleSnapshotLinux::AnnotationsSimpleMap() return annotations; } -std::vector ModuleSnapshotLinux::AnnotationObjects() const { +std::vector ModuleSnapshotElf::AnnotationObjects() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::vector annotations; if (crashpad_info_ && crashpad_info_->AnnotationsList()) { @@ -182,14 +177,13 @@ std::vector ModuleSnapshotLinux::AnnotationObjects() const { return annotations; } -std::set> ModuleSnapshotLinux::ExtraMemoryRanges() - const { +std::set> ModuleSnapshotElf::ExtraMemoryRanges() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return std::set>(); } std::vector -ModuleSnapshotLinux::CustomMinidumpStreams() const { +ModuleSnapshotElf::CustomMinidumpStreams() const { return std::vector(); } diff --git a/snapshot/linux/module_snapshot_linux.h b/snapshot/elf/module_snapshot_elf.h similarity index 80% rename from snapshot/linux/module_snapshot_linux.h rename to snapshot/elf/module_snapshot_elf.h index b277ff7a..1f7cf651 100644 --- a/snapshot/linux/module_snapshot_linux.h +++ b/snapshot/elf/module_snapshot_elf.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CRASHPAD_SNAPSHOT_LINUX_MODULE_SNAPSHOT_LINUX_H_ -#define CRASHPAD_SNAPSHOT_LINUX_MODULE_SNAPSHOT_LINUX_H_ +#ifndef CRASHPAD_SNAPSHOT_ELF_MODULE_SNAPSHOT_ELF_H_ +#define CRASHPAD_SNAPSHOT_ELF_MODULE_SNAPSHOT_ELF_H_ #include #include @@ -27,7 +27,6 @@ #include "snapshot/crashpad_info_client_options.h" #include "snapshot/crashpad_types/crashpad_info_reader.h" #include "snapshot/elf/elf_image_reader.h" -#include "snapshot/linux/process_reader.h" #include "snapshot/module_snapshot.h" #include "util/misc/initialization_state_dcheck.h" @@ -36,20 +35,22 @@ namespace crashpad { namespace internal { //! \brief A ModuleSnapshot of a code module (binary image) loaded into a -//! running (or crashed) process on a Linux system. -class ModuleSnapshotLinux final : public ModuleSnapshot { +//! running (or crashed) process on a system that uses ELF modules. +class ModuleSnapshotElf final : public ModuleSnapshot { public: - ModuleSnapshotLinux(); - ~ModuleSnapshotLinux() override; + //! \param[in] name The pathname used to load the module from disk. + //! \param[in] elf_reader An image reader for the module. + //! \param[in] type The module's type. + ModuleSnapshotElf(const std::string& name, + ElfImageReader* elf_reader, + ModuleSnapshot::ModuleType type); + ~ModuleSnapshotElf() override; //! \brief Initializes the object. //! - //! \param[in] process_reader_module The module within the ProcessReader for - //! which the snapshot should be created. - //! //! \return `true` if the snapshot could be created, `false` otherwise with //! an appropriate message logged. - bool Initialize(const ProcessReader::Module& process_reader_module); + bool Initialize(); //! \brief Returns options from the module’s CrashpadInfo structure. //! @@ -87,10 +88,10 @@ class ModuleSnapshotLinux final : public ModuleSnapshot { ModuleType type_; InitializationStateDcheck initialized_; - DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotLinux); + DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotElf); }; } // namespace internal } // namespace crashpad -#endif // CRASHPAD_SNAPSHOT_LINUX_MODULE_SNAPSHOT_LINUX_H_ +#endif // CRASHPAD_SNAPSHOT_ELF_MODULE_SNAPSHOT_ELF_H_ diff --git a/snapshot/fuchsia/module_snapshot_fuchsia.cc b/snapshot/fuchsia/module_snapshot_fuchsia.cc deleted file mode 100644 index 6a90edd0..00000000 --- a/snapshot/fuchsia/module_snapshot_fuchsia.cc +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2018 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/fuchsia/module_snapshot_fuchsia.h" - -#include - -#include "base/logging.h" -#include "snapshot/crashpad_types/image_annotation_reader.h" -#include "util/misc/elf_note_types.h" - -namespace crashpad { -namespace internal { - -ModuleSnapshotFuchsia::ModuleSnapshotFuchsia() = default; - -ModuleSnapshotFuchsia::~ModuleSnapshotFuchsia() = default; - -bool ModuleSnapshotFuchsia::Initialize( - const ProcessReader::Module& process_reader_module) { - INITIALIZATION_STATE_SET_INITIALIZING(initialized_); - - name_ = process_reader_module.name; - elf_image_reader_ = process_reader_module.reader; - type_ = process_reader_module.type; - if (!elf_image_reader_) { - return false; - } - - // The data payload is only sizeof(VMAddress) in the note, but add a bit to - // account for the name, header, and padding. - constexpr ssize_t kMaxNoteSize = 256; - std::unique_ptr notes = - elf_image_reader_->NotesWithNameAndType( - CRASHPAD_ELF_NOTE_NAME, - CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO, - kMaxNoteSize); - std::string desc; - VMAddress info_address; - if (notes->NextNote(nullptr, nullptr, &desc) == - ElfImageReader::NoteReader::Result::kSuccess) { - info_address = *reinterpret_cast(&desc[0]); - - ProcessMemoryRange range; - if (range.Initialize(*elf_image_reader_->Memory())) { - auto info = std::make_unique(); - if (info->Initialize(&range, info_address)) { - crashpad_info_ = std::move(info); - } - } - } - - INITIALIZATION_STATE_SET_VALID(initialized_); - return true; -} - -bool ModuleSnapshotFuchsia::GetCrashpadOptions( - CrashpadInfoClientOptions* options) { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - - if (!crashpad_info_) { - return false; - } - - options->crashpad_handler_behavior = - crashpad_info_->CrashpadHandlerBehavior(); - options->system_crash_reporter_forwarding = - crashpad_info_->SystemCrashReporterForwarding(); - options->gather_indirectly_referenced_memory = - crashpad_info_->GatherIndirectlyReferencedMemory(); - options->indirectly_referenced_memory_cap = - crashpad_info_->IndirectlyReferencedMemoryCap(); - return true; -} - -std::string ModuleSnapshotFuchsia::Name() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return name_; -} - -uint64_t ModuleSnapshotFuchsia::Address() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return elf_image_reader_->Address(); -} - -uint64_t ModuleSnapshotFuchsia::Size() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return elf_image_reader_->Address(); -} - -time_t ModuleSnapshotFuchsia::Timestamp() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return 0; -} - -void ModuleSnapshotFuchsia::FileVersion(uint16_t* version_0, - uint16_t* version_1, - uint16_t* version_2, - uint16_t* version_3) const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - *version_0 = 0; - *version_1 = 0; - *version_2 = 0; - *version_3 = 0; -} - -void ModuleSnapshotFuchsia::SourceVersion(uint16_t* version_0, - uint16_t* version_1, - uint16_t* version_2, - uint16_t* version_3) const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - *version_0 = 0; - *version_1 = 0; - *version_2 = 0; - *version_3 = 0; -} - -ModuleSnapshot::ModuleType ModuleSnapshotFuchsia::GetModuleType() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return type_; -} - -void ModuleSnapshotFuchsia::UUIDAndAge(crashpad::UUID* uuid, - uint32_t* age) const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - *age = 0; - - std::unique_ptr notes = - elf_image_reader_->NotesWithNameAndType( - ELF_NOTE_GNU, NT_GNU_BUILD_ID, 64); - std::string desc; - notes->NextNote(nullptr, nullptr, &desc); - desc.insert(desc.end(), 16 - std::min(desc.size(), size_t{16}), '\0'); - uuid->InitializeFromBytes(reinterpret_cast(&desc[0])); -} - -std::string ModuleSnapshotFuchsia::DebugFileName() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return std::string(); -} - -std::vector ModuleSnapshotFuchsia::AnnotationsVector() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return std::vector(); -} - -std::map ModuleSnapshotFuchsia::AnnotationsSimpleMap() - const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - std::map annotations; - if (crashpad_info_ && crashpad_info_->SimpleAnnotations()) { - ImageAnnotationReader reader(elf_image_reader_->Memory()); - reader.SimpleMap(crashpad_info_->SimpleAnnotations(), &annotations); - } - return annotations; -} - -std::vector ModuleSnapshotFuchsia::AnnotationObjects() - const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - std::vector annotations; - if (crashpad_info_ && crashpad_info_->AnnotationsList()) { - ImageAnnotationReader reader(elf_image_reader_->Memory()); - reader.AnnotationsList(crashpad_info_->AnnotationsList(), &annotations); - } - return annotations; -} - -std::set> ModuleSnapshotFuchsia::ExtraMemoryRanges() - const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return std::set>(); -} - -std::vector -ModuleSnapshotFuchsia::CustomMinidumpStreams() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return std::vector(); -} - -} // namespace internal -} // namespace crashpad diff --git a/snapshot/fuchsia/module_snapshot_fuchsia.h b/snapshot/fuchsia/module_snapshot_fuchsia.h deleted file mode 100644 index 6d66fd23..00000000 --- a/snapshot/fuchsia/module_snapshot_fuchsia.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2018 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_FUCHSIA_MODULE_SNAPSHOT_FUCHSIA_H_ -#define CRASHPAD_SNAPSHOT_FUCHSIA_MODULE_SNAPSHOT_FUCHSIA_H_ - -#include -#include - -#include -#include -#include -#include - -#include "base/macros.h" -#include "client/crashpad_info.h" -#include "snapshot/crashpad_info_client_options.h" -#include "snapshot/crashpad_types/crashpad_info_reader.h" -#include "snapshot/elf/elf_image_reader.h" -#include "snapshot/fuchsia/process_reader.h" -#include "snapshot/module_snapshot.h" -#include "util/misc/initialization_state_dcheck.h" - -namespace crashpad { -namespace internal { - -//! \brief A ModuleSnapshot of a code module (binary image) loaded into a -//! running (or crashed) process on a Fuchsia system. -class ModuleSnapshotFuchsia final : public ModuleSnapshot { - public: - ModuleSnapshotFuchsia(); - ~ModuleSnapshotFuchsia() override; - - //! \brief Initializes the object. - //! - //! \param[in] process_reader_module The module within the ProcessReader for - //! which the snapshot should be created. - //! - //! \return `true` if the snapshot could be created, `false` otherwise with - //! an appropriate message logged. - bool Initialize(const ProcessReader::Module& process_reader_module); - - //! \brief Returns options from the module’s CrashpadInfo structure. - //! - //! \param[out] options Options set in the module’s CrashpadInfo structure. - //! - //! \return `true` if there were options returned. Otherwise `false`. - bool GetCrashpadOptions(CrashpadInfoClientOptions* options); - - // 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 UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; - std::string DebugFileName() const override; - std::vector AnnotationsVector() const override; - std::map AnnotationsSimpleMap() const override; - std::vector AnnotationObjects() const override; - std::set> ExtraMemoryRanges() const override; - std::vector CustomMinidumpStreams() const override; - - private: - std::string name_; - ElfImageReader* elf_image_reader_; // weak - std::unique_ptr crashpad_info_; - ModuleType type_; - InitializationStateDcheck initialized_; - - DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotFuchsia); -}; - -} // namespace internal -} // namespace crashpad - -#endif // CRASHPAD_SNAPSHOT_FUCHSIA_MODULE_SNAPSHOT_FUCHSIA_H_ diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc index 02f8285d..e0070eac 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -168,12 +168,10 @@ std::vector ProcessSnapshotFuchsia::ExtraMemory() const { } void ProcessSnapshotFuchsia::InitializeModules() { - const std::vector& process_reader_modules = - process_reader_.Modules(); - for (const ProcessReader::Module& process_reader_module : - process_reader_modules) { - auto module = std::make_unique(); - if (module->Initialize(process_reader_module)) { + for (const ProcessReader::Module& reader_module : process_reader_.Modules()) { + auto module = std::make_unique( + reader_module.name, reader_module.reader, reader_module.type); + if (module->Initialize()) { modules_.push_back(std::move(module)); } } diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.h b/snapshot/fuchsia/process_snapshot_fuchsia.h index fa0167d3..04e2d29d 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.h +++ b/snapshot/fuchsia/process_snapshot_fuchsia.h @@ -23,7 +23,7 @@ #include "base/macros.h" #include "snapshot/crashpad_info_client_options.h" #include "snapshot/elf/elf_image_reader.h" -#include "snapshot/fuchsia/module_snapshot_fuchsia.h" +#include "snapshot/elf/module_snapshot_elf.h" #include "snapshot/fuchsia/process_reader.h" #include "snapshot/process_snapshot.h" #include "snapshot/unloaded_module_snapshot.h" @@ -76,7 +76,7 @@ class ProcessSnapshotFuchsia : public ProcessSnapshot { // Initializes modules_ on behalf of Initialize(). void InitializeModules(); - std::vector> modules_; + std::vector> modules_; ProcessReader process_reader_; std::map annotations_simple_map_; InitializationStateDcheck initialized_; diff --git a/snapshot/linux/process_snapshot_linux.cc b/snapshot/linux/process_snapshot_linux.cc index 8397ad9b..ecb4eb0e 100644 --- a/snapshot/linux/process_snapshot_linux.cc +++ b/snapshot/linux/process_snapshot_linux.cc @@ -226,8 +226,9 @@ void ProcessSnapshotLinux::InitializeThreads() { void ProcessSnapshotLinux::InitializeModules() { for (const ProcessReader::Module& reader_module : process_reader_.Modules()) { - auto module = std::make_unique(); - if (module->Initialize(reader_module)) { + auto module = std::make_unique( + reader_module.name, reader_module.elf_reader, reader_module.type); + if (module->Initialize()) { modules_.push_back(std::move(module)); } } diff --git a/snapshot/linux/process_snapshot_linux.h b/snapshot/linux/process_snapshot_linux.h index aa6964c4..613913ce 100644 --- a/snapshot/linux/process_snapshot_linux.h +++ b/snapshot/linux/process_snapshot_linux.h @@ -25,8 +25,8 @@ #include "base/macros.h" #include "snapshot/crashpad_info_client_options.h" +#include "snapshot/elf/module_snapshot_elf.h" #include "snapshot/linux/exception_snapshot_linux.h" -#include "snapshot/linux/module_snapshot_linux.h" #include "snapshot/linux/process_reader.h" #include "snapshot/linux/system_snapshot_linux.h" #include "snapshot/linux/thread_snapshot_linux.h" @@ -124,7 +124,7 @@ class ProcessSnapshotLinux final : public ProcessSnapshot { UUID report_id_; UUID client_id_; std::vector> threads_; - std::vector> modules_; + std::vector> modules_; std::unique_ptr exception_; internal::SystemSnapshotLinux system_; ProcessReader process_reader_; diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index da192341..f3364d13 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -49,6 +49,8 @@ 'elf/elf_image_reader.h', 'elf/elf_symbol_table_reader.cc', 'elf/elf_symbol_table_reader.h', + 'elf/module_snapshot_elf.cc', + 'elf/module_snapshot_elf.h', 'exception_snapshot.h', 'handle_snapshot.cc', 'handle_snapshot.h', @@ -60,8 +62,6 @@ 'linux/exception_snapshot_linux.h', 'linux/memory_snapshot_linux.cc', 'linux/memory_snapshot_linux.h', - 'linux/module_snapshot_linux.cc', - 'linux/module_snapshot_linux.h', 'linux/process_reader.cc', 'linux/process_reader.h', 'linux/process_snapshot_linux.cc', From 4d96e4e504ef04b6acd2e03a92397c41df606352 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 21 Feb 2018 09:54:41 -0800 Subject: [PATCH 189/326] fuchsia: Return ModuleSnapshot* out of ProcessSnapshotFuchsia And document that UnloadedModules() isn't applicable on Fuchsia. Bug: crashpad:196 Change-Id: Ic2c5f26fbc9cbd908ec0b941797c63f88caeec9c Reviewed-on: https://chromium-review.googlesource.com/929302 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- snapshot/fuchsia/process_snapshot_fuchsia.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc index e0070eac..ce8b0ea8 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -131,14 +131,17 @@ std::vector ProcessSnapshotFuchsia::Threads() const { std::vector ProcessSnapshotFuchsia::Modules() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return std::vector(); + std::vector modules; + for (const auto& module : modules_) { + modules.push_back(module.get()); + } + return modules; } std::vector ProcessSnapshotFuchsia::UnloadedModules() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + // dlclose() never unloads on Fuchsia. ZX-1728 upstream. return std::vector(); } From 2290a826af1e8b86ced76435d24c06b372580474 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 21 Feb 2018 16:07:17 -0800 Subject: [PATCH 190/326] Pull (most) platform-specific MemorySnapshots out Pulls the concrete non-test implementations of MemorySnapshot out into a template. They were effectively identical on Mac and Linux/Android, and I was going to have to add another identical one for Fuchsia. Unfortunately it needs to be a template because of the snapshot merging template it calls that needs the platform-specific ProcessReader (so it can't just pass in a base ProcessMemory in initialization instead). This is used on Mac, Linux, Android, and Fuchsia, but there is still a Windows implementation (different because its ProcessReader is a bit different) and a test implementation. Bug: crashpad:196 Change-Id: I4b5575fee0749e96b08e756be1f8380a2c994d7c Reviewed-on: https://chromium-review.googlesource.com/929308 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- snapshot/BUILD.gn | 5 +- snapshot/linux/memory_snapshot_linux.cc | 73 ------------------ snapshot/linux/memory_snapshot_linux.h | 76 ------------------- snapshot/linux/thread_snapshot_linux.h | 4 +- snapshot/mac/memory_snapshot_mac.cc | 75 ------------------ snapshot/mac/thread_snapshot_mac.h | 5 +- ...apshot_mac.h => memory_snapshot_generic.h} | 66 +++++++++++----- snapshot/snapshot.gyp | 5 +- 8 files changed, 56 insertions(+), 253 deletions(-) delete mode 100644 snapshot/linux/memory_snapshot_linux.cc delete mode 100644 snapshot/linux/memory_snapshot_linux.h delete mode 100644 snapshot/mac/memory_snapshot_mac.cc rename snapshot/{mac/memory_snapshot_mac.h => memory_snapshot_generic.h} (50%) diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 96df03d6..b613774d 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -34,6 +34,7 @@ static_library("snapshot") { "handle_snapshot.h", "memory_snapshot.cc", "memory_snapshot.h", + "memory_snapshot_generic.h", "minidump/minidump_annotation_reader.cc", "minidump/minidump_annotation_reader.h", "minidump/minidump_simple_string_dictionary_reader.cc", @@ -76,8 +77,6 @@ static_library("snapshot") { "mac/mach_o_image_segment_reader.h", "mac/mach_o_image_symbol_table_reader.cc", "mac/mach_o_image_symbol_table_reader.h", - "mac/memory_snapshot_mac.cc", - "mac/memory_snapshot_mac.h", "mac/module_snapshot_mac.cc", "mac/module_snapshot_mac.h", "mac/process_reader.cc", @@ -112,8 +111,6 @@ static_library("snapshot") { "linux/debug_rendezvous.h", "linux/exception_snapshot_linux.cc", "linux/exception_snapshot_linux.h", - "linux/memory_snapshot_linux.cc", - "linux/memory_snapshot_linux.h", "linux/process_reader.cc", "linux/process_reader.h", "linux/process_snapshot_linux.cc", diff --git a/snapshot/linux/memory_snapshot_linux.cc b/snapshot/linux/memory_snapshot_linux.cc deleted file mode 100644 index dfe9d330..00000000 --- a/snapshot/linux/memory_snapshot_linux.cc +++ /dev/null @@ -1,73 +0,0 @@ -// 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/linux/memory_snapshot_linux.h" - -#include - -namespace crashpad { -namespace internal { - -MemorySnapshotLinux::MemorySnapshotLinux() - : MemorySnapshot(), - process_reader_(nullptr), - address_(0), - size_(0), - initialized_() { -} - -MemorySnapshotLinux::~MemorySnapshotLinux() { -} - -void MemorySnapshotLinux::Initialize(ProcessReader* process_reader, - LinuxVMAddress address, - size_t size) { - INITIALIZATION_STATE_SET_INITIALIZING(initialized_); - process_reader_ = process_reader; - address_ = address; - size_ = size; - INITIALIZATION_STATE_SET_VALID(initialized_); -} - -uint64_t MemorySnapshotLinux::Address() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return address_; -} - -size_t MemorySnapshotLinux::Size() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return size_; -} - -bool MemorySnapshotLinux::Read(Delegate* delegate) const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - - if (size_ == 0) { - return delegate->MemorySnapshotDelegateRead(nullptr, size_); - } - - std::unique_ptr buffer(new uint8_t[size_]); - if (!process_reader_->Memory()->Read(address_, size_, buffer.get())) { - return false; - } - return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); -} - -const MemorySnapshot* MemorySnapshotLinux::MergeWithOtherSnapshot( - const MemorySnapshot* other) const { - return MergeWithOtherSnapshotImpl(this, other); -} - -} // namespace internal -} // namespace crashpad diff --git a/snapshot/linux/memory_snapshot_linux.h b/snapshot/linux/memory_snapshot_linux.h deleted file mode 100644 index 8b4bcf8d..00000000 --- a/snapshot/linux/memory_snapshot_linux.h +++ /dev/null @@ -1,76 +0,0 @@ -// 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_LINUX_MEMORY_SNAPSHOT_LINUX_H_ -#define CRASHPAD_SNAPSHOT_LINUX_MEMORY_SNAPSHOT_LINUX_H_ - -#include -#include - -#include "base/macros.h" -#include "snapshot/linux/process_reader.h" -#include "snapshot/memory_snapshot.h" -#include "util/linux/address_types.h" -#include "util/misc/initialization_state_dcheck.h" - -namespace crashpad { -namespace internal { - -//! \brief A MemorySnapshot of a memory region in a process on the running -//! system, when the system runs Linux. -class MemorySnapshotLinux final : public MemorySnapshot { - public: - MemorySnapshotLinux(); - ~MemorySnapshotLinux() override; - - //! \brief Initializes the object. - //! - //! Memory is read lazily. No attempt is made to read the memory snapshot data - //! until Read() is called, and the memory snapshot data is discared when - //! Read() returns. - //! - //! \param[in] process_reader A reader for the process being snapshotted. - //! \param[in] address The base address of the memory region to snapshot, in - //! the snapshot process’ address space. - //! \param[in] size The size of the memory region to snapshot. - void Initialize(ProcessReader* process_reader, - LinuxVMAddress address, - size_t size); - - // MemorySnapshot: - - uint64_t Address() const override; - size_t Size() const override; - bool Read(Delegate* delegate) const override; - const MemorySnapshot* MergeWithOtherSnapshot( - const MemorySnapshot* other) const override; - - private: - template - friend const MemorySnapshot* MergeWithOtherSnapshotImpl( - const T* self, - const MemorySnapshot* other); - - ProcessReader* process_reader_; // weak - uint64_t address_; - size_t size_; - InitializationStateDcheck initialized_; - - DISALLOW_COPY_AND_ASSIGN(MemorySnapshotLinux); -}; - -} // namespace internal -} // namespace crashpad - -#endif // CRASHPAD_SNAPSHOT_LINUX_MEMORY_SNAPSHOT_LINUX_H_ diff --git a/snapshot/linux/thread_snapshot_linux.h b/snapshot/linux/thread_snapshot_linux.h index 1ba291dc..5741b0cb 100644 --- a/snapshot/linux/thread_snapshot_linux.h +++ b/snapshot/linux/thread_snapshot_linux.h @@ -20,9 +20,9 @@ #include "base/macros.h" #include "build/build_config.h" #include "snapshot/cpu_context.h" -#include "snapshot/linux/memory_snapshot_linux.h" #include "snapshot/linux/process_reader.h" #include "snapshot/memory_snapshot.h" +#include "snapshot/memory_snapshot_generic.h" #include "snapshot/thread_snapshot.h" #include "util/misc/initialization_state_dcheck.h" @@ -70,7 +70,7 @@ class ThreadSnapshotLinux final : public ThreadSnapshot { #endif // ARCH_CPU_X86_FAMILY } context_union_; CPUContext context_; - MemorySnapshotLinux stack_; + MemorySnapshotGeneric stack_; LinuxVMAddress thread_specific_data_address_; pid_t thread_id_; int priority_; diff --git a/snapshot/mac/memory_snapshot_mac.cc b/snapshot/mac/memory_snapshot_mac.cc deleted file mode 100644 index ec1d4608..00000000 --- a/snapshot/mac/memory_snapshot_mac.cc +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2014 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/mac/memory_snapshot_mac.h" - -#include - -#include "util/mach/task_memory.h" - -namespace crashpad { -namespace internal { - -MemorySnapshotMac::MemorySnapshotMac() - : MemorySnapshot(), - process_reader_(nullptr), - address_(0), - size_(0), - initialized_() { -} - -MemorySnapshotMac::~MemorySnapshotMac() { -} - -void MemorySnapshotMac::Initialize(ProcessReader* process_reader, - uint64_t address, - uint64_t size) { - INITIALIZATION_STATE_SET_INITIALIZING(initialized_); - process_reader_ = process_reader; - address_ = address; - size_ = size; - INITIALIZATION_STATE_SET_VALID(initialized_); -} - -uint64_t MemorySnapshotMac::Address() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return address_; -} - -size_t MemorySnapshotMac::Size() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return size_; -} - -bool MemorySnapshotMac::Read(Delegate* delegate) const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - - if (size_ == 0) { - return delegate->MemorySnapshotDelegateRead(nullptr, size_); - } - - std::unique_ptr buffer(new uint8_t[size_]); - if (!process_reader_->Memory()->Read(address_, size_, buffer.get())) { - return false; - } - return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); -} - -const MemorySnapshot* MemorySnapshotMac::MergeWithOtherSnapshot( - const MemorySnapshot* other) const { - return MergeWithOtherSnapshotImpl(this, other); -} - -} // namespace internal -} // namespace crashpad diff --git a/snapshot/mac/thread_snapshot_mac.h b/snapshot/mac/thread_snapshot_mac.h index 28334434..03ca0fb5 100644 --- a/snapshot/mac/thread_snapshot_mac.h +++ b/snapshot/mac/thread_snapshot_mac.h @@ -21,8 +21,9 @@ #include "base/macros.h" #include "build/build_config.h" #include "snapshot/cpu_context.h" -#include "snapshot/mac/memory_snapshot_mac.h" +#include "snapshot/mac/process_reader.h" #include "snapshot/memory_snapshot.h" +#include "snapshot/memory_snapshot_generic.h" #include "snapshot/thread_snapshot.h" #include "util/misc/initialization_state_dcheck.h" @@ -69,7 +70,7 @@ class ThreadSnapshotMac final : public ThreadSnapshot { } context_union_; #endif CPUContext context_; - MemorySnapshotMac stack_; + MemorySnapshotGeneric stack_; uint64_t thread_id_; uint64_t thread_specific_data_address_; thread_t thread_; diff --git a/snapshot/mac/memory_snapshot_mac.h b/snapshot/memory_snapshot_generic.h similarity index 50% rename from snapshot/mac/memory_snapshot_mac.h rename to snapshot/memory_snapshot_generic.h index f06fbf47..402e913e 100644 --- a/snapshot/mac/memory_snapshot_mac.h +++ b/snapshot/memory_snapshot_generic.h @@ -12,26 +12,29 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CRASHPAD_SNAPSHOT_MAC_MEMORY_SNAPSHOT_MAC_H_ -#define CRASHPAD_SNAPSHOT_MAC_MEMORY_SNAPSHOT_MAC_H_ +#ifndef CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_GENERIC_H_ +#define CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_GENERIC_H_ #include #include #include "base/macros.h" -#include "snapshot/mac/process_reader.h" #include "snapshot/memory_snapshot.h" +#include "util/misc/address_types.h" #include "util/misc/initialization_state_dcheck.h" +#include "util/process/process_memory.h" namespace crashpad { namespace internal { //! \brief A MemorySnapshot of a memory region in a process on the running -//! system, when the system runs macOS. -class MemorySnapshotMac final : public MemorySnapshot { +//! system. Used on Mac, Linux, Android, and Fuchsia, templated on the +//! platform-specific ProcessReader type. +template +class MemorySnapshotGeneric final : public MemorySnapshot { public: - MemorySnapshotMac(); - ~MemorySnapshotMac() override; + MemorySnapshotGeneric() = default; + ~MemorySnapshotGeneric() = default; //! \brief Initializes the object. //! @@ -43,17 +46,46 @@ class MemorySnapshotMac final : public MemorySnapshot { //! \param[in] address The base address of the memory region to snapshot, in //! the snapshot process’ address space. //! \param[in] size The size of the memory region to snapshot. - void Initialize(ProcessReader* process_reader, - uint64_t address, - uint64_t size); + void Initialize(ProcessReaderType* process_reader, + VMAddress address, + VMSize size) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + process_reader_ = process_reader; + address_ = address; + size_ = size; + INITIALIZATION_STATE_SET_VALID(initialized_); + } // MemorySnapshot: - uint64_t Address() const override; - size_t Size() const override; - bool Read(Delegate* delegate) const override; + uint64_t Address() const override { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return address_; + } + + size_t Size() const override { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return size_; + } + + bool Read(Delegate* delegate) const override { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (size_ == 0) { + return delegate->MemorySnapshotDelegateRead(nullptr, size_); + } + + std::unique_ptr buffer(new uint8_t[size_]); + if (!process_reader_->Memory()->Read(address_, size_, buffer.get())) { + return false; + } + return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); + } + const MemorySnapshot* MergeWithOtherSnapshot( - const MemorySnapshot* other) const override; + const MemorySnapshot* other) const override { + return MergeWithOtherSnapshotImpl(this, other); + } private: template @@ -61,15 +93,15 @@ class MemorySnapshotMac final : public MemorySnapshot { const T* self, const MemorySnapshot* other); - ProcessReader* process_reader_; // weak + ProcessReaderType* process_reader_; // weak uint64_t address_; uint64_t size_; InitializationStateDcheck initialized_; - DISALLOW_COPY_AND_ASSIGN(MemorySnapshotMac); + DISALLOW_COPY_AND_ASSIGN(MemorySnapshotGeneric); }; } // namespace internal } // namespace crashpad -#endif // CRASHPAD_SNAPSHOT_MAC_MEMORY_SNAPSHOT_MAC_H_ +#endif // CRASHPAD_SNAPSHOT_GENERIC_MEMORY_SNAPSHOT_GENERIC_H_ diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index f3364d13..15fc34b3 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -60,8 +60,6 @@ 'linux/debug_rendezvous.h', 'linux/exception_snapshot_linux.cc', 'linux/exception_snapshot_linux.h', - 'linux/memory_snapshot_linux.cc', - 'linux/memory_snapshot_linux.h', 'linux/process_reader.cc', 'linux/process_reader.h', 'linux/process_snapshot_linux.cc', @@ -83,8 +81,6 @@ 'mac/mach_o_image_segment_reader.h', 'mac/mach_o_image_symbol_table_reader.cc', 'mac/mach_o_image_symbol_table_reader.h', - 'mac/memory_snapshot_mac.cc', - 'mac/memory_snapshot_mac.h', 'mac/module_snapshot_mac.cc', 'mac/module_snapshot_mac.h', 'mac/process_reader.cc', @@ -110,6 +106,7 @@ 'mac/thread_snapshot_mac.h', 'memory_snapshot.cc', 'memory_snapshot.h', + 'memory_snapshot_generic.h', 'minidump/minidump_annotation_reader.cc', 'minidump/minidump_annotation_reader.h', 'minidump/minidump_simple_string_dictionary_reader.cc', From 3030ae54171aa20f316bc35c7603ef4ac2b6cb86 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 22 Feb 2018 10:01:18 -0800 Subject: [PATCH 191/326] fuchsia: Fix ninja auto-regen after run After the avoidance of abspath(), automatic regeneration of ninja files was broken following a test run. The problem is that the --runtime-deps-list-file argument gets saved into the regeneration rule, but it's relative to the cwd. The cwd is CRASHPAD_DIR on the first run, but the binary_dir on regenerations, so either way it doesn't work (this should probably fixed in either GN or ninja). We could abspath the path the runtime deps targets file to avoid this. However, it's a bit cluttery to have that --runtime-deps-list-file in the regeneration rule anyway, when really it's only required to extract runtime deps at test-running time. (Also, if you happened to delete only targets.txt from the out dir, the regeneration would mysteriously fail.) So since generation only takes tens of milliseconds, the best thing to do is just remove it from the regeneration rule by re-running gn gen without the flag after we've extracted the .runtime_deps to prepare for the run. Bug: crashpad:196, chromium:814816 Change-Id: I009851d8b821fef5c953d463ba9c4880e5cc082a Reviewed-on: https://chromium-review.googlesource.com/929887 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- build/run_tests.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build/run_tests.py b/build/run_tests.py index 6e6c9108..efd0aa46 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -319,6 +319,11 @@ def _GenerateFuchsiaRuntimeDepsFiles(binary_dir, tests): [gn_path, '--root=' + CRASHPAD_DIR, 'gen', binary_dir, '--runtime-deps-list-file=' + targets_file]) + # Run again so that --runtime-deps-list-file isn't in the regen rule. See + # https://crbug.com/814816. + subprocess.check_call( + [gn_path, '--root=' + CRASHPAD_DIR, 'gen', binary_dir]) + def _HandleOutputFromFuchsiaLogListener(process, done_message): """Pass through the output from |process| (which should be an instance of From 61f1013ee4ef6275c00622957aa4c4ff6bcf22dd Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 22 Feb 2018 11:06:35 -0800 Subject: [PATCH 192/326] fuchsia: Add some thread reading to ProcessReader and a test This fills out Threads() in ProcessReader, gathering some information for which there's system calls, and adds some basic tests for ProcessReader on Fuchsia. Bug: crashpad:196 Change-Id: I0738e77121c90a8b883267c1df0fcfc6621674d7 Reviewed-on: https://chromium-review.googlesource.com/929350 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai --- snapshot/BUILD.gn | 4 + snapshot/fuchsia/process_reader.cc | 98 +++++++++++++++++++++++++ snapshot/fuchsia/process_reader.h | 30 ++++++++ snapshot/fuchsia/process_reader_test.cc | 95 ++++++++++++++++++++++++ 4 files changed, 227 insertions(+) create mode 100644 snapshot/fuchsia/process_reader_test.cc diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index b613774d..53311b88 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -341,6 +341,10 @@ source_set("snapshot_test") { sources += [ "posix/timezone_test.cc" ] } + if (crashpad_is_fuchsia) { + sources += [ "fuchsia/process_reader_test.cc" ] + } + # public_configs isn’t quite right. snapshot_test_link sets ldflags, and # what’s really needed is a way to push ldflags to dependent targets that # produce linker output. Luckily in this case, all dependents do produce diff --git a/snapshot/fuchsia/process_reader.cc b/snapshot/fuchsia/process_reader.cc index ffd81635..be9fa775 100644 --- a/snapshot/fuchsia/process_reader.cc +++ b/snapshot/fuchsia/process_reader.cc @@ -17,12 +17,20 @@ #include #include +#include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/scoped_zx_handle.h" +#include "base/logging.h" + namespace crashpad { ProcessReader::Module::Module() = default; ProcessReader::Module::~Module() = default; +ProcessReader::Thread::Thread() = default; + +ProcessReader::Thread::~Thread() = default; + ProcessReader::ProcessReader() = default; ProcessReader::~ProcessReader() = default; @@ -49,6 +57,16 @@ const std::vector& ProcessReader::Modules() { return modules_; } +const std::vector& ProcessReader::Threads() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!initialized_threads_) { + InitializeThreads(); + } + + return threads_; +} + void ProcessReader::InitializeModules() { DCHECK(!initialized_modules_); DCHECK(modules_.empty()); @@ -163,4 +181,84 @@ void ProcessReader::InitializeModules() { } } +void ProcessReader::InitializeThreads() { + DCHECK(!initialized_threads_); + DCHECK(threads_.empty()); + + initialized_threads_ = true; + + // Retrieve the thread koids. This is racy; better if the process is suspended + // itself, but threads could still be externally created. As there's no + // maximum, this needs to be retried in a loop until the actual threads + // retrieved is equal to the available threads. + + std::vector threads(100); + size_t actual_num_threads, available_num_threads; + for (;;) { + zx_status_t status = zx_object_get_info(process_, + ZX_INFO_PROCESS_THREADS, + &threads[0], + sizeof(threads[0]) * threads.size(), + &actual_num_threads, + &available_num_threads); + // If the buffer is too small (even zero), the result is still ZX_OK, not + // ZX_ERR_BUFFER_TOO_SMALL. + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_object_get_info ZX_INFO_PROCESS_THREADS"; + break; + } + if (actual_num_threads == available_num_threads) { + threads.resize(actual_num_threads); + break; + } + + // Resize to the expected number next time with a bit extra to attempt to + // handle the race between here and the next request. + threads.resize(available_num_threads + 10); + } + + for (const zx_koid_t thread_koid : threads) { + zx_handle_t raw_handle; + zx_status_t status = zx_object_get_child( + process_, thread_koid, ZX_RIGHT_SAME_RIGHTS, &raw_handle); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_object_get_child"; + // TODO(scottmg): Decide if it's worthwhile adding a mostly-empty Thread + // here, consisting only of the koid, but no other information. The only + // time this is expected to happen is when there's a race between getting + // the koid above, and requesting the handle here. + continue; + } + + base::ScopedZxHandle thread_handle(raw_handle); + + Thread thread; + thread.id = thread_koid; + + char name[ZX_MAX_NAME_LEN] = {0}; + status = zx_object_get_property( + thread_handle.get(), ZX_PROP_NAME, &name, sizeof(name)); + if (status != ZX_OK) { + ZX_LOG(WARNING, status) << "zx_object_get_property ZX_PROP_NAME"; + } else { + thread.name.assign(name); + } + + zx_info_thread_t thread_info; + status = zx_object_get_info(thread_handle.get(), + ZX_INFO_THREAD, + &thread_info, + sizeof(thread_info), + nullptr, + nullptr); + if (status != ZX_OK) { + ZX_LOG(WARNING, status) << "zx_object_get_info ZX_INFO_THREAD"; + } else { + thread.state = thread_info.state; + } + + threads_.push_back(thread); + } +} + } // namespace crashpad diff --git a/snapshot/fuchsia/process_reader.h b/snapshot/fuchsia/process_reader.h index b3b24e36..0e83fba7 100644 --- a/snapshot/fuchsia/process_reader.h +++ b/snapshot/fuchsia/process_reader.h @@ -54,6 +54,22 @@ class ProcessReader { ModuleSnapshot::ModuleType type = ModuleSnapshot::kModuleTypeUnknown; }; + //! \brief Contains information about a thread that belongs to a process. + struct Thread { + Thread(); + ~Thread(); + + //! \brief The kernel identifier for the thread. + zx_koid_t id = ZX_KOID_INVALID; + + //! \brief The state of the thread, the `ZX_THREAD_STATE_*` value or `-1` if + //! the value could not be retrieved. + uint32_t state = -1; + + //! \brief The `ZX_PROP_NAME` property of the thread. This may be empty. + std::string name; + }; + ProcessReader(); ~ProcessReader(); @@ -72,15 +88,29 @@ class ProcessReader { //! `0`) corresponds to the main executable. const std::vector& Modules(); + //! \return The threads that are in the process. + const std::vector& Threads(); + + //! \brief Return a memory reader for the target process. + ProcessMemory* Memory() { return process_memory_.get(); } + private: + //! Performs lazy initialization of the \a modules_ vector on behalf of + //! Modules(). void InitializeModules(); + //! Performs lazy initialization of the \a threads_ vector on behalf of + //! Threads(). + void InitializeThreads(); + std::vector modules_; + std::vector threads_; std::vector> module_readers_; std::vector> process_memory_ranges_; std::unique_ptr process_memory_; zx_handle_t process_; bool initialized_modules_ = false; + bool initialized_threads_ = false; InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ProcessReader); diff --git a/snapshot/fuchsia/process_reader_test.cc b/snapshot/fuchsia/process_reader_test.cc new file mode 100644 index 00000000..8a89b97a --- /dev/null +++ b/snapshot/fuchsia/process_reader_test.cc @@ -0,0 +1,95 @@ +// Copyright 2018 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/fuchsia/process_reader.h" + +#include +#include + +#include "gtest/gtest.h" +#include "test/multiprocess_exec.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(ProcessReader, SelfBasic) { + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(zx_process_self())); + + static constexpr char kTestMemory[] = "Some test memory"; + char buffer[arraysize(kTestMemory)]; + ASSERT_TRUE(process_reader.Memory()->Read( + reinterpret_cast(kTestMemory), sizeof(kTestMemory), &buffer)); + EXPECT_STREQ(kTestMemory, buffer); + + const auto& modules = process_reader.Modules(); + EXPECT_GT(modules.size(), 0u); + for (const auto& module : modules) { + EXPECT_FALSE(module.name.empty()); + EXPECT_NE(module.type, ModuleSnapshot::kModuleTypeUnknown); + } + + const auto& threads = process_reader.Threads(); + EXPECT_GT(threads.size(), 0u); + + zx_info_handle_basic_t info; + ASSERT_EQ(zx_object_get_info(zx_thread_self(), + ZX_INFO_HANDLE_BASIC, + &info, + sizeof(info), + nullptr, + nullptr), + ZX_OK); + EXPECT_EQ(threads[0].id, info.koid); + EXPECT_EQ(threads[0].state, ZX_THREAD_STATE_RUNNING); + EXPECT_EQ(threads[0].name, "initial-thread"); +} + +constexpr char kTestMemory[] = "Read me from another process"; + +CRASHPAD_CHILD_TEST_MAIN(ProcessReaderBasicChildTestMain) { + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class BasicChildTest : public MultiprocessExec { + public: + BasicChildTest() : MultiprocessExec() { + SetChildTestMainFunction("ProcessReaderBasicChildTestMain"); + } + ~BasicChildTest() {} + + private: + void MultiprocessParent() override { + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(zx_process_self())); + + std::string read_string; + ASSERT_TRUE(process_reader.Memory()->ReadCString( + reinterpret_cast(kTestMemory), &read_string)); + EXPECT_EQ(read_string, kTestMemory); + } + + DISALLOW_COPY_AND_ASSIGN(BasicChildTest); +}; + +TEST(ProcessReader, ChildBasic) { + BasicChildTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad From c69ba3d5278394d115f2e4124b6a12bf8b504a5e Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 22 Feb 2018 12:45:42 -0800 Subject: [PATCH 193/326] non-win: Add Multiprocess::SetExpectedChildTerminationBuiltinTrap() Bug: crashpad:30 Change-Id: Ide7ad3d0f8b9938f57d183ff3fc73868ce28c02c Reviewed-on: https://chromium-review.googlesource.com/932363 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- test/multiprocess.h | 6 ++++++ test/multiprocess_exec_fuchsia.cc | 4 ++++ test/multiprocess_exec_test.cc | 29 +++++++++++++++++++++++++++++ test/multiprocess_posix.cc | 8 ++++++++ 4 files changed, 47 insertions(+) diff --git a/test/multiprocess.h b/test/multiprocess.h index 1d3ee9b2..aac9288b 100644 --- a/test/multiprocess.h +++ b/test/multiprocess.h @@ -92,6 +92,12 @@ class Multiprocess { //! expected to kill the child. void SetExpectedChildTermination(TerminationReason reason, int code); +#if !defined(OS_WIN) + //! \brief Sets termination reason and code appropriately for a child that + //! terminates via `__builtin_trap()`. + void SetExpectedChildTerminationBuiltinTrap(); +#endif // !OS_WIN + protected: ~Multiprocess(); diff --git a/test/multiprocess_exec_fuchsia.cc b/test/multiprocess_exec_fuchsia.cc index e6306080..7d571f91 100644 --- a/test/multiprocess_exec_fuchsia.cc +++ b/test/multiprocess_exec_fuchsia.cc @@ -86,6 +86,10 @@ void Multiprocess::SetExpectedChildTermination(TerminationReason reason, code_ = code; } +void Multiprocess::SetExpectedChildTerminationBuiltinTrap() { + SetExpectedChildTermination(kTerminationNormal, -1); +} + Multiprocess::~Multiprocess() { delete info_; } diff --git a/test/multiprocess_exec_test.cc b/test/multiprocess_exec_test.cc index 0104b0f3..99a1b01f 100644 --- a/test/multiprocess_exec_test.cc +++ b/test/multiprocess_exec_test.cc @@ -98,6 +98,35 @@ TEST(MultiprocessExec, MultiprocessExecSimpleChildReturnsNonZero) { exec.Run(); }; +#if !defined(OS_WIN) + +CRASHPAD_CHILD_TEST_MAIN(BuiltinTrapChild) { + __builtin_trap(); + return EXIT_SUCCESS; +} + +class TestBuiltinTrapTermination final : public MultiprocessExec { + public: + TestBuiltinTrapTermination() { + SetChildTestMainFunction("BuiltinTrapChild"); + SetExpectedChildTerminationBuiltinTrap(); + } + + ~TestBuiltinTrapTermination() = default; + + private: + void MultiprocessParent() override {} + + DISALLOW_COPY_AND_ASSIGN(TestBuiltinTrapTermination); +}; + +TEST(MultiprocessExec, BuiltinTrapTermination) { + TestBuiltinTrapTermination test; + test.Run(); +} + +#endif // !OS_WIN + } // namespace } // namespace test } // namespace crashpad diff --git a/test/multiprocess_posix.cc b/test/multiprocess_posix.cc index c638b48a..96b8ad26 100644 --- a/test/multiprocess_posix.cc +++ b/test/multiprocess_posix.cc @@ -157,6 +157,14 @@ void Multiprocess::SetExpectedChildTermination(TerminationReason reason, code_ = code; } +void Multiprocess::SetExpectedChildTerminationBuiltinTrap() { +#if defined(ARCH_CPU_ARM64) + SetExpectedChildTermination(kTerminationSignal, SIGTRAP); +#else + SetExpectedChildTermination(kTerminationSignal, SIGILL); +#endif +} + Multiprocess::~Multiprocess() { } From f130822b9f4b99cb7d6aa24947b522c2ef6a78d2 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 22 Feb 2018 13:29:05 -0800 Subject: [PATCH 194/326] linux: Add CrashpadClient tests Bug: crashpad:30 Change-Id: Ie2bea049d8c47c09e53e76601ed45817591f2e28 Reviewed-on: https://chromium-review.googlesource.com/927795 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- client/BUILD.gn | 4 + client/client_test.gyp | 11 +- client/crashpad_client_linux_test.cc | 303 +++++++++++++++++++++++++++ 3 files changed, 315 insertions(+), 3 deletions(-) create mode 100644 client/crashpad_client_linux_test.cc diff --git a/client/BUILD.gn b/client/BUILD.gn index 9a2f66ea..cb4161f5 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -107,6 +107,10 @@ source_set("client_test") { sources += [ "crashpad_client_win_test.cc" ] } + if (crashpad_is_linux || crashpad_is_android) { + sources += [ "crashpad_client_linux_test.cc" ] + } + deps = [ ":client", "../compat", diff --git a/client/client_test.gyp b/client/client_test.gyp index ab2626a0..61a4a7e5 100644 --- a/client/client_test.gyp +++ b/client/client_test.gyp @@ -39,6 +39,7 @@ 'annotation_list_test.cc', 'crash_report_database_test.cc', 'crashpad_client_win_test.cc', + 'crashpad_client_linux_test.cc', 'prune_crash_reports_test.cc', 'settings_test.cc', 'simple_address_range_bag_test.cc', @@ -51,9 +52,13 @@ '../handler/handler.gyp:crashpad_handler_console', ], }], - ['OS=="linux" or OS=="android"', - {'dependencies!': ['../handler/handler.gyp:crashpad_handler']}, - ], + ], + 'target_conditions': [ + ['OS=="android"', { + 'sources/': [ + ['include', '^crashpad_client_linux_test\\.cc$'], + ], + }], ], }, ], diff --git a/client/crashpad_client_linux_test.cc b/client/crashpad_client_linux_test.cc new file mode 100644 index 00000000..1e07394a --- /dev/null +++ b/client/crashpad_client_linux_test.cc @@ -0,0 +1,303 @@ +// Copyright 2018 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 "client/crashpad_client.h" + +#include +#include +#include +#include +#include + +#include "base/logging.h" +#include "client/crash_report_database.h" +#include "client/simulate_crash.h" +#include "gtest/gtest.h" +#include "test/multiprocess_exec.h" +#include "test/multiprocess.h" +#include "test/scoped_temp_dir.h" +#include "test/test_paths.h" +#include "util/file/file_io.h" +#include "util/linux/exception_handler_client.h" +#include "util/linux/exception_information.h" +#include "util/misc/address_types.h" +#include "util/misc/from_pointer_cast.h" +#include "util/posix/signals.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(CrashpadClient, SimulateCrash) { + ScopedTempDir temp_dir; + + base::FilePath handler_path = TestPaths::Executable().DirName().Append( + FILE_PATH_LITERAL("crashpad_handler")); + + crashpad::CrashpadClient client; + ASSERT_TRUE(client.StartHandlerAtCrash(handler_path, + base::FilePath(temp_dir.path()), + base::FilePath(), + "", + std::map(), + std::vector())); + + CRASHPAD_SIMULATE_CRASH(); + + auto database = + CrashReportDatabase::InitializeWithoutCreating(temp_dir.path()); + ASSERT_TRUE(database); + + std::vector reports; + ASSERT_EQ(database->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + EXPECT_EQ(reports.size(), 0u); + + reports.clear(); + ASSERT_EQ(database->GetCompletedReports(&reports), + CrashReportDatabase::kNoError); + EXPECT_EQ(reports.size(), 1u); +} + +CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) { + FileHandle in = StdioFileHandle(StdioStream::kStandardInput); + + VMSize temp_dir_length; + CheckedReadFileExactly(in, &temp_dir_length, sizeof(temp_dir_length)); + + std::string temp_dir(temp_dir_length, '\0'); + CheckedReadFileExactly(in, &temp_dir[0], temp_dir_length); + + base::FilePath handler_path = TestPaths::Executable().DirName().Append( + FILE_PATH_LITERAL("crashpad_handler")); + + crashpad::CrashpadClient client; + if (!client.StartHandlerAtCrash(handler_path, + base::FilePath(temp_dir), + base::FilePath(), + "", + std::map(), + std::vector())) { + return EXIT_FAILURE; + } + + __builtin_trap(); + + NOTREACHED(); + return EXIT_SUCCESS; +} + +class StartHandlerAtCrashTest : public MultiprocessExec { + public: + StartHandlerAtCrashTest() : MultiprocessExec() { + SetChildTestMainFunction("StartHandlerAtCrashChild"); + SetExpectedChildTerminationBuiltinTrap(); + } + + private: + void MultiprocessParent() override { + ScopedTempDir temp_dir; + VMSize temp_dir_length = temp_dir.path().value().size(); + ASSERT_TRUE(LoggingWriteFile( + WritePipeHandle(), &temp_dir_length, sizeof(temp_dir_length))); + ASSERT_TRUE(LoggingWriteFile( + WritePipeHandle(), temp_dir.path().value().data(), temp_dir_length)); + + // Wait for child to finish. + CheckedReadFileAtEOF(ReadPipeHandle()); + + auto database = CrashReportDatabase::Initialize(temp_dir.path()); + ASSERT_TRUE(database); + + std::vector reports; + ASSERT_EQ(database->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + EXPECT_EQ(reports.size(), 0u); + + ASSERT_EQ(database->GetCompletedReports(&reports), + CrashReportDatabase::kNoError); + EXPECT_EQ(reports.size(), 1u); + } + + DISALLOW_COPY_AND_ASSIGN(StartHandlerAtCrashTest); +}; + +TEST(CrashpadClient, StartHandlerAtCrash) { + StartHandlerAtCrashTest test; + test.Run(); +} + +// Test state for starting the handler for another process. +class StartHandlerForClientTest { + public: + StartHandlerForClientTest() = default; + ~StartHandlerForClientTest() = default; + + bool Initialize() { + int socks[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) != 0) { + PLOG(ERROR) << "socketpair"; + return false; + } + client_sock_.reset(socks[0]); + server_sock_.reset(socks[1]); + + return true; + } + + bool StartHandlerOnDemand() { + char c; + if (!LoggingReadFileExactly(server_sock_.get(), &c, sizeof(c))) { + ADD_FAILURE(); + return false; + } + + base::FilePath handler_path = TestPaths::Executable().DirName().Append( + FILE_PATH_LITERAL("crashpad_handler")); + + CrashpadClient client; + if (!client.StartHandlerForClient(handler_path, + temp_dir_.path(), + base::FilePath(), + "", + std::map(), + std::vector(), + server_sock_.get())) { + ADD_FAILURE(); + return false; + } + + return true; + } + + void ExpectReport() { + auto database = + CrashReportDatabase::InitializeWithoutCreating(temp_dir_.path()); + ASSERT_TRUE(database); + + std::vector reports; + ASSERT_EQ(database->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + EXPECT_EQ(reports.size(), 0u); + + ASSERT_EQ(database->GetCompletedReports(&reports), + CrashReportDatabase::kNoError); + EXPECT_EQ(reports.size(), 1u); + } + + bool InstallHandler() { + auto signal_handler = SandboxedHandler::Get(); + return signal_handler->Initialize(client_sock_.get()); + } + + private: + // A signal handler that defers handler process startup to another, presumably + // more privileged, process. + class SandboxedHandler { + public: + static SandboxedHandler* Get() { + static SandboxedHandler* instance = new SandboxedHandler(); + return instance; + } + + bool Initialize(FileHandle client_sock) { + client_sock_ = client_sock; + return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr); + } + + private: + SandboxedHandler() = default; + ~SandboxedHandler() = delete; + + static void HandleCrash(int signo, siginfo_t* siginfo, void* context) { + auto state = Get(); + + char c; + CHECK(LoggingWriteFile(state->client_sock_, &c, sizeof(c))); + + ExceptionInformation exception_information; + exception_information.siginfo_address = + FromPointerCast( + siginfo); + exception_information.context_address = + FromPointerCast( + context); + exception_information.thread_id = syscall(SYS_gettid); + + ClientInformation info = {}; + info.exception_information_address = + FromPointerCast( + &exception_information); + + ExceptionHandlerClient handler_client(state->client_sock_); + CHECK_EQ(handler_client.RequestCrashDump(info), 0); + + Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr); + } + + FileHandle client_sock_; + + DISALLOW_COPY_AND_ASSIGN(SandboxedHandler); + }; + + ScopedTempDir temp_dir_; + ScopedFileHandle client_sock_; + ScopedFileHandle server_sock_; + + DISALLOW_COPY_AND_ASSIGN(StartHandlerForClientTest); +}; + +// Tests starting the handler for a child process. +class StartHandlerForChildTest : public Multiprocess { + public: + StartHandlerForChildTest() = default; + ~StartHandlerForChildTest() = default; + + bool Initialize() { + SetExpectedChildTerminationBuiltinTrap(); + return test_state_.Initialize(); + } + + private: + void MultiprocessParent() { + ASSERT_TRUE(test_state_.StartHandlerOnDemand()); + + // Wait for chlid to finish. + CheckedReadFileAtEOF(ReadPipeHandle()); + + test_state_.ExpectReport(); + } + + void MultiprocessChild() { + CHECK(test_state_.InstallHandler()); + + __builtin_trap(); + + NOTREACHED(); + } + + StartHandlerForClientTest test_state_; + + DISALLOW_COPY_AND_ASSIGN(StartHandlerForChildTest); +}; + +TEST(CrashpadClient, StartHandlerForChild) { + StartHandlerForChildTest test; + ASSERT_TRUE(test.Initialize()); + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad From 2b05eb522fa43501179b31893fb18f8f2ea222e2 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 22 Feb 2018 12:12:26 -0800 Subject: [PATCH 195/326] Rename ProcessReader to platform-suffixed versions Mac's ProcessReader becomes ProcessReaderMac. Linux/Android's ProcessReader becomes ProcessReaderLinux. Fuchsia's ProcessReader becomes ProcessReaderFuchsia. No intended change in behavior. Bug: crashpad:196, crashpad:30 Change-Id: I7ec8d72f79533bd78189173261ade2ad99010bad Reviewed-on: https://chromium-review.googlesource.com/930321 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- snapshot/BUILD.gn | 18 +- ...ss_reader.cc => process_reader_fuchsia.cc} | 28 +-- ...cess_reader.h => process_reader_fuchsia.h} | 10 +- ...test.cc => process_reader_fuchsia_test.cc} | 10 +- snapshot/fuchsia/process_snapshot_fuchsia.cc | 3 +- snapshot/fuchsia/process_snapshot_fuchsia.h | 4 +- snapshot/linux/exception_snapshot_linux.cc | 14 +- snapshot/linux/exception_snapshot_linux.h | 11 +- .../linux/exception_snapshot_linux_test.cc | 8 +- ...cess_reader.cc => process_reader_linux.cc} | 34 ++-- ...rocess_reader.h => process_reader_linux.h} | 20 +-- ...r_test.cc => process_reader_linux_test.cc} | 50 +++--- snapshot/linux/process_snapshot_linux.cc | 7 +- snapshot/linux/process_snapshot_linux.h | 4 +- snapshot/linux/system_snapshot_linux.cc | 2 +- snapshot/linux/system_snapshot_linux.h | 13 +- snapshot/linux/system_snapshot_linux_test.cc | 4 +- snapshot/linux/thread_snapshot_linux.cc | 5 +- snapshot/linux/thread_snapshot_linux.h | 14 +- snapshot/mac/exception_snapshot_mac.cc | 9 +- snapshot/mac/exception_snapshot_mac.h | 8 +- .../mac/mach_o_image_annotations_reader.cc | 7 +- .../mac/mach_o_image_annotations_reader.h | 6 +- .../mach_o_image_annotations_reader_test.cc | 14 +- snapshot/mac/mach_o_image_reader.cc | 4 +- snapshot/mac/mach_o_image_reader.h | 6 +- snapshot/mac/mach_o_image_reader_test.cc | 6 +- snapshot/mac/mach_o_image_segment_reader.cc | 4 +- snapshot/mac/mach_o_image_segment_reader.h | 2 +- .../mac/mach_o_image_symbol_table_reader.cc | 6 +- .../mac/mach_o_image_symbol_table_reader.h | 4 +- snapshot/mac/module_snapshot_mac.cc | 4 +- snapshot/mac/module_snapshot_mac.h | 14 +- ...rocess_reader.cc => process_reader_mac.cc} | 59 +++---- ...{process_reader.h => process_reader_mac.h} | 16 +- ...der_test.cc => process_reader_mac_test.cc} | 131 +++++++------- snapshot/mac/process_snapshot_mac.cc | 8 +- snapshot/mac/process_snapshot_mac.h | 4 +- snapshot/mac/process_types.cc | 52 +++--- snapshot/mac/process_types.h | 160 +++++++++--------- snapshot/mac/process_types/custom.cc | 44 ++--- snapshot/mac/process_types_test.cc | 2 +- snapshot/mac/system_snapshot_mac.cc | 4 +- snapshot/mac/system_snapshot_mac.h | 13 +- snapshot/mac/system_snapshot_mac_test.cc | 6 +- snapshot/mac/thread_snapshot_mac.cc | 6 +- snapshot/mac/thread_snapshot_mac.h | 16 +- snapshot/snapshot.gyp | 8 +- snapshot/snapshot_test.gyp | 4 +- snapshot/win/exception_snapshot_win.h | 4 +- snapshot/win/module_snapshot_win.h | 8 +- test/mac/dyld.cc | 4 +- 52 files changed, 451 insertions(+), 451 deletions(-) rename snapshot/fuchsia/{process_reader.cc => process_reader_fuchsia.cc} (91%) rename snapshot/fuchsia/{process_reader.h => process_reader_fuchsia.h} (95%) rename snapshot/fuchsia/{process_reader_test.cc => process_reader_fuchsia_test.cc} (92%) rename snapshot/linux/{process_reader.cc => process_reader_linux.cc} (91%) rename snapshot/linux/{process_reader.h => process_reader_linux.h} (91%) rename snapshot/linux/{process_reader_test.cc => process_reader_linux_test.cc} (93%) rename snapshot/mac/{process_reader.cc => process_reader_mac.cc} (94%) rename snapshot/mac/{process_reader.h => process_reader_mac.h} (96%) rename snapshot/mac/{process_reader_test.cc => process_reader_mac_test.cc} (88%) diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 53311b88..84df23f1 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -79,8 +79,8 @@ static_library("snapshot") { "mac/mach_o_image_symbol_table_reader.h", "mac/module_snapshot_mac.cc", "mac/module_snapshot_mac.h", - "mac/process_reader.cc", - "mac/process_reader.h", + "mac/process_reader_mac.cc", + "mac/process_reader_mac.h", "mac/process_snapshot_mac.cc", "mac/process_snapshot_mac.h", "mac/process_types.cc", @@ -111,8 +111,8 @@ static_library("snapshot") { "linux/debug_rendezvous.h", "linux/exception_snapshot_linux.cc", "linux/exception_snapshot_linux.h", - "linux/process_reader.cc", - "linux/process_reader.h", + "linux/process_reader_linux.cc", + "linux/process_reader_linux.h", "linux/process_snapshot_linux.cc", "linux/process_snapshot_linux.h", "linux/signal_context.h", @@ -175,8 +175,8 @@ static_library("snapshot") { if (crashpad_is_fuchsia) { sources += [ - "fuchsia/process_reader.cc", - "fuchsia/process_reader.h", + "fuchsia/process_reader_fuchsia.cc", + "fuchsia/process_reader_fuchsia.h", "fuchsia/process_snapshot_fuchsia.cc", "fuchsia/process_snapshot_fuchsia.h", ] @@ -294,7 +294,7 @@ source_set("snapshot_test") { "mac/mach_o_image_annotations_reader_test.cc", "mac/mach_o_image_reader_test.cc", "mac/mach_o_image_segment_reader_test.cc", - "mac/process_reader_test.cc", + "mac/process_reader_mac_test.cc", "mac/process_types_test.cc", "mac/system_snapshot_mac_test.cc", ] @@ -304,7 +304,7 @@ source_set("snapshot_test") { sources += [ "linux/debug_rendezvous_test.cc", "linux/exception_snapshot_linux_test.cc", - "linux/process_reader_test.cc", + "linux/process_reader_linux_test.cc", "linux/system_snapshot_linux_test.cc", ] } else { @@ -342,7 +342,7 @@ source_set("snapshot_test") { } if (crashpad_is_fuchsia) { - sources += [ "fuchsia/process_reader_test.cc" ] + sources += [ "fuchsia/process_reader_fuchsia_test.cc" ] } # public_configs isn’t quite right. snapshot_test_link sets ldflags, and diff --git a/snapshot/fuchsia/process_reader.cc b/snapshot/fuchsia/process_reader_fuchsia.cc similarity index 91% rename from snapshot/fuchsia/process_reader.cc rename to snapshot/fuchsia/process_reader_fuchsia.cc index be9fa775..1ace0d13 100644 --- a/snapshot/fuchsia/process_reader.cc +++ b/snapshot/fuchsia/process_reader_fuchsia.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "snapshot/fuchsia/process_reader.h" +#include "snapshot/fuchsia/process_reader_fuchsia.h" #include #include @@ -23,19 +23,19 @@ namespace crashpad { -ProcessReader::Module::Module() = default; +ProcessReaderFuchsia::Module::Module() = default; -ProcessReader::Module::~Module() = default; +ProcessReaderFuchsia::Module::~Module() = default; -ProcessReader::Thread::Thread() = default; +ProcessReaderFuchsia::Thread::Thread() = default; -ProcessReader::Thread::~Thread() = default; +ProcessReaderFuchsia::Thread::~Thread() = default; -ProcessReader::ProcessReader() = default; +ProcessReaderFuchsia::ProcessReaderFuchsia() = default; -ProcessReader::~ProcessReader() = default; +ProcessReaderFuchsia::~ProcessReaderFuchsia() = default; -bool ProcessReader::Initialize(zx_handle_t process) { +bool ProcessReaderFuchsia::Initialize(zx_handle_t process) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); process_ = process; @@ -47,7 +47,8 @@ bool ProcessReader::Initialize(zx_handle_t process) { return true; } -const std::vector& ProcessReader::Modules() { +const std::vector& +ProcessReaderFuchsia::Modules() { INITIALIZATION_STATE_DCHECK_VALID(initialized_); if (!initialized_modules_) { @@ -57,7 +58,8 @@ const std::vector& ProcessReader::Modules() { return modules_; } -const std::vector& ProcessReader::Threads() { +const std::vector& +ProcessReaderFuchsia::Threads() { INITIALIZATION_STATE_DCHECK_VALID(initialized_); if (!initialized_threads_) { @@ -67,7 +69,7 @@ const std::vector& ProcessReader::Threads() { return threads_; } -void ProcessReader::InitializeModules() { +void ProcessReaderFuchsia::InitializeModules() { DCHECK(!initialized_modules_); DCHECK(modules_.empty()); @@ -181,7 +183,7 @@ void ProcessReader::InitializeModules() { } } -void ProcessReader::InitializeThreads() { +void ProcessReaderFuchsia::InitializeThreads() { DCHECK(!initialized_threads_); DCHECK(threads_.empty()); @@ -220,7 +222,7 @@ void ProcessReader::InitializeThreads() { for (const zx_koid_t thread_koid : threads) { zx_handle_t raw_handle; zx_status_t status = zx_object_get_child( - process_, thread_koid, ZX_RIGHT_SAME_RIGHTS, &raw_handle); + process_, thread_koid, ZX_RIGHT_SAME_RIGHTS, &raw_handle); if (status != ZX_OK) { ZX_LOG(ERROR, status) << "zx_object_get_child"; // TODO(scottmg): Decide if it's worthwhile adding a mostly-empty Thread diff --git a/snapshot/fuchsia/process_reader.h b/snapshot/fuchsia/process_reader_fuchsia.h similarity index 95% rename from snapshot/fuchsia/process_reader.h rename to snapshot/fuchsia/process_reader_fuchsia.h index 0e83fba7..d0811cfe 100644 --- a/snapshot/fuchsia/process_reader.h +++ b/snapshot/fuchsia/process_reader_fuchsia.h @@ -30,7 +30,7 @@ namespace crashpad { //! \brief Accesses information about another process, identified by a Fuchsia //! process. -class ProcessReader { +class ProcessReaderFuchsia { public: //! \brief Contains information about a module loaded into a process. struct Module { @@ -44,7 +44,7 @@ class ProcessReader { //! \brief An image reader for the module. //! //! The lifetime of this ElfImageReader is scoped to the lifetime of the - //! ProcessReader that created it. + //! ProcessReaderFuchsia that created it. //! //! This field may be `nullptr` if a reader could not be created for the //! module. @@ -70,8 +70,8 @@ class ProcessReader { std::string name; }; - ProcessReader(); - ~ProcessReader(); + ProcessReaderFuchsia(); + ~ProcessReaderFuchsia(); //! \brief Initializes this object. This method must be called before any //! other. @@ -113,7 +113,7 @@ class ProcessReader { bool initialized_threads_ = false; InitializationStateDcheck initialized_; - DISALLOW_COPY_AND_ASSIGN(ProcessReader); + DISALLOW_COPY_AND_ASSIGN(ProcessReaderFuchsia); }; } // namespace crashpad diff --git a/snapshot/fuchsia/process_reader_test.cc b/snapshot/fuchsia/process_reader_fuchsia_test.cc similarity index 92% rename from snapshot/fuchsia/process_reader_test.cc rename to snapshot/fuchsia/process_reader_fuchsia_test.cc index 8a89b97a..35af291c 100644 --- a/snapshot/fuchsia/process_reader_test.cc +++ b/snapshot/fuchsia/process_reader_fuchsia_test.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "snapshot/fuchsia/process_reader.h" +#include "snapshot/fuchsia/process_reader_fuchsia.h" #include #include @@ -24,8 +24,8 @@ namespace crashpad { namespace test { namespace { -TEST(ProcessReader, SelfBasic) { - ProcessReader process_reader; +TEST(ProcessReaderFuchsia, SelfBasic) { + ProcessReaderFuchsia process_reader; ASSERT_TRUE(process_reader.Initialize(zx_process_self())); static constexpr char kTestMemory[] = "Some test memory"; @@ -73,7 +73,7 @@ class BasicChildTest : public MultiprocessExec { private: void MultiprocessParent() override { - ProcessReader process_reader; + ProcessReaderFuchsia process_reader; ASSERT_TRUE(process_reader.Initialize(zx_process_self())); std::string read_string; @@ -85,7 +85,7 @@ class BasicChildTest : public MultiprocessExec { DISALLOW_COPY_AND_ASSIGN(BasicChildTest); }; -TEST(ProcessReader, ChildBasic) { +TEST(ProcessReaderFuchsia, ChildBasic) { BasicChildTest test; test.Run(); } diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc index ce8b0ea8..f3d22907 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -171,7 +171,8 @@ std::vector ProcessSnapshotFuchsia::ExtraMemory() const { } void ProcessSnapshotFuchsia::InitializeModules() { - for (const ProcessReader::Module& reader_module : process_reader_.Modules()) { + for (const ProcessReaderFuchsia::Module& reader_module : + process_reader_.Modules()) { auto module = std::make_unique( reader_module.name, reader_module.reader, reader_module.type); if (module->Initialize()) { diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.h b/snapshot/fuchsia/process_snapshot_fuchsia.h index 04e2d29d..d94dec7b 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.h +++ b/snapshot/fuchsia/process_snapshot_fuchsia.h @@ -24,7 +24,7 @@ #include "snapshot/crashpad_info_client_options.h" #include "snapshot/elf/elf_image_reader.h" #include "snapshot/elf/module_snapshot_elf.h" -#include "snapshot/fuchsia/process_reader.h" +#include "snapshot/fuchsia/process_reader_fuchsia.h" #include "snapshot/process_snapshot.h" #include "snapshot/unloaded_module_snapshot.h" #include "util/misc/initialization_state_dcheck.h" @@ -77,7 +77,7 @@ class ProcessSnapshotFuchsia : public ProcessSnapshot { void InitializeModules(); std::vector> modules_; - ProcessReader process_reader_; + ProcessReaderFuchsia process_reader_; std::map annotations_simple_map_; InitializationStateDcheck initialized_; diff --git a/snapshot/linux/exception_snapshot_linux.cc b/snapshot/linux/exception_snapshot_linux.cc index 498b1f79..fa2e8f94 100644 --- a/snapshot/linux/exception_snapshot_linux.cc +++ b/snapshot/linux/exception_snapshot_linux.cc @@ -18,7 +18,7 @@ #include "base/logging.h" #include "snapshot/linux/cpu_context_linux.h" -#include "snapshot/linux/process_reader.h" +#include "snapshot/linux/process_reader_linux.h" #include "snapshot/linux/signal_context.h" #include "util/linux/traits.h" #include "util/misc/reinterpret_bytes.h" @@ -43,7 +43,7 @@ ExceptionSnapshotLinux::~ExceptionSnapshotLinux() {} #if defined(ARCH_CPU_X86_FAMILY) template <> bool ExceptionSnapshotLinux::ReadContext( - ProcessReader* reader, + ProcessReaderLinux* reader, LinuxVMAddress context_address) { UContext ucontext; if (!reader->Memory()->Read(context_address, sizeof(ucontext), &ucontext)) { @@ -79,7 +79,7 @@ bool ExceptionSnapshotLinux::ReadContext( template <> bool ExceptionSnapshotLinux::ReadContext( - ProcessReader* reader, + ProcessReaderLinux* reader, LinuxVMAddress context_address) { UContext ucontext; if (!reader->Memory()->Read(context_address, sizeof(ucontext), &ucontext)) { @@ -99,7 +99,7 @@ bool ExceptionSnapshotLinux::ReadContext( template <> bool ExceptionSnapshotLinux::ReadContext( - ProcessReader* reader, + ProcessReaderLinux* reader, LinuxVMAddress context_address) { context_.architecture = kCPUArchitectureARM; context_.arm = &context_union_.arm; @@ -179,7 +179,7 @@ bool ExceptionSnapshotLinux::ReadContext( template <> bool ExceptionSnapshotLinux::ReadContext( - ProcessReader* reader, + ProcessReaderLinux* reader, LinuxVMAddress context_address) { context_.architecture = kCPUArchitectureARM64; context_.arm64 = &context_union_.arm64; @@ -253,7 +253,7 @@ bool ExceptionSnapshotLinux::ReadContext( #endif // ARCH_CPU_X86_FAMILY -bool ExceptionSnapshotLinux::Initialize(ProcessReader* process_reader, +bool ExceptionSnapshotLinux::Initialize(ProcessReaderLinux* process_reader, LinuxVMAddress siginfo_address, LinuxVMAddress context_address, pid_t thread_id) { @@ -278,7 +278,7 @@ bool ExceptionSnapshotLinux::Initialize(ProcessReader* process_reader, } template -bool ExceptionSnapshotLinux::ReadSiginfo(ProcessReader* reader, +bool ExceptionSnapshotLinux::ReadSiginfo(ProcessReaderLinux* reader, LinuxVMAddress siginfo_address) { Siginfo siginfo; if (!reader->Memory()->Read(siginfo_address, sizeof(siginfo), &siginfo)) { diff --git a/snapshot/linux/exception_snapshot_linux.h b/snapshot/linux/exception_snapshot_linux.h index 73949668..0dcead7b 100644 --- a/snapshot/linux/exception_snapshot_linux.h +++ b/snapshot/linux/exception_snapshot_linux.h @@ -24,7 +24,7 @@ #include "build/build_config.h" #include "snapshot/cpu_context.h" #include "snapshot/exception_snapshot.h" -#include "snapshot/linux/process_reader.h" +#include "snapshot/linux/process_reader_linux.h" #include "snapshot/memory_snapshot.h" #include "util/linux/address_types.h" #include "util/misc/initialization_state_dcheck.h" @@ -41,7 +41,8 @@ class ExceptionSnapshotLinux final : public ExceptionSnapshot { //! \brief Initializes the object. //! - //! \param[in] process_reader A ProcessReader for the process that received + //! \param[in] process_reader A ProcessReaderLinux for the process that + //! received //! the signal. //! \param[in] siginfo_address The address in the target process' address //! space of the siginfo_t passed to the signal handler. @@ -51,7 +52,7 @@ class ExceptionSnapshotLinux final : public ExceptionSnapshot { //! //! \return `true` if the snapshot could be created, `false` otherwise with //! an appropriate message logged. - bool Initialize(ProcessReader* process_reader, + bool Initialize(ProcessReaderLinux* process_reader, LinuxVMAddress siginfo_address, LinuxVMAddress context_address, pid_t thread_id); @@ -68,10 +69,10 @@ class ExceptionSnapshotLinux final : public ExceptionSnapshot { private: template - bool ReadSiginfo(ProcessReader* reader, LinuxVMAddress siginfo_address); + bool ReadSiginfo(ProcessReaderLinux* reader, LinuxVMAddress siginfo_address); template - bool ReadContext(ProcessReader* reader, LinuxVMAddress context_address); + bool ReadContext(ProcessReaderLinux* reader, LinuxVMAddress context_address); union { #if defined(ARCH_CPU_X86_FAMILY) diff --git a/snapshot/linux/exception_snapshot_linux_test.cc b/snapshot/linux/exception_snapshot_linux_test.cc index 24f0ef52..4add607d 100644 --- a/snapshot/linux/exception_snapshot_linux_test.cc +++ b/snapshot/linux/exception_snapshot_linux_test.cc @@ -25,7 +25,7 @@ #include "base/strings/stringprintf.h" #include "gtest/gtest.h" #include "snapshot/cpu_architecture.h" -#include "snapshot/linux/process_reader.h" +#include "snapshot/linux/process_reader_linux.h" #include "snapshot/linux/signal_context.h" #include "sys/syscall.h" #include "test/errors.h" @@ -271,7 +271,7 @@ TEST(ExceptionSnapshotLinux, SelfBasic) { FakePtraceConnection connection; ASSERT_TRUE(connection.Initialize(getpid())); - ProcessReader process_reader; + ProcessReaderLinux process_reader; ASSERT_TRUE(process_reader.Initialize(&connection)); siginfo_t siginfo; @@ -348,7 +348,7 @@ class RaiseTest { FakePtraceConnection connection; ASSERT_TRUE(connection.Initialize(getpid())); - ProcessReader process_reader; + ProcessReaderLinux process_reader; ASSERT_TRUE(process_reader.Initialize(&connection)); internal::ExceptionSnapshotLinux exception; @@ -411,7 +411,7 @@ class TimerTest { FakePtraceConnection connection; ASSERT_TRUE(connection.Initialize(getpid())); - ProcessReader process_reader; + ProcessReaderLinux process_reader; ASSERT_TRUE(process_reader.Initialize(&connection)); internal::ExceptionSnapshotLinux exception; diff --git a/snapshot/linux/process_reader.cc b/snapshot/linux/process_reader_linux.cc similarity index 91% rename from snapshot/linux/process_reader.cc rename to snapshot/linux/process_reader_linux.cc index 0f196c63..40b14068 100644 --- a/snapshot/linux/process_reader.cc +++ b/snapshot/linux/process_reader_linux.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "snapshot/linux/process_reader.h" +#include "snapshot/linux/process_reader_linux.h" #include #include @@ -46,7 +46,7 @@ bool ShouldMergeStackMappings(const MemoryMap::Mapping& stack_mapping, } // namespace -ProcessReader::Thread::Thread() +ProcessReaderLinux::Thread::Thread() : thread_info(), stack_region_address(0), stack_region_size(0), @@ -54,9 +54,10 @@ ProcessReader::Thread::Thread() static_priority(-1), nice_value(-1) {} -ProcessReader::Thread::~Thread() {} +ProcessReaderLinux::Thread::~Thread() {} -bool ProcessReader::Thread::InitializePtrace(PtraceConnection* connection) { +bool ProcessReaderLinux::Thread::InitializePtrace( + PtraceConnection* connection) { if (!connection->GetThreadInfo(tid, &thread_info)) { return false; } @@ -89,7 +90,7 @@ bool ProcessReader::Thread::InitializePtrace(PtraceConnection* connection) { return true; } -void ProcessReader::Thread::InitializeStack(ProcessReader* reader) { +void ProcessReaderLinux::Thread::InitializeStack(ProcessReaderLinux* reader) { LinuxVMAddress stack_pointer; #if defined(ARCH_CPU_X86_FAMILY) stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.rsp @@ -169,12 +170,12 @@ void ProcessReader::Thread::InitializeStack(ProcessReader* reader) { } } -ProcessReader::Module::Module() +ProcessReaderLinux::Module::Module() : name(), elf_reader(nullptr), type(ModuleSnapshot::kModuleTypeUnknown) {} -ProcessReader::Module::~Module() = default; +ProcessReaderLinux::Module::~Module() = default; -ProcessReader::ProcessReader() +ProcessReaderLinux::ProcessReaderLinux() : connection_(), process_info_(), memory_map_(), @@ -187,9 +188,9 @@ ProcessReader::ProcessReader() initialized_modules_(false), initialized_() {} -ProcessReader::~ProcessReader() {} +ProcessReaderLinux::~ProcessReaderLinux() {} -bool ProcessReader::Initialize(PtraceConnection* connection) { +bool ProcessReaderLinux::Initialize(PtraceConnection* connection) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); DCHECK(connection); connection_ = connection; @@ -213,12 +214,13 @@ bool ProcessReader::Initialize(PtraceConnection* connection) { return true; } -bool ProcessReader::StartTime(timeval* start_time) const { +bool ProcessReaderLinux::StartTime(timeval* start_time) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return process_info_.StartTime(start_time); } -bool ProcessReader::CPUTimes(timeval* user_time, timeval* system_time) const { +bool ProcessReaderLinux::CPUTimes(timeval* user_time, + timeval* system_time) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); timerclear(user_time); timerclear(system_time); @@ -253,7 +255,7 @@ bool ProcessReader::CPUTimes(timeval* user_time, timeval* system_time) const { return true; } -const std::vector& ProcessReader::Threads() { +const std::vector& ProcessReaderLinux::Threads() { INITIALIZATION_STATE_DCHECK_VALID(initialized_); if (!initialized_threads_) { InitializeThreads(); @@ -261,7 +263,7 @@ const std::vector& ProcessReader::Threads() { return threads_; } -const std::vector& ProcessReader::Modules() { +const std::vector& ProcessReaderLinux::Modules() { INITIALIZATION_STATE_DCHECK_VALID(initialized_); if (!initialized_modules_) { InitializeModules(); @@ -269,7 +271,7 @@ const std::vector& ProcessReader::Modules() { return modules_; } -void ProcessReader::InitializeThreads() { +void ProcessReaderLinux::InitializeThreads() { DCHECK(threads_.empty()); pid_t pid = ProcessID(); @@ -326,7 +328,7 @@ void ProcessReader::InitializeThreads() { DCHECK(main_thread_found); } -void ProcessReader::InitializeModules() { +void ProcessReaderLinux::InitializeModules() { INITIALIZATION_STATE_DCHECK_VALID(initialized_); AuxiliaryVector aux; diff --git a/snapshot/linux/process_reader.h b/snapshot/linux/process_reader_linux.h similarity index 91% rename from snapshot/linux/process_reader.h rename to snapshot/linux/process_reader_linux.h index 59a3c7ef..2e89655e 100644 --- a/snapshot/linux/process_reader.h +++ b/snapshot/linux/process_reader_linux.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_H_ -#define CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_H_ +#ifndef CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_LINUX_H_ #include #include @@ -38,7 +38,7 @@ namespace crashpad { //! \brief Accesses information about another process, identified by a process //! ID. -class ProcessReader { +class ProcessReaderLinux { public: //! \brief Contains information about a thread that belongs to a process. struct Thread { @@ -54,10 +54,10 @@ class ProcessReader { int nice_value; private: - friend class ProcessReader; + friend class ProcessReaderLinux; bool InitializePtrace(PtraceConnection* connection); - void InitializeStack(ProcessReader* reader); + void InitializeStack(ProcessReaderLinux* reader); }; //! \brief Contains information about a module loaded into a process. @@ -71,7 +71,7 @@ class ProcessReader { //! \brief An image reader for the module. //! //! The lifetime of this ElfImageReader is scoped to the lifetime of the - //! ProcessReader that created it. + //! ProcessReaderLinux that created it. //! //! This field may be `nullptr` if a reader could not be created for the //! module. @@ -81,8 +81,8 @@ class ProcessReader { ModuleSnapshot::ModuleType type; }; - ProcessReader(); - ~ProcessReader(); + ProcessReaderLinux(); + ~ProcessReaderLinux(); //! \brief Initializes this object. //! @@ -152,9 +152,9 @@ class ProcessReader { bool initialized_modules_; InitializationStateDcheck initialized_; - DISALLOW_COPY_AND_ASSIGN(ProcessReader); + DISALLOW_COPY_AND_ASSIGN(ProcessReaderLinux); }; } // namespace crashpad -#endif // CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_H_ +#endif // CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_LINUX_H_ diff --git a/snapshot/linux/process_reader_test.cc b/snapshot/linux/process_reader_linux_test.cc similarity index 93% rename from snapshot/linux/process_reader_test.cc rename to snapshot/linux/process_reader_linux_test.cc index a8190b39..11a606a9 100644 --- a/snapshot/linux/process_reader_test.cc +++ b/snapshot/linux/process_reader_linux_test.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "snapshot/linux/process_reader.h" +#include "snapshot/linux/process_reader_linux.h" #include #include @@ -55,11 +55,11 @@ pid_t gettid() { return syscall(SYS_gettid); } -TEST(ProcessReader, SelfBasic) { +TEST(ProcessReaderLinux, SelfBasic) { FakePtraceConnection connection; connection.Initialize(getpid()); - ProcessReader process_reader; + ProcessReaderLinux process_reader; ASSERT_TRUE(process_reader.Initialize(&connection)); #if defined(ARCH_CPU_64_BITS) @@ -92,7 +92,7 @@ class BasicChildTest : public Multiprocess { DirectPtraceConnection connection; ASSERT_TRUE(connection.Initialize(ChildPID())); - ProcessReader process_reader; + ProcessReaderLinux process_reader; ASSERT_TRUE(process_reader.Initialize(&connection)); #if !defined(ARCH_CPU_64_BITS) @@ -115,7 +115,7 @@ class BasicChildTest : public Multiprocess { DISALLOW_COPY_AND_ASSIGN(BasicChildTest); }; -TEST(ProcessReader, ChildBasic) { +TEST(ProcessReaderLinux, ChildBasic) { BasicChildTest test; test.Run(); } @@ -241,7 +241,7 @@ class TestThreadPool { using ThreadMap = std::map; void ExpectThreads(const ThreadMap& thread_map, - const std::vector& threads, + const std::vector& threads, const pid_t pid) { ASSERT_EQ(threads.size(), thread_map.size()); MemoryMap memory_map; @@ -302,9 +302,9 @@ class ChildThreadTest : public Multiprocess { DirectPtraceConnection connection; ASSERT_TRUE(connection.Initialize(ChildPID())); - ProcessReader process_reader; + ProcessReaderLinux process_reader; ASSERT_TRUE(process_reader.Initialize(&connection)); - const std::vector& threads = + const std::vector& threads = process_reader.Threads(); ExpectThreads(thread_map, threads, ChildPID()); } @@ -350,12 +350,12 @@ class ChildThreadTest : public Multiprocess { DISALLOW_COPY_AND_ASSIGN(ChildThreadTest); }; -TEST(ProcessReader, ChildWithThreads) { +TEST(ProcessReaderLinux, ChildWithThreads) { ChildThreadTest test; test.Run(); } -TEST(ProcessReader, ChildThreadsWithSmallUserStacks) { +TEST(ProcessReaderLinux, ChildThreadsWithSmallUserStacks) { ChildThreadTest test(PTHREAD_STACK_MIN); test.Run(); } @@ -379,10 +379,10 @@ class ChildWithSplitStackTest : public Multiprocess { DirectPtraceConnection connection; ASSERT_TRUE(connection.Initialize(ChildPID())); - ProcessReader process_reader; + ProcessReaderLinux process_reader; ASSERT_TRUE(process_reader.Initialize(&connection)); - const std::vector& threads = + const std::vector& threads = process_reader.Threads(); ASSERT_EQ(threads.size(), 1u); @@ -440,7 +440,7 @@ class ChildWithSplitStackTest : public Multiprocess { DISALLOW_COPY_AND_ASSIGN(ChildWithSplitStackTest); }; -TEST(ProcessReader, ChildWithSplitStack) { +TEST(ProcessReaderLinux, ChildWithSplitStack) { ChildWithSplitStackTest test; test.Run(); } @@ -454,7 +454,7 @@ int ExpectFindModule(dl_phdr_info* info, size_t size, void* data) { LinuxVMAddress{info->dlpi_addr}, FromPointerCast(info->dlpi_phdr))); auto modules = - reinterpret_cast*>(data); + reinterpret_cast*>(data); auto phdr_addr = FromPointerCast(info->dlpi_phdr); @@ -482,7 +482,8 @@ int ExpectFindModule(dl_phdr_info* info, size_t size, void* data) { } #endif // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21 -void ExpectModulesFromSelf(const std::vector& modules) { +void ExpectModulesFromSelf( + const std::vector& modules) { for (const auto& module : modules) { EXPECT_FALSE(module.name.empty()); EXPECT_NE(module.type, ModuleSnapshot::kModuleTypeUnknown); @@ -490,19 +491,20 @@ void ExpectModulesFromSelf(const std::vector& modules) { // Android doesn't provide dl_iterate_phdr on ARM until API 21. #if !defined(OS_ANDROID) || !defined(ARCH_CPU_ARMEL) || __ANDROID_API__ >= 21 - EXPECT_EQ(dl_iterate_phdr( - ExpectFindModule, - reinterpret_cast( - const_cast*>(&modules))), - 0); + EXPECT_EQ( + dl_iterate_phdr( + ExpectFindModule, + reinterpret_cast( + const_cast*>(&modules))), + 0); #endif // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21 } -TEST(ProcessReader, SelfModules) { +TEST(ProcessReaderLinux, SelfModules) { FakePtraceConnection connection; connection.Initialize(getpid()); - ProcessReader process_reader; + ProcessReaderLinux process_reader; ASSERT_TRUE(process_reader.Initialize(&connection)); ExpectModulesFromSelf(process_reader.Modules()); @@ -518,7 +520,7 @@ class ChildModuleTest : public Multiprocess { DirectPtraceConnection connection; ASSERT_TRUE(connection.Initialize(ChildPID())); - ProcessReader process_reader; + ProcessReaderLinux process_reader; ASSERT_TRUE(process_reader.Initialize(&connection)); ExpectModulesFromSelf(process_reader.Modules()); @@ -529,7 +531,7 @@ class ChildModuleTest : public Multiprocess { DISALLOW_COPY_AND_ASSIGN(ChildModuleTest); }; -TEST(ProcessReader, ChildModules) { +TEST(ProcessReaderLinux, ChildModules) { ChildModuleTest test; test.Run(); } diff --git a/snapshot/linux/process_snapshot_linux.cc b/snapshot/linux/process_snapshot_linux.cc index ecb4eb0e..9e212445 100644 --- a/snapshot/linux/process_snapshot_linux.cc +++ b/snapshot/linux/process_snapshot_linux.cc @@ -213,9 +213,9 @@ std::vector ProcessSnapshotLinux::ExtraMemory() const { } void ProcessSnapshotLinux::InitializeThreads() { - const std::vector& process_reader_threads = + const std::vector& process_reader_threads = process_reader_.Threads(); - for (const ProcessReader::Thread& process_reader_thread : + for (const ProcessReaderLinux::Thread& process_reader_thread : process_reader_threads) { auto thread = std::make_unique(); if (thread->Initialize(&process_reader_, process_reader_thread)) { @@ -225,7 +225,8 @@ void ProcessSnapshotLinux::InitializeThreads() { } void ProcessSnapshotLinux::InitializeModules() { - for (const ProcessReader::Module& reader_module : process_reader_.Modules()) { + for (const ProcessReaderLinux::Module& reader_module : + process_reader_.Modules()) { auto module = std::make_unique( reader_module.name, reader_module.elf_reader, reader_module.type); if (module->Initialize()) { diff --git a/snapshot/linux/process_snapshot_linux.h b/snapshot/linux/process_snapshot_linux.h index 613913ce..25c414db 100644 --- a/snapshot/linux/process_snapshot_linux.h +++ b/snapshot/linux/process_snapshot_linux.h @@ -27,7 +27,7 @@ #include "snapshot/crashpad_info_client_options.h" #include "snapshot/elf/module_snapshot_elf.h" #include "snapshot/linux/exception_snapshot_linux.h" -#include "snapshot/linux/process_reader.h" +#include "snapshot/linux/process_reader_linux.h" #include "snapshot/linux/system_snapshot_linux.h" #include "snapshot/linux/thread_snapshot_linux.h" #include "snapshot/memory_map_region_snapshot.h" @@ -127,7 +127,7 @@ class ProcessSnapshotLinux final : public ProcessSnapshot { std::vector> modules_; std::unique_ptr exception_; internal::SystemSnapshotLinux system_; - ProcessReader process_reader_; + ProcessReaderLinux process_reader_; InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotLinux); diff --git a/snapshot/linux/system_snapshot_linux.cc b/snapshot/linux/system_snapshot_linux.cc index c9c64383..4c392888 100644 --- a/snapshot/linux/system_snapshot_linux.cc +++ b/snapshot/linux/system_snapshot_linux.cc @@ -151,7 +151,7 @@ SystemSnapshotLinux::SystemSnapshotLinux() SystemSnapshotLinux::~SystemSnapshotLinux() {} -void SystemSnapshotLinux::Initialize(ProcessReader* process_reader, +void SystemSnapshotLinux::Initialize(ProcessReaderLinux* process_reader, const timeval* snapshot_time) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); process_reader_ = process_reader; diff --git a/snapshot/linux/system_snapshot_linux.h b/snapshot/linux/system_snapshot_linux.h index a9914500..d22c49a6 100644 --- a/snapshot/linux/system_snapshot_linux.h +++ b/snapshot/linux/system_snapshot_linux.h @@ -22,7 +22,7 @@ #include "base/macros.h" #include "build/build_config.h" -#include "snapshot/linux/process_reader.h" +#include "snapshot/linux/process_reader_linux.h" #include "snapshot/system_snapshot.h" #include "util/misc/initialization_state_dcheck.h" @@ -44,9 +44,9 @@ class SystemSnapshotLinux final : public SystemSnapshot { //! \param[in] process_reader A reader for the process being snapshotted. //! \n\n //! It seems odd that a system snapshot implementation would need a - //! ProcessReader, but some of the information reported about the system - //! depends on the process it’s being reported for. For example, the - //! architecture returned by GetCPUArchitecture() should be the + //! ProcessReaderLinux, but some of the information reported about the + //! system depends on the process it’s being reported for. For example, + //! the architecture returned by GetCPUArchitecture() should be the //! architecture of the process, which may be different than the native //! architecture of the system: an x86_64 system can run both x86_64 and //! 32-bit x86 processes. @@ -57,7 +57,8 @@ class SystemSnapshotLinux final : public SystemSnapshot { //! Otherwise, it would need to base its determination on the current //! time, which may be different than the snapshot time for snapshots //! generated around the daylight saving transition time. - void Initialize(ProcessReader* process_reader, const timeval* snapshot_time); + void Initialize(ProcessReaderLinux* process_reader, + const timeval* snapshot_time); // SystemSnapshot: @@ -91,7 +92,7 @@ class SystemSnapshotLinux final : public SystemSnapshot { std::string os_version_full_; std::string os_version_build_; - ProcessReader* process_reader_; // weak + ProcessReaderLinux* process_reader_; // weak const timeval* snapshot_time_; // weak #if defined(ARCH_CPU_X86_FAMILY) CpuidReader cpuid_; diff --git a/snapshot/linux/system_snapshot_linux_test.cc b/snapshot/linux/system_snapshot_linux_test.cc index a91dfa41..46d3845f 100644 --- a/snapshot/linux/system_snapshot_linux_test.cc +++ b/snapshot/linux/system_snapshot_linux_test.cc @@ -21,7 +21,7 @@ #include "build/build_config.h" #include "gtest/gtest.h" -#include "snapshot/linux/process_reader.h" +#include "snapshot/linux/process_reader_linux.h" #include "test/errors.h" #include "test/linux/fake_ptrace_connection.h" @@ -33,7 +33,7 @@ TEST(SystemSnapshotLinux, Basic) { FakePtraceConnection connection; ASSERT_TRUE(connection.Initialize(getpid())); - ProcessReader process_reader; + ProcessReaderLinux process_reader; ASSERT_TRUE(process_reader.Initialize(&connection)); timeval snapshot_time; diff --git a/snapshot/linux/thread_snapshot_linux.cc b/snapshot/linux/thread_snapshot_linux.cc index f465a59c..084ed73a 100644 --- a/snapshot/linux/thread_snapshot_linux.cc +++ b/snapshot/linux/thread_snapshot_linux.cc @@ -37,9 +37,8 @@ ThreadSnapshotLinux::ThreadSnapshotLinux() ThreadSnapshotLinux::~ThreadSnapshotLinux() { } -bool ThreadSnapshotLinux::Initialize( - ProcessReader* process_reader, - const ProcessReader::Thread& thread) { +bool ThreadSnapshotLinux::Initialize(ProcessReaderLinux* process_reader, + const ProcessReaderLinux::Thread& thread) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); #if defined(ARCH_CPU_X86_FAMILY) diff --git a/snapshot/linux/thread_snapshot_linux.h b/snapshot/linux/thread_snapshot_linux.h index 5741b0cb..8fc7e17e 100644 --- a/snapshot/linux/thread_snapshot_linux.h +++ b/snapshot/linux/thread_snapshot_linux.h @@ -20,7 +20,7 @@ #include "base/macros.h" #include "build/build_config.h" #include "snapshot/cpu_context.h" -#include "snapshot/linux/process_reader.h" +#include "snapshot/linux/process_reader_linux.h" #include "snapshot/memory_snapshot.h" #include "snapshot/memory_snapshot_generic.h" #include "snapshot/thread_snapshot.h" @@ -37,15 +37,15 @@ class ThreadSnapshotLinux final : public ThreadSnapshot { //! \brief Initializes the object. //! - //! \param[in] process_reader A ProcessReader for the process containing the - //! thread. - //! \param[in] thread The thread within the ProcessReader for + //! \param[in] process_reader A ProcessReaderLinux for the process containing + //! the thread. + //! \param[in] thread The thread within the ProcessReaderLinux for //! which the snapshot should be created. //! //! \return `true` if the snapshot could be created, `false` otherwise with //! a message logged. - bool Initialize(ProcessReader* process_reader, - const ProcessReader::Thread& thread); + bool Initialize(ProcessReaderLinux* process_reader, + const ProcessReaderLinux::Thread& thread); // ThreadSnapshot: @@ -70,7 +70,7 @@ class ThreadSnapshotLinux final : public ThreadSnapshot { #endif // ARCH_CPU_X86_FAMILY } context_union_; CPUContext context_; - MemorySnapshotGeneric stack_; + MemorySnapshotGeneric stack_; LinuxVMAddress thread_specific_data_address_; pid_t thread_id_; int priority_; diff --git a/snapshot/mac/exception_snapshot_mac.cc b/snapshot/mac/exception_snapshot_mac.cc index 92c8450b..50d1a121 100644 --- a/snapshot/mac/exception_snapshot_mac.cc +++ b/snapshot/mac/exception_snapshot_mac.cc @@ -17,7 +17,7 @@ #include "base/logging.h" #include "base/strings/stringprintf.h" #include "snapshot/mac/cpu_context_mac.h" -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include "util/mach/exception_behaviors.h" #include "util/mach/exception_types.h" #include "util/mach/symbolic_constants_mach.h" @@ -41,7 +41,7 @@ ExceptionSnapshotMac::ExceptionSnapshotMac() ExceptionSnapshotMac::~ExceptionSnapshotMac() { } -bool ExceptionSnapshotMac::Initialize(ProcessReader* process_reader, +bool ExceptionSnapshotMac::Initialize(ProcessReaderMac* process_reader, exception_behavior_t behavior, thread_t exception_thread, exception_type_t exception, @@ -126,8 +126,9 @@ bool ExceptionSnapshotMac::Initialize(ProcessReader* process_reader, exception_code_0_ = unsigned_exception_code_0; } - const ProcessReader::Thread* thread = nullptr; - for (const ProcessReader::Thread& loop_thread : process_reader->Threads()) { + const ProcessReaderMac::Thread* thread = nullptr; + for (const ProcessReaderMac::Thread& loop_thread : + process_reader->Threads()) { if (exception_thread == loop_thread.port) { thread = &loop_thread; break; diff --git a/snapshot/mac/exception_snapshot_mac.h b/snapshot/mac/exception_snapshot_mac.h index 9a6ddcaa..52ef519b 100644 --- a/snapshot/mac/exception_snapshot_mac.h +++ b/snapshot/mac/exception_snapshot_mac.h @@ -29,7 +29,7 @@ namespace crashpad { -class ProcessReader; +class ProcessReaderMac; namespace internal { @@ -45,8 +45,8 @@ class ExceptionSnapshotMac final : public ExceptionSnapshot { //! Other than \a process_reader, the parameters may be passed directly //! through from a Mach exception handler. //! - //! \param[in] process_reader A ProcessReader for the task that sustained the - //! exception. + //! \param[in] process_reader A ProcessReaderMac for the task that sustained + //! the exception. //! \param[in] behavior //! \param[in] exception_thread //! \param[in] exception @@ -58,7 +58,7 @@ class ExceptionSnapshotMac final : public ExceptionSnapshot { //! //! \return `true` if the snapshot could be created, `false` otherwise with //! an appropriate message logged. - bool Initialize(ProcessReader* process_reader, + bool Initialize(ProcessReaderMac* process_reader, exception_behavior_t behavior, thread_t exception_thread, exception_type_t exception, diff --git a/snapshot/mac/mach_o_image_annotations_reader.cc b/snapshot/mac/mach_o_image_annotations_reader.cc index bb8f7e2b..b02acaea 100644 --- a/snapshot/mac/mach_o_image_annotations_reader.cc +++ b/snapshot/mac/mach_o_image_annotations_reader.cc @@ -24,7 +24,7 @@ #include "client/crashpad_info.h" #include "client/simple_string_dictionary.h" #include "snapshot/mac/mach_o_image_reader.h" -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include "snapshot/snapshot_constants.h" #include "util/mach/task_memory.h" #include "util/stdlib/strnlen.h" @@ -32,13 +32,12 @@ namespace crashpad { MachOImageAnnotationsReader::MachOImageAnnotationsReader( - ProcessReader* process_reader, + ProcessReaderMac* process_reader, const MachOImageReader* image_reader, const std::string& name) : name_(name), process_reader_(process_reader), - image_reader_(image_reader) { -} + image_reader_(image_reader) {} std::vector MachOImageAnnotationsReader::Vector() const { std::vector vector_annotations; diff --git a/snapshot/mac/mach_o_image_annotations_reader.h b/snapshot/mac/mach_o_image_annotations_reader.h index 06d2bea9..a56b073b 100644 --- a/snapshot/mac/mach_o_image_annotations_reader.h +++ b/snapshot/mac/mach_o_image_annotations_reader.h @@ -26,7 +26,7 @@ namespace crashpad { class MachOImageReader; -class ProcessReader; +class ProcessReaderMac; //! \brief A reader for annotations stored in a Mach-O image mapped into another //! process. @@ -54,7 +54,7 @@ class MachOImageAnnotationsReader { //! contained within the remote process. //! \param[in] name The module’s name, a string to be used in logged messages. //! This string is for diagnostic purposes only, and may be empty. - MachOImageAnnotationsReader(ProcessReader* process_reader, + MachOImageAnnotationsReader(ProcessReaderMac* process_reader, const MachOImageReader* image_reader, const std::string& name); @@ -91,7 +91,7 @@ class MachOImageAnnotationsReader { std::vector* vector_annotations) const; std::string name_; - ProcessReader* process_reader_; // weak + ProcessReaderMac* process_reader_; // weak const MachOImageReader* image_reader_; // weak DISALLOW_COPY_AND_ASSIGN(MachOImageAnnotationsReader); diff --git a/snapshot/mac/mach_o_image_annotations_reader_test.cc b/snapshot/mac/mach_o_image_annotations_reader_test.cc index f486c1d5..69302503 100644 --- a/snapshot/mac/mach_o_image_annotations_reader_test.cc +++ b/snapshot/mac/mach_o_image_annotations_reader_test.cc @@ -33,7 +33,7 @@ #include "client/crashpad_info.h" #include "client/simple_string_dictionary.h" #include "gtest/gtest.h" -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include "test/errors.h" #include "test/mac/mach_errors.h" #include "test/mac/mach_multiprocess.h" @@ -161,15 +161,15 @@ class TestMachOImageAnnotationsReader final EXPECT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "pid_for_task"); EXPECT_EQ(task_pid, ChildPID()); - ProcessReader process_reader; + ProcessReaderMac process_reader; bool rv = process_reader.Initialize(task); if (!rv) { ADD_FAILURE(); } else { - const std::vector& modules = + const std::vector& modules = process_reader.Modules(); std::vector all_annotations_vector; - for (const ProcessReader::Module& module : modules) { + for (const ProcessReaderMac::Module& module : modules) { if (module.reader) { MachOImageAnnotationsReader module_annotations_reader( &process_reader, module.reader, module.name); @@ -271,7 +271,7 @@ class TestMachOImageAnnotationsReader final // MachMultiprocess: void MachMultiprocessParent() override { - ProcessReader process_reader; + ProcessReaderMac process_reader; ASSERT_TRUE(process_reader.Initialize(ChildTask())); // Wait for the child process to indicate that it’s done setting up its @@ -281,11 +281,11 @@ class TestMachOImageAnnotationsReader final // Verify the “simple map” and object-based annotations set via the // CrashpadInfo interface. - const std::vector& modules = + const std::vector& modules = process_reader.Modules(); std::map all_annotations_simple_map; std::vector all_annotations; - for (const ProcessReader::Module& module : modules) { + for (const ProcessReaderMac::Module& module : modules) { MachOImageAnnotationsReader module_annotations_reader( &process_reader, module.reader, module.name); std::map module_annotations_simple_map = diff --git a/snapshot/mac/mach_o_image_reader.cc b/snapshot/mac/mach_o_image_reader.cc index 8a657264..6baee770 100644 --- a/snapshot/mac/mach_o_image_reader.cc +++ b/snapshot/mac/mach_o_image_reader.cc @@ -26,7 +26,7 @@ #include "client/crashpad_info.h" #include "snapshot/mac/mach_o_image_segment_reader.h" #include "snapshot/mac/mach_o_image_symbol_table_reader.h" -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include "util/mac/checked_mach_address_range.h" #include "util/misc/implicit_cast.h" @@ -62,7 +62,7 @@ MachOImageReader::MachOImageReader() MachOImageReader::~MachOImageReader() { } -bool MachOImageReader::Initialize(ProcessReader* process_reader, +bool MachOImageReader::Initialize(ProcessReaderMac* process_reader, mach_vm_address_t address, const std::string& name) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); diff --git a/snapshot/mac/mach_o_image_reader.h b/snapshot/mac/mach_o_image_reader.h index c16cce99..aded956c 100644 --- a/snapshot/mac/mach_o_image_reader.h +++ b/snapshot/mac/mach_o_image_reader.h @@ -33,7 +33,7 @@ namespace crashpad { class MachOImageSegmentReader; class MachOImageSymbolTableReader; -class ProcessReader; +class ProcessReaderMac; //! \brief A reader for Mach-O images mapped into another process. //! @@ -64,7 +64,7 @@ class MachOImageReader { //! //! \return `true` if the image was read successfully, including all load //! commands. `false` otherwise, with an appropriate message logged. - bool Initialize(ProcessReader* process_reader, + bool Initialize(ProcessReaderMac* process_reader, mach_vm_address_t address, const std::string& name); @@ -337,7 +337,7 @@ class MachOImageReader { mutable std::unique_ptr symbol_table_; std::unique_ptr id_dylib_command_; - ProcessReader* process_reader_; // weak + ProcessReaderMac* process_reader_; // weak uint32_t file_type_; InitializationStateDcheck initialized_; diff --git a/snapshot/mac/mach_o_image_reader_test.cc b/snapshot/mac/mach_o_image_reader_test.cc index d6b801f8..625f8a70 100644 --- a/snapshot/mac/mach_o_image_reader_test.cc +++ b/snapshot/mac/mach_o_image_reader_test.cc @@ -29,7 +29,7 @@ #include "client/crashpad_info.h" #include "gtest/gtest.h" #include "snapshot/mac/mach_o_image_segment_reader.h" -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include "snapshot/mac/process_types.h" #include "test/mac/dyld.h" #include "util/misc/from_pointer_cast.h" @@ -496,7 +496,7 @@ void ExpectSymbolTable(const MachHeader* expect_image, } TEST(MachOImageReader, Self_MainExecutable) { - ProcessReader process_reader; + ProcessReaderMac process_reader; ASSERT_TRUE(process_reader.Initialize(mach_task_self())); const MachHeader* mh_execute_header = @@ -531,7 +531,7 @@ TEST(MachOImageReader, Self_MainExecutable) { } TEST(MachOImageReader, Self_DyldImages) { - ProcessReader process_reader; + ProcessReaderMac process_reader; ASSERT_TRUE(process_reader.Initialize(mach_task_self())); uint32_t count = _dyld_image_count(); diff --git a/snapshot/mac/mach_o_image_segment_reader.cc b/snapshot/mac/mach_o_image_segment_reader.cc index 06e1daf4..1be829dc 100644 --- a/snapshot/mac/mach_o_image_segment_reader.cc +++ b/snapshot/mac/mach_o_image_segment_reader.cc @@ -20,7 +20,7 @@ #include "base/logging.h" #include "base/strings/stringprintf.h" -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include "util/mac/checked_mach_address_range.h" #include "util/mac/mac_util.h" #include "util/stdlib/strnlen.h" @@ -47,7 +47,7 @@ MachOImageSegmentReader::MachOImageSegmentReader() MachOImageSegmentReader::~MachOImageSegmentReader() { } -bool MachOImageSegmentReader::Initialize(ProcessReader* process_reader, +bool MachOImageSegmentReader::Initialize(ProcessReaderMac* process_reader, mach_vm_address_t load_command_address, const std::string& load_command_info, const std::string& module_name, diff --git a/snapshot/mac/mach_o_image_segment_reader.h b/snapshot/mac/mach_o_image_segment_reader.h index 20f891da..bdd57716 100644 --- a/snapshot/mac/mach_o_image_segment_reader.h +++ b/snapshot/mac/mach_o_image_segment_reader.h @@ -62,7 +62,7 @@ class MachOImageSegmentReader { //! //! \return `true` if the load command was read successfully. `false` //! otherwise, with an appropriate message logged. - bool Initialize(ProcessReader* process_reader, + bool Initialize(ProcessReaderMac* process_reader, mach_vm_address_t load_command_address, const std::string& load_command_info, const std::string& module_name, diff --git a/snapshot/mac/mach_o_image_symbol_table_reader.cc b/snapshot/mac/mach_o_image_symbol_table_reader.cc index c5eb1969..361253ce 100644 --- a/snapshot/mac/mach_o_image_symbol_table_reader.cc +++ b/snapshot/mac/mach_o_image_symbol_table_reader.cc @@ -39,7 +39,7 @@ namespace internal { class MachOImageSymbolTableReaderInitializer { public: MachOImageSymbolTableReaderInitializer( - ProcessReader* process_reader, + ProcessReaderMac* process_reader, const MachOImageSegmentReader* linkedit_segment, const std::string& module_info) : module_info_(module_info), @@ -243,7 +243,7 @@ class MachOImageSymbolTableReaderInitializer { std::string module_info_; CheckedMachAddressRange linkedit_range_; - ProcessReader* process_reader_; // weak + ProcessReaderMac* process_reader_; // weak const MachOImageSegmentReader* linkedit_segment_; // weak DISALLOW_COPY_AND_ASSIGN(MachOImageSymbolTableReaderInitializer); @@ -259,7 +259,7 @@ MachOImageSymbolTableReader::~MachOImageSymbolTableReader() { } bool MachOImageSymbolTableReader::Initialize( - ProcessReader* process_reader, + ProcessReaderMac* process_reader, const process_types::symtab_command* symtab_command, const process_types::dysymtab_command* dysymtab_command, const MachOImageSegmentReader* linkedit_segment, diff --git a/snapshot/mac/mach_o_image_symbol_table_reader.h b/snapshot/mac/mach_o_image_symbol_table_reader.h index a097854b..841b479a 100644 --- a/snapshot/mac/mach_o_image_symbol_table_reader.h +++ b/snapshot/mac/mach_o_image_symbol_table_reader.h @@ -23,7 +23,7 @@ #include "base/macros.h" #include "snapshot/mac/mach_o_image_segment_reader.h" -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include "snapshot/mac/process_types.h" #include "util/misc/initialization_state_dcheck.h" @@ -92,7 +92,7 @@ class MachOImageSymbolTableReader { //! //! \return `true` if the symbol table was read successfully. `false` //! otherwise, with an appropriate message logged. - bool Initialize(ProcessReader* process_reader, + bool Initialize(ProcessReaderMac* process_reader, const process_types::symtab_command* symtab_command, const process_types::dysymtab_command* dysymtab_command, const MachOImageSegmentReader* linkedit_segment, diff --git a/snapshot/mac/module_snapshot_mac.cc b/snapshot/mac/module_snapshot_mac.cc index 19c4759d..41608978 100644 --- a/snapshot/mac/module_snapshot_mac.cc +++ b/snapshot/mac/module_snapshot_mac.cc @@ -41,8 +41,8 @@ ModuleSnapshotMac::~ModuleSnapshotMac() { } bool ModuleSnapshotMac::Initialize( - ProcessReader* process_reader, - const ProcessReader::Module& process_reader_module) { + ProcessReaderMac* process_reader, + const ProcessReaderMac::Module& process_reader_module) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); process_reader_ = process_reader; diff --git a/snapshot/mac/module_snapshot_mac.h b/snapshot/mac/module_snapshot_mac.h index 44c07910..fe2d40a1 100644 --- a/snapshot/mac/module_snapshot_mac.h +++ b/snapshot/mac/module_snapshot_mac.h @@ -25,7 +25,7 @@ #include "base/macros.h" #include "client/crashpad_info.h" #include "snapshot/crashpad_info_client_options.h" -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include "snapshot/module_snapshot.h" #include "util/misc/initialization_state_dcheck.h" @@ -45,15 +45,15 @@ class ModuleSnapshotMac final : public ModuleSnapshot { //! \brief Initializes the object. //! - //! \param[in] process_reader A ProcessReader for the task containing the + //! \param[in] process_reader A ProcessReaderMac for the task containing the //! module. - //! \param[in] process_reader_module The module within the ProcessReader for - //! which the snapshot should be created. + //! \param[in] process_reader_module The module within the ProcessReaderMac + //! for which the snapshot should be created. //! //! \return `true` if the snapshot could be created, `false` otherwise with //! an appropriate message logged. - bool Initialize(ProcessReader* process_reader, - const ProcessReader::Module& process_reader_module); + bool Initialize(ProcessReaderMac* process_reader, + const ProcessReaderMac::Module& process_reader_module); //! \brief Returns options from the module’s CrashpadInfo structure. //! @@ -87,7 +87,7 @@ class ModuleSnapshotMac final : public ModuleSnapshot { std::string name_; time_t timestamp_; const MachOImageReader* mach_o_image_reader_; // weak - ProcessReader* process_reader_; // weak + ProcessReaderMac* process_reader_; // weak InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotMac); diff --git a/snapshot/mac/process_reader.cc b/snapshot/mac/process_reader_mac.cc similarity index 94% rename from snapshot/mac/process_reader.cc rename to snapshot/mac/process_reader_mac.cc index 6ddcff05..e142fd2e 100644 --- a/snapshot/mac/process_reader.cc +++ b/snapshot/mac/process_reader_mac.cc @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include -#include #include +#include #include #include @@ -71,7 +71,7 @@ kern_return_t MachVMRegionRecurseDeepest(task_t task, namespace crashpad { -ProcessReader::Thread::Thread() +ProcessReaderMac::Thread::Thread() : thread_context(), float_context(), debug_context(), @@ -81,16 +81,13 @@ ProcessReader::Thread::Thread() thread_specific_data_address(0), port(THREAD_NULL), suspend_count(0), - priority(0) { -} + priority(0) {} -ProcessReader::Module::Module() : name(), reader(nullptr), timestamp(0) { -} +ProcessReaderMac::Module::Module() : name(), reader(nullptr), timestamp(0) {} -ProcessReader::Module::~Module() { -} +ProcessReaderMac::Module::~Module() {} -ProcessReader::ProcessReader() +ProcessReaderMac::ProcessReaderMac() : process_info_(), threads_(), modules_(), @@ -100,17 +97,16 @@ ProcessReader::ProcessReader() initialized_(), is_64_bit_(false), initialized_threads_(false), - initialized_modules_(false) { -} + initialized_modules_(false) {} -ProcessReader::~ProcessReader() { +ProcessReaderMac::~ProcessReaderMac() { for (const Thread& thread : threads_) { kern_return_t kr = mach_port_deallocate(mach_task_self(), thread.port); MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate"; } } -bool ProcessReader::Initialize(task_t task) { +bool ProcessReaderMac::Initialize(task_t task) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); if (!process_info_.InitializeWithTask(task)) { @@ -126,12 +122,13 @@ bool ProcessReader::Initialize(task_t task) { return true; } -void ProcessReader::StartTime(timeval* start_time) const { +void ProcessReaderMac::StartTime(timeval* start_time) const { bool rv = process_info_.StartTime(start_time); DCHECK(rv); } -bool ProcessReader::CPUTimes(timeval* user_time, timeval* system_time) const { +bool ProcessReaderMac::CPUTimes(timeval* user_time, + timeval* system_time) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); // Calculate user and system time the same way the kernel does for @@ -177,7 +174,7 @@ bool ProcessReader::CPUTimes(timeval* user_time, timeval* system_time) const { return true; } -const std::vector& ProcessReader::Threads() { +const std::vector& ProcessReaderMac::Threads() { INITIALIZATION_STATE_DCHECK_VALID(initialized_); if (!initialized_threads_) { @@ -187,7 +184,7 @@ const std::vector& ProcessReader::Threads() { return threads_; } -const std::vector& ProcessReader::Modules() { +const std::vector& ProcessReaderMac::Modules() { INITIALIZATION_STATE_DCHECK_VALID(initialized_); if (!initialized_modules_) { @@ -197,7 +194,7 @@ const std::vector& ProcessReader::Modules() { return modules_; } -mach_vm_address_t ProcessReader::DyldAllImageInfo( +mach_vm_address_t ProcessReaderMac::DyldAllImageInfo( mach_vm_size_t* all_image_info_size) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); @@ -210,9 +207,9 @@ mach_vm_address_t ProcessReader::DyldAllImageInfo( return 0; } - // TODO(mark): Deal with statically linked executables which don’t use dyld. - // This may look for the module that matches the executable path in the same - // data set that vmmap uses. +// TODO(mark): Deal with statically linked executables which don’t use dyld. +// This may look for the module that matches the executable path in the same +// data set that vmmap uses. #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 // The task_dyld_info_data_t struct grew in 10.7, adding the format field. @@ -237,7 +234,7 @@ mach_vm_address_t ProcessReader::DyldAllImageInfo( return dyld_info.all_image_info_addr; } -void ProcessReader::InitializeThreads() { +void ProcessReaderMac::InitializeThreads() { DCHECK(!initialized_threads_); DCHECK(threads_.empty()); @@ -378,7 +375,7 @@ void ProcessReader::InitializeThreads() { threads_need_owners.Disarm(); } -void ProcessReader::InitializeModules() { +void ProcessReaderMac::InitializeModules() { DCHECK(!initialized_modules_); DCHECK(modules_.empty()); @@ -465,8 +462,8 @@ void ProcessReader::InitializeModules() { image_info.imageLoadAddress == all_image_infos.dyldImageLoadAddress) { found_dyld = true; LOG(WARNING) << base::StringPrintf( - "found dylinker (%s) in dyld_all_image_infos::infoArray", - module.name.c_str()); + "found dylinker (%s) in dyld_all_image_infos::infoArray", + module.name.c_str()); LOG_IF(WARNING, file_type != MH_DYLINKER) << base::StringPrintf("dylinker (%s) has unexpected Mach-O type %d", @@ -563,7 +560,7 @@ void ProcessReader::InitializeModules() { } } -mach_vm_address_t ProcessReader::CalculateStackRegion( +mach_vm_address_t ProcessReaderMac::CalculateStackRegion( mach_vm_address_t stack_pointer, mach_vm_size_t* stack_region_size) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); @@ -675,10 +672,10 @@ mach_vm_address_t ProcessReader::CalculateStackRegion( return region_base; } -void ProcessReader::LocateRedZone(mach_vm_address_t* const start_address, - mach_vm_address_t* const region_base, - mach_vm_address_t* const region_size, - const unsigned int user_tag) { +void ProcessReaderMac::LocateRedZone(mach_vm_address_t* const start_address, + mach_vm_address_t* const region_base, + mach_vm_address_t* const region_size, + const unsigned int user_tag) { #if defined(ARCH_CPU_X86_FAMILY) if (Is64Bit()) { // x86_64 has a red zone. See AMD64 ABI 0.99.8, diff --git a/snapshot/mac/process_reader.h b/snapshot/mac/process_reader_mac.h similarity index 96% rename from snapshot/mac/process_reader.h rename to snapshot/mac/process_reader_mac.h index ecca2a5a..91836dbb 100644 --- a/snapshot/mac/process_reader.h +++ b/snapshot/mac/process_reader_mac.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_H_ -#define CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_H_ +#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_MAC_H_ #include #include @@ -37,7 +37,7 @@ class MachOImageReader; //! \brief Accesses information about another process, identified by a Mach //! task. -class ProcessReader { +class ProcessReaderMac { public: //! \brief Contains information about a thread that belongs to a task //! (process). @@ -83,7 +83,7 @@ class ProcessReader { //! \brief An image reader for the module. //! //! The lifetime of this MachOImageReader is scoped to the lifetime of the - //! ProcessReader that created it. + //! ProcessReaderMac that created it. //! //! This field may be `nullptr` if a reader could not be created for the //! module. @@ -97,8 +97,8 @@ class ProcessReader { time_t timestamp; }; - ProcessReader(); - ~ProcessReader(); + ProcessReaderMac(); + ~ProcessReaderMac(); //! \brief Initializes this object. This method must be called before any //! other. @@ -244,9 +244,9 @@ class ProcessReader { bool initialized_threads_; bool initialized_modules_; - DISALLOW_COPY_AND_ASSIGN(ProcessReader); + DISALLOW_COPY_AND_ASSIGN(ProcessReaderMac); }; } // namespace crashpad -#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_H_ +#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_MAC_H_ diff --git a/snapshot/mac/process_reader_test.cc b/snapshot/mac/process_reader_mac_test.cc similarity index 88% rename from snapshot/mac/process_reader_test.cc rename to snapshot/mac/process_reader_mac_test.cc index c9f39e71..faec1472 100644 --- a/snapshot/mac/process_reader_test.cc +++ b/snapshot/mac/process_reader_mac_test.cc @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include +#include #include #include #include -#include #include #include @@ -48,8 +48,8 @@ namespace { constexpr char kDyldPath[] = "/usr/lib/dyld"; -TEST(ProcessReader, SelfBasic) { - ProcessReader process_reader; +TEST(ProcessReaderMac, SelfBasic) { + ProcessReaderMac process_reader; ASSERT_TRUE(process_reader.Initialize(mach_task_self())); #if !defined(ARCH_CPU_64_BITS) @@ -80,7 +80,7 @@ class ProcessReaderChild final : public MachMultiprocess { private: void MachMultiprocessParent() override { - ProcessReader process_reader; + ProcessReaderMac process_reader; ASSERT_TRUE(process_reader.Initialize(ChildTask())); #if !defined(ARCH_CPU_64_BITS) @@ -116,7 +116,7 @@ class ProcessReaderChild final : public MachMultiprocess { DISALLOW_COPY_AND_ASSIGN(ProcessReaderChild); }; -TEST(ProcessReader, ChildBasic) { +TEST(ProcessReaderMac, ChildBasic) { ProcessReaderChild process_reader_child; process_reader_child.Run(); } @@ -131,11 +131,12 @@ uint64_t PthreadToThreadID(pthread_t pthread) { return thread_id; } -TEST(ProcessReader, SelfOneThread) { - ProcessReader process_reader; +TEST(ProcessReaderMac, SelfOneThread) { + ProcessReaderMac process_reader; ASSERT_TRUE(process_reader.Initialize(mach_task_self())); - const std::vector& threads = process_reader.Threads(); + const std::vector& threads = + process_reader.Threads(); // If other tests ran in this process previously, threads may have been // created and may still be running. This check must look for at least one @@ -157,8 +158,7 @@ class TestThreadPool { int suspend_count; }; - TestThreadPool() : thread_infos_() { - } + TestThreadPool() : thread_infos_() {} // Resumes suspended threads, signals each thread’s exit semaphore asking it // to exit, and joins each thread, blocking until they have all exited. @@ -192,10 +192,8 @@ class TestThreadPool { thread_infos_.push_back(std::make_unique()); ThreadInfo* thread_info = thread_infos_.back().get(); - int rv = pthread_create(&thread_info->pthread, - nullptr, - ThreadMain, - thread_info); + int rv = pthread_create( + &thread_info->pthread, nullptr, ThreadMain, thread_info); ASSERT_EQ(rv, 0); } @@ -210,8 +208,7 @@ class TestThreadPool { ++thread_index) { thread_t thread_port = pthread_mach_thread_np(thread_infos_[thread_index]->pthread); - for (size_t suspend_count = 0; - suspend_count < thread_index; + for (size_t suspend_count = 0; suspend_count < thread_index; ++suspend_count) { kern_return_t kr = thread_suspend(thread_port); EXPECT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "thread_suspend"); @@ -222,8 +219,7 @@ class TestThreadPool { } } - uint64_t GetThreadInfo(size_t thread_index, - ThreadExpectation* expectation) { + uint64_t GetThreadInfo(size_t thread_index, ThreadExpectation* expectation) { CHECK_LT(thread_index, thread_infos_.size()); const auto& thread_info = thread_infos_[thread_index]; @@ -240,8 +236,7 @@ class TestThreadPool { stack_address(0), ready_semaphore(0), exit_semaphore(0), - suspend_count(0) { - } + suspend_count(0) {} ~ThreadInfo() {} @@ -294,14 +289,14 @@ class TestThreadPool { using ThreadMap = std::map; -// Verifies that all of the threads in |threads|, obtained from ProcessReader, -// agree with the expectation in |thread_map|. If |tolerate_extra_threads| is -// true, |threads| is allowed to contain threads that are not listed in -// |thread_map|. This is useful when testing situations where code outside of -// the test’s control (such as system libraries) may start threads, or may have -// started threads prior to a test’s execution. +// Verifies that all of the threads in |threads|, obtained from +// ProcessReaderMac, agree with the expectation in |thread_map|. If +// |tolerate_extra_threads| is true, |threads| is allowed to contain threads +// that are not listed in |thread_map|. This is useful when testing situations +// where code outside of the test’s control (such as system libraries) may start +// threads, or may have started threads prior to a test’s execution. void ExpectSeveralThreads(ThreadMap* thread_map, - const std::vector& threads, + const std::vector& threads, const bool tolerate_extra_threads) { if (tolerate_extra_threads) { ASSERT_GE(threads.size(), thread_map->size()); @@ -310,7 +305,7 @@ void ExpectSeveralThreads(ThreadMap* thread_map, } for (size_t thread_index = 0; thread_index < threads.size(); ++thread_index) { - const ProcessReader::Thread& thread = threads[thread_index]; + const ProcessReaderMac::Thread& thread = threads[thread_index]; mach_vm_address_t thread_stack_region_end = thread.stack_region_address + thread.stack_region_size; @@ -336,26 +331,26 @@ void ExpectSeveralThreads(ThreadMap* thread_map, // with any other thread’s. Each thread should have a unique value for its // ID and port, and each should have its own stack that doesn’t touch any // other thread’s stack. - for (size_t other_thread_index = 0; - other_thread_index < threads.size(); + for (size_t other_thread_index = 0; other_thread_index < threads.size(); ++other_thread_index) { if (other_thread_index == thread_index) { continue; } - const ProcessReader::Thread& other_thread = threads[other_thread_index]; + const ProcessReaderMac::Thread& other_thread = + threads[other_thread_index]; EXPECT_NE(other_thread.id, thread.id); EXPECT_NE(other_thread.port, thread.port); mach_vm_address_t other_thread_stack_region_end = other_thread.stack_region_address + other_thread.stack_region_size; - EXPECT_FALSE( - thread.stack_region_address >= other_thread.stack_region_address && - thread.stack_region_address < other_thread_stack_region_end); - EXPECT_FALSE( - thread_stack_region_end > other_thread.stack_region_address && - thread_stack_region_end <= other_thread_stack_region_end); + EXPECT_FALSE(thread.stack_region_address >= + other_thread.stack_region_address && + thread.stack_region_address < other_thread_stack_region_end); + EXPECT_FALSE(thread_stack_region_end > + other_thread.stack_region_address && + thread_stack_region_end <= other_thread_stack_region_end); } } @@ -363,12 +358,12 @@ void ExpectSeveralThreads(ThreadMap* thread_map, EXPECT_TRUE(thread_map->empty()); } -TEST(ProcessReader, SelfSeveralThreads) { - // Set up the ProcessReader here, before any other threads are running. This - // tests that the threads it returns are lazily initialized as a snapshot of - // the threads at the time of the first call to Threads(), and not at the +TEST(ProcessReaderMac, SelfSeveralThreads) { + // Set up the ProcessReaderMac here, before any other threads are running. + // This tests that the threads it returns are lazily initialized as a snapshot + // of the threads at the time of the first call to Threads(), and not at the // time the ProcessReader was created or initialized. - ProcessReader process_reader; + ProcessReaderMac process_reader; ASSERT_TRUE(process_reader.Initialize(mach_task_self())); TestThreadPool thread_pool; @@ -392,7 +387,8 @@ TEST(ProcessReader, SelfSeveralThreads) { thread_map[thread_id] = expectation; } - const std::vector& threads = process_reader.Threads(); + const std::vector& threads = + process_reader.Threads(); // Other tests that have run previously may have resulted in the creation of // threads that still exist, so pass true for |tolerate_extra_threads|. @@ -403,7 +399,7 @@ TEST(ProcessReader, SelfSeveralThreads) { // shows up once. thread_t thread_self = MachThreadSelf(); bool found_thread_self = false; - for (const ProcessReader::Thread& thread : threads) { + for (const ProcessReaderMac::Thread& thread : threads) { if (thread.port == thread_self) { EXPECT_FALSE(found_thread_self); found_thread_self = true; @@ -416,15 +412,13 @@ TEST(ProcessReader, SelfSeveralThreads) { class ProcessReaderThreadedChild final : public MachMultiprocess { public: explicit ProcessReaderThreadedChild(size_t thread_count) - : MachMultiprocess(), - thread_count_(thread_count) { - } + : MachMultiprocess(), thread_count_(thread_count) {} ~ProcessReaderThreadedChild() {} private: void MachMultiprocessParent() override { - ProcessReader process_reader; + ProcessReaderMac process_reader; ASSERT_TRUE(process_reader.Initialize(ChildTask())); FileHandle read_handle = ReadPipeHandle(); @@ -433,8 +427,7 @@ class ProcessReaderThreadedChild final : public MachMultiprocess { // addresses that should lie somewhere within each thread’s stack as values. // These IDs and addresses all come from the child process via the pipe. ThreadMap thread_map; - for (size_t thread_index = 0; - thread_index < thread_count_ + 1; + for (size_t thread_index = 0; thread_index < thread_count_ + 1; ++thread_index) { uint64_t thread_id; CheckedReadFileExactly(read_handle, &thread_id, sizeof(thread_id)); @@ -453,7 +446,8 @@ class ProcessReaderThreadedChild final : public MachMultiprocess { thread_map[thread_id] = expectation; } - const std::vector& threads = process_reader.Threads(); + const std::vector& threads = + process_reader.Threads(); // The child shouldn’t have any threads other than its main thread and the // ones it created in its pool, so pass false for |tolerate_extra_threads|. @@ -484,8 +478,7 @@ class ProcessReaderThreadedChild final : public MachMultiprocess { sizeof(expectation.suspend_count)); // Write an entry for everything in the thread pool. - for (size_t thread_index = 0; - thread_index < thread_count_; + for (size_t thread_index = 0; thread_index < thread_count_; ++thread_index) { uint64_t thread_id = thread_pool.GetThreadInfo(thread_index, &expectation); @@ -509,14 +502,14 @@ class ProcessReaderThreadedChild final : public MachMultiprocess { DISALLOW_COPY_AND_ASSIGN(ProcessReaderThreadedChild); }; -TEST(ProcessReader, ChildOneThread) { +TEST(ProcessReaderMac, ChildOneThread) { // The main thread plus zero child threads equals one thread. constexpr size_t kChildThreads = 0; ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads); process_reader_threaded_child.Run(); } -TEST(ProcessReader, ChildSeveralThreads) { +TEST(ProcessReaderMac, ChildSeveralThreads) { constexpr size_t kChildThreads = 64; ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads); process_reader_threaded_child.Run(); @@ -537,10 +530,7 @@ TEST(ProcessReader, ChildSeveralThreads) { class ScopedOpenCLNoOpKernel { public: ScopedOpenCLNoOpKernel() - : context_(nullptr), - program_(nullptr), - kernel_(nullptr) { - } + : context_(nullptr), program_(nullptr), kernel_(nullptr) {} ~ScopedOpenCLNoOpKernel() { if (kernel_) { @@ -566,10 +556,10 @@ class ScopedOpenCLNoOpKernel { #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10 && \ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 - // cl_device_id is really available in OpenCL.framework back to 10.5, but in - // the 10.10 SDK and later, OpenCL.framework includes , - // which has its own cl_device_id that was introduced in 10.10. That - // triggers erroneous availability warnings. +// cl_device_id is really available in OpenCL.framework back to 10.5, but in +// the 10.10 SDK and later, OpenCL.framework includes , +// which has its own cl_device_id that was introduced in 10.10. That +// triggers erroneous availability warnings. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunguarded-availability" #define DISABLED_WUNGUARDED_AVAILABILITY @@ -642,15 +632,16 @@ bool ExpectCLKernels() { #endif } -TEST(ProcessReader, SelfModules) { +TEST(ProcessReaderMac, SelfModules) { ScopedOpenCLNoOpKernel ensure_cl_kernels; ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp()); - ProcessReader process_reader; + ProcessReaderMac process_reader; ASSERT_TRUE(process_reader.Initialize(mach_task_self())); uint32_t dyld_image_count = _dyld_image_count(); - const std::vector& modules = process_reader.Modules(); + const std::vector& modules = + process_reader.Modules(); // There needs to be at least an entry for the main executable, for a dylib, // and for dyld. @@ -718,10 +709,10 @@ class ProcessReaderModulesChild final : public MachMultiprocess { private: void MachMultiprocessParent() override { - ProcessReader process_reader; + ProcessReaderMac process_reader; ASSERT_TRUE(process_reader.Initialize(ChildTask())); - const std::vector& modules = + const std::vector& modules = process_reader.Modules(); // There needs to be at least an entry for the main executable, for a dylib, @@ -829,7 +820,7 @@ class ProcessReaderModulesChild final : public MachMultiprocess { DISALLOW_COPY_AND_ASSIGN(ProcessReaderModulesChild); }; -TEST(ProcessReader, ChildModules) { +TEST(ProcessReaderMac, ChildModules) { ScopedOpenCLNoOpKernel ensure_cl_kernels; ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp()); diff --git a/snapshot/mac/process_snapshot_mac.cc b/snapshot/mac/process_snapshot_mac.cc index eaaf3dca..cf5233a9 100644 --- a/snapshot/mac/process_snapshot_mac.cc +++ b/snapshot/mac/process_snapshot_mac.cc @@ -218,9 +218,9 @@ std::vector ProcessSnapshotMac::ExtraMemory() const { } void ProcessSnapshotMac::InitializeThreads() { - const std::vector& process_reader_threads = + const std::vector& process_reader_threads = process_reader_.Threads(); - for (const ProcessReader::Thread& process_reader_thread : + for (const ProcessReaderMac::Thread& process_reader_thread : process_reader_threads) { auto thread = std::make_unique(); if (thread->Initialize(&process_reader_, process_reader_thread)) { @@ -230,9 +230,9 @@ void ProcessSnapshotMac::InitializeThreads() { } void ProcessSnapshotMac::InitializeModules() { - const std::vector& process_reader_modules = + const std::vector& process_reader_modules = process_reader_.Modules(); - for (const ProcessReader::Module& process_reader_module : + for (const ProcessReaderMac::Module& process_reader_module : process_reader_modules) { auto module = std::make_unique(); if (module->Initialize(&process_reader_, process_reader_module)) { diff --git a/snapshot/mac/process_snapshot_mac.h b/snapshot/mac/process_snapshot_mac.h index e7195d94..06bac749 100644 --- a/snapshot/mac/process_snapshot_mac.h +++ b/snapshot/mac/process_snapshot_mac.h @@ -30,7 +30,7 @@ #include "snapshot/exception_snapshot.h" #include "snapshot/mac/exception_snapshot_mac.h" #include "snapshot/mac/module_snapshot_mac.h" -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include "snapshot/mac/system_snapshot_mac.h" #include "snapshot/mac/thread_snapshot_mac.h" #include "snapshot/memory_map_region_snapshot.h" @@ -143,7 +143,7 @@ class ProcessSnapshotMac final : public ProcessSnapshot { std::vector> threads_; std::vector> modules_; std::unique_ptr exception_; - ProcessReader process_reader_; + ProcessReaderMac process_reader_; UUID report_id_; UUID client_id_; std::map annotations_simple_map_; diff --git a/snapshot/mac/process_types.cc b/snapshot/mac/process_types.cc index 35d81db1..65c39ea5 100644 --- a/snapshot/mac/process_types.cc +++ b/snapshot/mac/process_types.cc @@ -94,7 +94,7 @@ inline void Assign(UInt64Array4* destination, namespace process_types { \ \ /* static */ \ - size_t struct_name::ExpectedSize(ProcessReader* process_reader) { \ + size_t struct_name::ExpectedSize(ProcessReaderMac* process_reader) { \ if (!process_reader->Is64Bit()) { \ return internal::struct_name::Size(); \ } else { \ @@ -103,7 +103,7 @@ inline void Assign(UInt64Array4* destination, } \ \ /* static */ \ - bool struct_name::ReadInto(ProcessReader* process_reader, \ + bool struct_name::ReadInto(ProcessReaderMac* process_reader, \ mach_vm_address_t address, \ struct_name* generic) { \ if (!process_reader->Is64Bit()) { \ @@ -117,7 +117,7 @@ inline void Assign(UInt64Array4* destination, \ /* static */ \ template \ - bool struct_name::ReadIntoInternal(ProcessReader* process_reader, \ + bool struct_name::ReadIntoInternal(ProcessReaderMac* process_reader, \ mach_vm_address_t address, \ struct_name* generic) { \ T specific; \ @@ -166,22 +166,22 @@ inline void Assign(UInt64Array4* destination, // implementations in snapshot/mac/process_types/custom.cc. #define PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO 1 -#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \ - namespace crashpad { \ - namespace process_types { \ - namespace internal { \ - \ - /* static */ \ - template \ - bool struct_name::ReadInto(ProcessReader* process_reader, \ - mach_vm_address_t address, \ - struct_name* specific) { \ - return process_reader->Memory()->Read( \ - address, sizeof(*specific), specific); \ - } \ - } /* namespace internal */ \ - } /* namespace process_types */ \ - } /* namespace crashpad */ +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \ + namespace crashpad { \ + namespace process_types { \ + namespace internal { \ + \ + /* static */ \ + template \ + bool struct_name::ReadInto(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + struct_name* specific) { \ + return process_reader->Memory()->Read( \ + address, sizeof(*specific), specific); \ + } \ + } /* namespace internal */ \ + } /* namespace process_types */ \ + } /* namespace crashpad */ #define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) @@ -214,7 +214,7 @@ inline void Assign(UInt64Array4* destination, \ /* static */ \ template \ - bool struct_name::ReadArrayInto(ProcessReader* process_reader, \ + bool struct_name::ReadArrayInto(ProcessReaderMac* process_reader, \ mach_vm_address_t address, \ size_t count, \ struct_name* specific) { \ @@ -225,7 +225,7 @@ inline void Assign(UInt64Array4* destination, } /* namespace internal */ \ \ /* static */ \ - bool struct_name::ReadArrayInto(ProcessReader* process_reader, \ + bool struct_name::ReadArrayInto(ProcessReaderMac* process_reader, \ mach_vm_address_t address, \ size_t count, \ struct_name* generic) { \ @@ -241,7 +241,7 @@ inline void Assign(UInt64Array4* destination, \ /* static */ \ template \ - bool struct_name::ReadArrayIntoInternal(ProcessReader* process_reader, \ + bool struct_name::ReadArrayIntoInternal(ProcessReaderMac* process_reader, \ mach_vm_address_t address, \ size_t count, \ struct_name* generic) { \ @@ -293,7 +293,7 @@ inline void Assign(UInt64Array4* destination, \ /* static */ \ size_t struct_name::ExpectedSizeForVersion( \ - ProcessReader* process_reader, \ + ProcessReaderMac* process_reader, \ decltype(struct_name::version_field) version) { \ if (!process_reader->Is64Bit()) { \ return internal::struct_name< \ @@ -304,8 +304,8 @@ inline void Assign(UInt64Array4* destination, } \ } \ \ - } /* namespace process_types */ \ - } /* namespace crashpad */ + } /* namespace process_types */ \ + } /* namespace crashpad */ #define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) @@ -354,7 +354,7 @@ inline void Assign(UInt64Array4* destination, } /* namespace internal */ \ \ /* static */ \ - size_t struct_name::MinimumSize(ProcessReader* process_reader) { \ + size_t struct_name::MinimumSize(ProcessReaderMac* process_reader) { \ if (!process_reader->Is64Bit()) { \ return internal::struct_name::MinimumSize(); \ } else { \ diff --git a/snapshot/mac/process_types.h b/snapshot/mac/process_types.h index 350b4060..6a5f9c1c 100644 --- a/snapshot/mac/process_types.h +++ b/snapshot/mac/process_types.h @@ -21,7 +21,7 @@ #include #include -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" namespace crashpad { namespace process_types { @@ -80,14 +80,14 @@ DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64) \ /* Initializes an object with data read from |process_reader| at \ * |address|, properly genericized. */ \ - bool Read(ProcessReader* process_reader, mach_vm_address_t address) { \ + bool Read(ProcessReaderMac* process_reader, mach_vm_address_t address) { \ return ReadInto(process_reader, address, this); \ } \ \ /* Reads |count| objects from |process_reader| beginning at |address|, and \ * genericizes the objects. The caller must provide storage for |count| \ * objects in |generic|. */ \ - static bool ReadArrayInto(ProcessReader* process_reader, \ + static bool ReadArrayInto(ProcessReaderMac* process_reader, \ mach_vm_address_t address, \ size_t count, \ struct_name* generic); \ @@ -102,47 +102,48 @@ DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64) * on the process’ bitness. This can be used prior to reading any data \ * from a process. For versioned and sized structures, \ * ExpectedSizeForVersion() and MinimumSize() may also be useful. */ \ - static size_t ExpectedSize(ProcessReader* process_reader); + static size_t ExpectedSize(ProcessReaderMac* process_reader); #define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \ member_type member_name __VA_ARGS__; -#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) \ - /* Similar to ExpectedSize(), but computes the expected size of a \ - * structure based on the process’ bitness and a custom value, such as a \ - * structure version number. This can be used prior to reading any data \ - * from a process. */ \ - static size_t ExpectedSizeForVersion( \ - ProcessReader* process_reader, \ - decltype(struct_name::version_field) version); +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) \ + /* Similar to ExpectedSize(), but computes the expected size of a \ + * structure based on the process’ bitness and a custom value, such as a \ + * structure version number. This can be used prior to reading any data \ + * from a process. */ \ + static size_t ExpectedSizeForVersion( \ + ProcessReaderMac* process_reader, \ + decltype(struct_name::version_field) version); -#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) \ - /* Similar to ExpectedSize(), but computes the minimum size of a \ - * structure based on the process’ bitness, typically including enough of \ - * a structure to contain its size field. This can be used prior to \ - * reading any data from a process. */ \ - static size_t MinimumSize(ProcessReader* process_reader); +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) \ + /* Similar to ExpectedSize(), but computes the minimum size of a \ + * structure based on the process’ bitness, typically including enough of \ + * a structure to contain its size field. This can be used prior to \ + * reading any data from a process. */ \ + static size_t MinimumSize(ProcessReaderMac* process_reader); -#define PROCESS_TYPE_STRUCT_END(struct_name) \ - private: \ - /* The static form of Read(). Populates the struct at |generic|. */ \ - static bool ReadInto(ProcessReader* process_reader, \ - mach_vm_address_t address, \ - struct_name* generic); \ - \ - template \ - static bool ReadIntoInternal(ProcessReader* process_reader, \ - mach_vm_address_t address, \ - struct_name* generic); \ - template \ - static bool ReadArrayIntoInternal(ProcessReader* process_reader, \ - mach_vm_address_t address, \ - size_t count, \ - struct_name* generic); \ - size_t size_; \ - }; \ - } /* namespace process_types */ \ - } /* namespace crashpad */ +#define PROCESS_TYPE_STRUCT_END(struct_name) \ + private: \ + /* The static form of Read(). Populates the struct at |generic|. */ \ + static bool ReadInto(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + struct_name* generic); \ + \ + template \ + static bool ReadIntoInternal(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + struct_name* generic); \ + template \ + static bool ReadArrayIntoInternal(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + size_t count, \ + struct_name* generic); \ + size_t size_; \ + } \ + ; \ + } /* namespace process_types */ \ + } /* namespace crashpad */ #include "snapshot/mac/process_types/all.proctype" @@ -163,37 +164,37 @@ DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64) // remote process into the generic form. #define PROCESS_TYPE_STRUCT_DECLARE_INTERNAL 1 -#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \ - namespace crashpad { \ - namespace process_types { \ - namespace internal { \ - template \ - struct struct_name { \ - public: \ - using Long = typename Traits::Long; \ - using ULong = typename Traits::ULong; \ - using Pointer = typename Traits::Pointer; \ - using IntPtr = typename Traits::IntPtr; \ - using UIntPtr = typename Traits::UIntPtr; \ - using Reserved32_32Only = typename Traits::Reserved32_32Only; \ - using Reserved32_64Only = typename Traits::Reserved32_64Only; \ - using Reserved64_64Only = typename Traits::Reserved64_64Only; \ - using Nothing = typename Traits::Nothing; \ - \ - /* Read(), ReadArrayInto(), and Size() are as in the generic user-visible \ - * struct above. */ \ - bool Read(ProcessReader* process_reader, mach_vm_address_t address) { \ - return ReadInto(process_reader, address, this); \ - } \ - static bool ReadArrayInto(ProcessReader* process_reader, \ - mach_vm_address_t address, \ - size_t count, \ - struct_name* specific); \ - static size_t Size() { return sizeof(struct_name); } \ - \ - /* Translates a struct from the representation used in the remote process \ - * into the generic form. */ \ - void GenericizeInto(process_types::struct_name* generic, \ +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \ + namespace crashpad { \ + namespace process_types { \ + namespace internal { \ + template \ + struct struct_name { \ + public: \ + using Long = typename Traits::Long; \ + using ULong = typename Traits::ULong; \ + using Pointer = typename Traits::Pointer; \ + using IntPtr = typename Traits::IntPtr; \ + using UIntPtr = typename Traits::UIntPtr; \ + using Reserved32_32Only = typename Traits::Reserved32_32Only; \ + using Reserved32_64Only = typename Traits::Reserved32_64Only; \ + using Reserved64_64Only = typename Traits::Reserved64_64Only; \ + using Nothing = typename Traits::Nothing; \ + \ + /* Read(), ReadArrayInto(), and Size() are as in the generic user-visible \ + * struct above. */ \ + bool Read(ProcessReaderMac* process_reader, mach_vm_address_t address) { \ + return ReadInto(process_reader, address, this); \ + } \ + static bool ReadArrayInto(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + size_t count, \ + struct_name* specific); \ + static size_t Size() { return sizeof(struct_name); } \ + \ + /* Translates a struct from the representation used in the remote process \ + * into the generic form. */ \ + void GenericizeInto(process_types::struct_name* generic, \ size_t* specific_size); #define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \ @@ -209,16 +210,17 @@ DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64) /* MinimumSize() is as in the generic user-visible struct above. */ \ static size_t MinimumSize(); -#define PROCESS_TYPE_STRUCT_END(struct_name) \ - private: \ - /* ReadInto() is as in the generic user-visible struct above. */ \ - static bool ReadInto(ProcessReader* process_reader, \ - mach_vm_address_t address, \ - struct_name* specific); \ - }; \ - } /* namespace internal */ \ - } /* namespace process_types */ \ - } /* namespace crashpad */ +#define PROCESS_TYPE_STRUCT_END(struct_name) \ + private: \ + /* ReadInto() is as in the generic user-visible struct above. */ \ + static bool ReadInto(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + struct_name* specific); \ + } \ + ; \ + } /* namespace internal */ \ + } /* namespace process_types */ \ + } /* namespace crashpad */ #include "snapshot/mac/process_types/all.proctype" diff --git a/snapshot/mac/process_types/custom.cc b/snapshot/mac/process_types/custom.cc index b9cb0e79..7c7b172a 100644 --- a/snapshot/mac/process_types/custom.cc +++ b/snapshot/mac/process_types/custom.cc @@ -75,7 +75,7 @@ bool FieldAddressIfInRange(mach_vm_address_t address, } template -bool ReadIntoVersioned(ProcessReader* process_reader, +bool ReadIntoVersioned(ProcessReaderMac* process_reader, mach_vm_address_t address, T* specific) { mach_vm_address_t field_address; @@ -95,7 +95,7 @@ bool ReadIntoVersioned(ProcessReader* process_reader, } template -bool ReadIntoSized(ProcessReader* process_reader, +bool ReadIntoSized(ProcessReaderMac* process_reader, mach_vm_address_t address, T* specific) { mach_vm_address_t field_address; @@ -156,7 +156,7 @@ size_t dyld_all_image_infos::ExpectedSizeForVersion( // static template bool dyld_all_image_infos::ReadInto( - ProcessReader* process_reader, + ProcessReaderMac* process_reader, mach_vm_address_t address, dyld_all_image_infos* specific) { return ReadIntoVersioned(process_reader, address, specific); @@ -178,7 +178,7 @@ size_t crashreporter_annotations_t::ExpectedSizeForVersion( // static template bool crashreporter_annotations_t::ReadInto( - ProcessReader* process_reader, + ProcessReaderMac* process_reader, mach_vm_address_t address, crashreporter_annotations_t* specific) { return ReadIntoVersioned(process_reader, address, specific); @@ -186,30 +186,30 @@ bool crashreporter_annotations_t::ReadInto( // static template -bool CrashpadInfo::ReadInto(ProcessReader* process_reader, +bool CrashpadInfo::ReadInto(ProcessReaderMac* process_reader, mach_vm_address_t address, CrashpadInfo* specific) { return ReadIntoSized(process_reader, address, specific); } // Explicit template instantiation of the above. -#define PROCESS_TYPE_FLAVOR_TRAITS(lp_bits) \ - template size_t \ - dyld_all_image_infos::ExpectedSizeForVersion( \ - decltype(dyld_all_image_infos::version)); \ - template bool dyld_all_image_infos::ReadInto( \ - ProcessReader*, \ - mach_vm_address_t, \ - dyld_all_image_infos*); \ - template size_t \ - crashreporter_annotations_t::ExpectedSizeForVersion( \ - decltype(crashreporter_annotations_t::version)); \ - template bool crashreporter_annotations_t::ReadInto( \ - ProcessReader*, \ - mach_vm_address_t, \ - crashreporter_annotations_t*); \ - template bool CrashpadInfo::ReadInto( \ - ProcessReader*, mach_vm_address_t, CrashpadInfo*); +#define PROCESS_TYPE_FLAVOR_TRAITS(lp_bits) \ + template size_t \ + dyld_all_image_infos::ExpectedSizeForVersion( \ + decltype(dyld_all_image_infos::version)); \ + template bool dyld_all_image_infos::ReadInto( \ + ProcessReaderMac*, \ + mach_vm_address_t, \ + dyld_all_image_infos*); \ + template size_t \ + crashreporter_annotations_t::ExpectedSizeForVersion( \ + decltype(crashreporter_annotations_t::version)); \ + template bool crashreporter_annotations_t::ReadInto( \ + ProcessReaderMac*, \ + mach_vm_address_t, \ + crashreporter_annotations_t*); \ + template bool CrashpadInfo::ReadInto( \ + ProcessReaderMac*, mach_vm_address_t, CrashpadInfo*); #include "snapshot/mac/process_types/flavors.h" diff --git a/snapshot/mac/process_types_test.cc b/snapshot/mac/process_types_test.cc index 8ab15c8d..f116c4dd 100644 --- a/snapshot/mac/process_types_test.cc +++ b/snapshot/mac/process_types_test.cc @@ -101,7 +101,7 @@ TEST(ProcessTypes, DyldImagesSelf) { } #endif - ProcessReader process_reader; + ProcessReaderMac process_reader; ASSERT_TRUE(process_reader.Initialize(mach_task_self())); #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 diff --git a/snapshot/mac/system_snapshot_mac.cc b/snapshot/mac/system_snapshot_mac.cc index 140e7f49..21f254ee 100644 --- a/snapshot/mac/system_snapshot_mac.cc +++ b/snapshot/mac/system_snapshot_mac.cc @@ -25,7 +25,7 @@ #include "base/strings/stringprintf.h" #include "build/build_config.h" #include "snapshot/cpu_context.h" -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include "snapshot/posix/timezone.h" #include "util/mac/mac_util.h" #include "util/numeric/in_range_cast.h" @@ -104,7 +104,7 @@ SystemSnapshotMac::SystemSnapshotMac() SystemSnapshotMac::~SystemSnapshotMac() { } -void SystemSnapshotMac::Initialize(ProcessReader* process_reader, +void SystemSnapshotMac::Initialize(ProcessReaderMac* process_reader, const timeval* snapshot_time) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); diff --git a/snapshot/mac/system_snapshot_mac.h b/snapshot/mac/system_snapshot_mac.h index 2ac2ef90..62b2ae61 100644 --- a/snapshot/mac/system_snapshot_mac.h +++ b/snapshot/mac/system_snapshot_mac.h @@ -25,7 +25,7 @@ namespace crashpad { -class ProcessReader; +class ProcessReaderMac; namespace internal { @@ -40,9 +40,9 @@ class SystemSnapshotMac final : public SystemSnapshot { //! \param[in] process_reader A reader for the process being snapshotted. //! \n\n //! It seems odd that a system snapshot implementation would need a - //! ProcessReader, but some of the information reported about the system - //! depends on the process it’s being reported for. For example, the - //! architecture returned by GetCPUArchitecture() should be the + //! ProcessReaderMac, but some of the information reported about the + //! system depends on the process it’s being reported for. For example, + //! the architecture returned by GetCPUArchitecture() should be the //! architecture of the process, which may be different than the native //! architecture of the system: an x86_64 system can run both x86_64 and //! 32-bit x86 processes. @@ -53,7 +53,8 @@ class SystemSnapshotMac final : public SystemSnapshot { //! Otherwise, it would need to base its determination on the current //! time, which may be different than the snapshot time for snapshots //! generated around the daylight saving transition time. - void Initialize(ProcessReader* process_reader, const timeval* snapshot_time); + void Initialize(ProcessReaderMac* process_reader, + const timeval* snapshot_time); // SystemSnapshot: @@ -83,7 +84,7 @@ class SystemSnapshotMac final : public SystemSnapshot { private: std::string os_version_full_; std::string os_version_build_; - ProcessReader* process_reader_; // weak + ProcessReaderMac* process_reader_; // weak const timeval* snapshot_time_; // weak int os_version_major_; int os_version_minor_; diff --git a/snapshot/mac/system_snapshot_mac_test.cc b/snapshot/mac/system_snapshot_mac_test.cc index 646021b3..69048eb0 100644 --- a/snapshot/mac/system_snapshot_mac_test.cc +++ b/snapshot/mac/system_snapshot_mac_test.cc @@ -20,7 +20,7 @@ #include "build/build_config.h" #include "gtest/gtest.h" -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include "test/errors.h" #include "util/mac/mac_util.h" @@ -30,7 +30,7 @@ namespace { // SystemSnapshotMac objects would be cumbersome to construct in each test that // requires one, because of the repetitive and mechanical work necessary to set -// up a ProcessReader and timeval, along with the checks to verify that these +// up a ProcessReaderMac and timeval, along with the checks to verify that these // operations succeed. This test fixture class handles the initialization work // so that individual tests don’t have to. class SystemSnapshotMacTest : public testing::Test { @@ -55,7 +55,7 @@ class SystemSnapshotMacTest : public testing::Test { } private: - ProcessReader process_reader_; + ProcessReaderMac process_reader_; timeval snapshot_time_; internal::SystemSnapshotMac system_snapshot_; diff --git a/snapshot/mac/thread_snapshot_mac.cc b/snapshot/mac/thread_snapshot_mac.cc index b042f758..f45fc418 100644 --- a/snapshot/mac/thread_snapshot_mac.cc +++ b/snapshot/mac/thread_snapshot_mac.cc @@ -16,7 +16,7 @@ #include "base/logging.h" #include "snapshot/mac/cpu_context_mac.h" -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" namespace crashpad { namespace internal { @@ -38,8 +38,8 @@ ThreadSnapshotMac::~ThreadSnapshotMac() { } bool ThreadSnapshotMac::Initialize( - ProcessReader* process_reader, - const ProcessReader::Thread& process_reader_thread) { + ProcessReaderMac* process_reader, + const ProcessReaderMac::Thread& process_reader_thread) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); thread_ = process_reader_thread.port; diff --git a/snapshot/mac/thread_snapshot_mac.h b/snapshot/mac/thread_snapshot_mac.h index 03ca0fb5..8f5d722b 100644 --- a/snapshot/mac/thread_snapshot_mac.h +++ b/snapshot/mac/thread_snapshot_mac.h @@ -21,7 +21,7 @@ #include "base/macros.h" #include "build/build_config.h" #include "snapshot/cpu_context.h" -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include "snapshot/memory_snapshot.h" #include "snapshot/memory_snapshot_generic.h" #include "snapshot/thread_snapshot.h" @@ -29,7 +29,7 @@ namespace crashpad { -class ProcessReader; +class ProcessReaderMac; namespace internal { @@ -42,15 +42,15 @@ class ThreadSnapshotMac final : public ThreadSnapshot { //! \brief Initializes the object. //! - //! \param[in] process_reader A ProcessReader for the task containing the + //! \param[in] process_reader A ProcessReaderMac for the task containing the //! thread. - //! \param[in] process_reader_thread The thread within the ProcessReader for - //! which the snapshot should be created. + //! \param[in] process_reader_thread The thread within the ProcessReaderMac + //! for which the snapshot should be created. //! //! \return `true` if the snapshot could be created, `false` otherwise with //! an appropriate message logged. - bool Initialize(ProcessReader* process_reader, - const ProcessReader::Thread& process_reader_thread); + bool Initialize(ProcessReaderMac* process_reader, + const ProcessReaderMac::Thread& process_reader_thread); // ThreadSnapshot: @@ -70,7 +70,7 @@ class ThreadSnapshotMac final : public ThreadSnapshot { } context_union_; #endif CPUContext context_; - MemorySnapshotGeneric stack_; + MemorySnapshotGeneric stack_; uint64_t thread_id_; uint64_t thread_specific_data_address_; thread_t thread_; diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index 15fc34b3..b199841b 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -60,8 +60,8 @@ 'linux/debug_rendezvous.h', 'linux/exception_snapshot_linux.cc', 'linux/exception_snapshot_linux.h', - 'linux/process_reader.cc', - 'linux/process_reader.h', + 'linux/process_reader_linux.cc', + 'linux/process_reader_linux.h', 'linux/process_snapshot_linux.cc', 'linux/process_snapshot_linux.h', 'linux/signal_context.h', @@ -83,8 +83,8 @@ 'mac/mach_o_image_symbol_table_reader.h', 'mac/module_snapshot_mac.cc', 'mac/module_snapshot_mac.h', - 'mac/process_reader.cc', - 'mac/process_reader.h', + 'mac/process_reader_mac.cc', + 'mac/process_reader_mac.h', 'mac/process_snapshot_mac.cc', 'mac/process_snapshot_mac.h', 'mac/process_types.cc', diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index d8c0c08f..6f0fc90e 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -81,13 +81,13 @@ 'elf/elf_image_reader_test_note.S', 'linux/debug_rendezvous_test.cc', 'linux/exception_snapshot_linux_test.cc', - 'linux/process_reader_test.cc', + 'linux/process_reader_linux_test.cc', 'linux/system_snapshot_linux_test.cc', 'mac/cpu_context_mac_test.cc', 'mac/mach_o_image_annotations_reader_test.cc', 'mac/mach_o_image_reader_test.cc', 'mac/mach_o_image_segment_reader_test.cc', - 'mac/process_reader_test.cc', + 'mac/process_reader_mac_test.cc', 'mac/process_types_test.cc', 'mac/system_snapshot_mac_test.cc', 'minidump/process_snapshot_minidump_test.cc', diff --git a/snapshot/win/exception_snapshot_win.h b/snapshot/win/exception_snapshot_win.h index 0d216683..f6e29d9f 100644 --- a/snapshot/win/exception_snapshot_win.h +++ b/snapshot/win/exception_snapshot_win.h @@ -52,8 +52,8 @@ class ExceptionSnapshotWin final : public ExceptionSnapshot { //! \brief Initializes the object. //! - //! \param[in] process_reader A ProcessReader for the process that sustained - //! the exception. + //! \param[in] process_reader A ProcessReaderWin for the process that + //! sustained the exception. //! \param[in] thread_id The thread ID in which the exception occurred. //! \param[in] exception_pointers The address of an `EXCEPTION_POINTERS` //! record in the target process, passed through from the exception diff --git a/snapshot/win/module_snapshot_win.h b/snapshot/win/module_snapshot_win.h index 414f0a4d..693588db 100644 --- a/snapshot/win/module_snapshot_win.h +++ b/snapshot/win/module_snapshot_win.h @@ -47,10 +47,10 @@ class ModuleSnapshotWin final : public ModuleSnapshot { //! \brief Initializes the object. //! - //! \param[in] process_reader A ProcessReader for the task containing the - //! module. - //! \param[in] process_reader_module The module within the ProcessReader for - //! which the snapshot should be created. + //! \param[in] process_reader A ProcessReaderWin for the process containing + //! the module. + //! \param[in] process_reader_module The module within the ProcessReaderWin + //! for which the snapshot should be created. //! //! \return `true` if the snapshot could be created, `false` otherwise with //! an appropriate message logged. diff --git a/test/mac/dyld.cc b/test/mac/dyld.cc index 6fa2176a..fb2156ed 100644 --- a/test/mac/dyld.cc +++ b/test/mac/dyld.cc @@ -21,7 +21,7 @@ #include #include "base/logging.h" -#include "snapshot/mac/process_reader.h" +#include "snapshot/mac/process_reader_mac.h" #include "test/scoped_module_handle.h" #include "util/numeric/safe_assignment.h" @@ -74,7 +74,7 @@ const dyld_all_image_infos* DyldGetAllImageInfos() { #endif // On 10.13 and later, do it the hard way. - ProcessReader process_reader; + ProcessReaderMac process_reader; if (!process_reader.Initialize(mach_task_self())) { return nullptr; } From fa2a03fbdd0f8ca13511f66dded97a09cd08536e Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Fri, 23 Feb 2018 11:32:06 -0800 Subject: [PATCH 196/326] linux: Add CrashpadClient::SetFirstChanceExceptionHandler() Bug: crashpad:30 Change-Id: Idde7fd5c8ddec7c807c7720cd5b4958bf7f13fe8 Reviewed-on: https://chromium-review.googlesource.com/933363 Reviewed-by: Mark Mentovai --- client/crashpad_client.h | 26 ++++++++++++++++ client/crashpad_client_linux.cc | 35 +++++++++++++++++----- client/crashpad_client_linux_test.cc | 44 +++++++++++++++++++++------- 3 files changed, 87 insertions(+), 18 deletions(-) diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 1a48c181..3cd806a1 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -31,6 +31,9 @@ #elif defined(OS_WIN) #include #include "util/win/scoped_handle.h" +#elif defined(OS_LINUX) || defined(OS_ANDROID) +#include +#include #endif namespace crashpad { @@ -173,6 +176,8 @@ class CrashpadClient { //! \brief Requests that the handler capture a dump even though there hasn't //! been a crash. //! + //! A handler must have already been installed before calling this method. + //! //! TODO(jperaza): Floating point information in the context is zeroed out //! until CaptureContext() supports collecting that information. //! @@ -180,6 +185,27 @@ class CrashpadClient { //! CaptureContext() or similar. static void DumpWithoutCrash(NativeCPUContext* context); + //! \brief The type for custom handlers installed by clients. + using FirstChanceHandler = bool (*)(int, siginfo_t*, ucontext_t*); + + //! \brief Installs a custom crash signal handler which runs before the + //! currently installed Crashpad handler. + //! + //! Handling signals appropriately can be tricky and use of this method + //! should be avoided, if possible. + //! + //! A handler must have already been installed before calling this method. + //! + //! The custom handler runs in a signal handler context and must be safe for + //! that purpose. + //! + //! If the custom handler returns `true`, the signal is considered handled and + //! the signal handler returns. Otherwise, the currently installed Crashpad + //! signal handler is run. + //! + //! \param[in] handler The custom crash signal handler to install. + static void SetFirstChanceExceptionHandler(FirstChanceHandler handler); + #endif // OS_LINUX || OS_ANDROID || DOXYGEN #if defined(OS_MACOSX) || DOXYGEN diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc index 12558965..ca13c297 100644 --- a/client/crashpad_client_linux.cc +++ b/client/crashpad_client_linux.cc @@ -98,13 +98,19 @@ class SignalHandler { virtual void HandleCrashFatal(int signo, siginfo_t* siginfo, void* context) = 0; - virtual void HandleCrashNonFatal(int signo, + virtual bool HandleCrashNonFatal(int signo, siginfo_t* siginfo, void* context) = 0; + void SetFirstChanceHandler(CrashpadClient::FirstChanceHandler handler) { + first_chance_handler_ = handler; + } + protected: SignalHandler() = default; ~SignalHandler() = default; + + CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr; }; // Launches a single use handler to snapshot this process. @@ -125,9 +131,15 @@ class LaunchAtCrashHandler : public SignalHandler { return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr); } - void HandleCrashNonFatal(int signo, + bool HandleCrashNonFatal(int signo, siginfo_t* siginfo, void* context) override { + if (first_chance_handler_ && + first_chance_handler_( + signo, siginfo, static_cast(context))) { + return true; + } + exception_information_.siginfo_address = FromPointerCast( siginfo); @@ -140,7 +152,7 @@ class LaunchAtCrashHandler : public SignalHandler { pid_t pid = fork(); if (pid < 0) { - return; + return false; } if (pid == 0) { execv(argv_[0], const_cast(argv_.data())); @@ -149,10 +161,13 @@ class LaunchAtCrashHandler : public SignalHandler { int status; waitpid(pid, &status, 0); + return false; } void HandleCrashFatal(int signo, siginfo_t* siginfo, void* context) override { - HandleCrashNonFatal(signo, siginfo, context); + if (HandleCrashNonFatal(signo, siginfo, context)) { + return; + } Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr); } @@ -239,10 +254,7 @@ bool CrashpadClient::StartHandlerForClient( // static void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) { - if (!g_crash_handler) { - LOG(WARNING) << "No crash handler installed"; - return; - } + DCHECK(g_crash_handler); #if defined(ARCH_CPU_X86) memset(&context->__fpregs_mem, 0, sizeof(context->__fpregs_mem)); @@ -267,4 +279,11 @@ void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) { siginfo.si_signo, &siginfo, reinterpret_cast(context)); } +// static +void CrashpadClient::SetFirstChanceExceptionHandler( + FirstChanceHandler handler) { + DCHECK(g_crash_handler); + g_crash_handler->SetFirstChanceHandler(handler); +} + } // namespace crashpad diff --git a/client/crashpad_client_linux_test.cc b/client/crashpad_client_linux_test.cc index 1e07394a..9610cddf 100644 --- a/client/crashpad_client_linux_test.cc +++ b/client/crashpad_client_linux_test.cc @@ -39,6 +39,10 @@ namespace crashpad { namespace test { namespace { +bool HandleCrashSuccessfully(int, siginfo_t*, ucontext_t*) { + return true; +} + TEST(CrashpadClient, SimulateCrash) { ScopedTempDir temp_dir; @@ -53,21 +57,41 @@ TEST(CrashpadClient, SimulateCrash) { std::map(), std::vector())); - CRASHPAD_SIMULATE_CRASH(); - auto database = CrashReportDatabase::InitializeWithoutCreating(temp_dir.path()); ASSERT_TRUE(database); - std::vector reports; - ASSERT_EQ(database->GetPendingReports(&reports), - CrashReportDatabase::kNoError); - EXPECT_EQ(reports.size(), 0u); + { + CrashpadClient::SetFirstChanceExceptionHandler(HandleCrashSuccessfully); - reports.clear(); - ASSERT_EQ(database->GetCompletedReports(&reports), - CrashReportDatabase::kNoError); - EXPECT_EQ(reports.size(), 1u); + CRASHPAD_SIMULATE_CRASH(); + + std::vector reports; + ASSERT_EQ(database->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + EXPECT_EQ(reports.size(), 0u); + + reports.clear(); + ASSERT_EQ(database->GetCompletedReports(&reports), + CrashReportDatabase::kNoError); + EXPECT_EQ(reports.size(), 0u); + } + + { + CrashpadClient::SetFirstChanceExceptionHandler(nullptr); + + CRASHPAD_SIMULATE_CRASH(); + + std::vector reports; + ASSERT_EQ(database->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + EXPECT_EQ(reports.size(), 0u); + + reports.clear(); + ASSERT_EQ(database->GetCompletedReports(&reports), + CrashReportDatabase::kNoError); + EXPECT_EQ(reports.size(), 1u); + } } CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) { From cd3afe616e63728e1dca914eaffb5c5048a47164 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 23 Feb 2018 14:24:28 -0800 Subject: [PATCH 197/326] Linux: Pull and use clang toolchain in third_party Includes mini_chromium update including one change: b4128fb Linux GN: support override of clang to different path Complementary bot change at https://chromium-review.googlesource.com/c/chromium/tools/build/+/935668. Bug: crashpad:79, crashpad:220 Change-Id: I9228bf6786d94c34265e933e21e45a899816bb52 Reviewed-on: https://chromium-review.googlesource.com/935470 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- .gitignore | 2 ++ DEPS | 31 +++++++++++++++++++++++------ third_party/fuchsia/README.crashpad | 5 ++--- third_party/linux/README.crashpad | 3 +++ 4 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 third_party/linux/README.crashpad diff --git a/.gitignore b/.gitignore index e6daaab9..f673ecd4 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,8 @@ /third_party/fuchsia/qemu /third_party/fuchsia/sdk /third_party/gtest/gtest +/third_party/linux/.cipd +/third_party/linux/clang /third_party/gyp/gyp /third_party/mini_chromium/mini_chromium /third_party/zlib/zlib diff --git a/DEPS b/DEPS index bd137b66..e426728f 100644 --- a/DEPS +++ b/DEPS @@ -14,6 +14,7 @@ vars = { 'chromium_git': 'https://chromium.googlesource.com', + 'pull_linux_clang': False } deps = { @@ -28,7 +29,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '4e3b2c0fd5b18832e4221876941111cb12892d3b', + 'b4128fb9a0684369c9d144a2210496b7583c01a6', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', @@ -115,8 +116,27 @@ hooks = [ }, { # This uses “cipd install” so that mac-amd64 and linux-amd64 can coexist - # peacefully. “cipd ensure” would remove the Linux package when running on a - # macOS build host and vice-versa. https://crbug.com/789364. + # peacefully. “cipd ensure” would remove the macOS package when running on a + # Linux build host and vice-versa. https://crbug.com/789364. This package is + # only updated when the solution in .gclient includes an entry like: + # "custom_vars": { "pull_linux_clang": True } + 'name': 'clang_linux', + 'pattern': '.', + 'condition': 'checkout_linux and pull_linux_clang', + 'action': [ + 'cipd', + 'install', + # sic, using Fuchsia team's generic build of clang for linux-amd64 to + # build for linux-amd64 target too. + 'fuchsia/clang/linux-amd64', + 'latest', + '-root', 'crashpad/third_party/linux/clang/linux-amd64', + '-log-level', 'info', + ], + }, + { + # Same rationale for using "install" rather than "ensure" as for first clang + # package. https://crbug.com/789364. 'name': 'fuchsia_clang_mac', 'pattern': '.', 'condition': 'checkout_fuchsia and host_os == "mac"', @@ -130,9 +150,8 @@ hooks = [ ], }, { - # This uses “cipd install” so that mac-amd64 and linux-amd64 can coexist - # peacefully. “cipd ensure” would remove the macOS package when running on a - # Linux build host and vice-versa. https://crbug.com/789364. + # Same rationale for using "install" rather than "ensure" as for first clang + # package. https://crbug.com/789364. 'name': 'fuchsia_clang_linux', 'pattern': '.', 'condition': 'checkout_fuchsia and host_os == "linux"', diff --git a/third_party/fuchsia/README.crashpad b/third_party/fuchsia/README.crashpad index fc4a514a..8bf0a914 100644 --- a/third_party/fuchsia/README.crashpad +++ b/third_party/fuchsia/README.crashpad @@ -1,4 +1,3 @@ This directory is a placeholder for Fuchsia tools that will be downloaded by -CIPD (https://github.com/luci/luci-go/tree/master/cipd). The toolchain.ensure -files specifies which CIPD packages to retrieve, at which versions, and where -they're stored locally. The CIPD update happens as part of gclient runhooks. +CIPD (https://github.com/luci/luci-go/tree/master/cipd). The CIPD update happens +as part of gclient runhooks. diff --git a/third_party/linux/README.crashpad b/third_party/linux/README.crashpad new file mode 100644 index 00000000..8bf0a914 --- /dev/null +++ b/third_party/linux/README.crashpad @@ -0,0 +1,3 @@ +This directory is a placeholder for Fuchsia tools that will be downloaded by +CIPD (https://github.com/luci/luci-go/tree/master/cipd). The CIPD update happens +as part of gclient runhooks. From 8e80a575d15f8ae333c5014d68d4a7e56b5723b5 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 23 Feb 2018 20:27:50 -0800 Subject: [PATCH 198/326] Linux: Pull a sysroot if pulling a local clang Didn't actually end up being too bad. Also requires setting GN arg of target_sysroot = "//third_party/linux/sysroot" when building. Bug: crashpad:220 Change-Id: I4d4b282f165d454b5d32fc8cc11287ff665b943d Reviewed-on: https://chromium-review.googlesource.com/935981 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- .gitignore | 1 + DEPS | 10 +++++ build/install_linux_sysroot.py | 74 ++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100755 build/install_linux_sysroot.py diff --git a/.gitignore b/.gitignore index f673ecd4..d225542e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ /third_party/gtest/gtest /third_party/linux/.cipd /third_party/linux/clang +/third_party/linux/sysroot /third_party/gyp/gyp /third_party/mini_chromium/mini_chromium /third_party/zlib/zlib diff --git a/DEPS b/DEPS index e426728f..bae97c3a 100644 --- a/DEPS +++ b/DEPS @@ -134,6 +134,16 @@ hooks = [ '-log-level', 'info', ], }, + { + # If using a local clang ("pull_linux_clang" above), also pull down a + # sysroot. + 'name': 'clang_linux', + 'pattern': '.', + 'condition': 'checkout_linux and pull_linux_clang', + 'action': [ + 'crashpad/build/install_linux_sysroot.py', + ], + }, { # Same rationale for using "install" rather than "ensure" as for first clang # package. https://crbug.com/789364. diff --git a/build/install_linux_sysroot.py b/build/install_linux_sysroot.py new file mode 100755 index 00000000..afa88157 --- /dev/null +++ b/build/install_linux_sysroot.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python + +# Copyright 2018 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. + +# Various code adapted from: +# https://cs.chromium.org/chromium/src/build/linux/sysroot_scripts/install-sysroot.py + +import os +import shutil +import subprocess +import sys +import urllib2 + + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) + +# Sysroot revision from: +# https://cs.chromium.org/chromium/src/build/linux/sysroot_scripts/sysroots.json +SERVER = 'https://commondatastorage.googleapis.com' +PATH = 'chrome-linux-sysroot/toolchain' +REVISION = '3c248ba4290a5ad07085b7af07e6785bf1ae5b66' +FILENAME = 'debian_stretch_amd64_sysroot.tar.xz' + +def main(): + url = '%s/%s/%s/%s' % (SERVER, PATH, REVISION, FILENAME) + + sysroot = os.path.join(SCRIPT_DIR, os.pardir, + 'third_party', 'linux', 'sysroot') + + stamp = os.path.join(sysroot, '.stamp') + if os.path.exists(stamp): + with open(stamp) as s: + if s.read() == url: + return + + print 'Installing Debian root image from %s' % url + + if os.path.isdir(sysroot): + shutil.rmtree(sysroot) + os.mkdir(sysroot) + tarball = os.path.join(sysroot, FILENAME) + print 'Downloading %s' % url + + for _ in range(3): + response = urllib2.urlopen(url) + with open(tarball, 'wb') as f: + f.write(response.read()) + break + else: + raise Exception('Failed to download %s' % url) + + subprocess.check_call(['tar', 'xf', tarball, '-C', sysroot]) + + os.remove(tarball) + + with open(stamp, 'w') as s: + s.write(url) + + +if __name__ == '__main__': + main() + sys.exit(0) From fae18c2fc4923916cfbf2c552d6782b4b8d9f8a4 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 22 Feb 2018 16:48:50 -0800 Subject: [PATCH 199/326] fuchsia: Add implementation of ThreadSnapshot This is mostly empty except for the ID, until I concoct a way to get the stack out of Fuchsia, and implement context capture. Bug: crashpad:196 Change-Id: I26d0622d44aefba88750f7ec6feb1a6e95467208 Reviewed-on: https://chromium-review.googlesource.com/932941 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- snapshot/BUILD.gn | 2 + snapshot/fuchsia/process_snapshot_fuchsia.cc | 13 +++ snapshot/fuchsia/process_snapshot_fuchsia.h | 5 + snapshot/fuchsia/thread_snapshot_fuchsia.cc | 96 ++++++++++++++++++++ snapshot/fuchsia/thread_snapshot_fuchsia.h | 81 +++++++++++++++++ 5 files changed, 197 insertions(+) create mode 100644 snapshot/fuchsia/thread_snapshot_fuchsia.cc create mode 100644 snapshot/fuchsia/thread_snapshot_fuchsia.h diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 84df23f1..5ccc735e 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -179,6 +179,8 @@ static_library("snapshot") { "fuchsia/process_reader_fuchsia.h", "fuchsia/process_snapshot_fuchsia.cc", "fuchsia/process_snapshot_fuchsia.h", + "fuchsia/thread_snapshot_fuchsia.cc", + "fuchsia/thread_snapshot_fuchsia.h", ] } diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc index f3d22907..9d0897d8 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -29,6 +29,7 @@ bool ProcessSnapshotFuchsia::Initialize(zx_handle_t process) { return false; } + InitializeThreads(); InitializeModules(); INITIALIZATION_STATE_SET_VALID(initialized_); @@ -170,6 +171,18 @@ std::vector ProcessSnapshotFuchsia::ExtraMemory() const { return std::vector(); } +void ProcessSnapshotFuchsia::InitializeThreads() { + const std::vector& process_reader_threads = + process_reader_.Threads(); + for (const ProcessReaderFuchsia::Thread& process_reader_thread : + process_reader_threads) { + auto thread = std::make_unique(); + if (thread->Initialize(&process_reader_, process_reader_thread)) { + threads_.push_back(std::move(thread)); + } + } +} + void ProcessSnapshotFuchsia::InitializeModules() { for (const ProcessReaderFuchsia::Module& reader_module : process_reader_.Modules()) { diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.h b/snapshot/fuchsia/process_snapshot_fuchsia.h index d94dec7b..eb077330 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.h +++ b/snapshot/fuchsia/process_snapshot_fuchsia.h @@ -25,6 +25,7 @@ #include "snapshot/elf/elf_image_reader.h" #include "snapshot/elf/module_snapshot_elf.h" #include "snapshot/fuchsia/process_reader_fuchsia.h" +#include "snapshot/fuchsia/thread_snapshot_fuchsia.h" #include "snapshot/process_snapshot.h" #include "snapshot/unloaded_module_snapshot.h" #include "util/misc/initialization_state_dcheck.h" @@ -73,9 +74,13 @@ class ProcessSnapshotFuchsia : public ProcessSnapshot { std::vector ExtraMemory() const override; private: + // Initializes threads_ on behalf of Initialize(). + void InitializeThreads(); + // Initializes modules_ on behalf of Initialize(). void InitializeModules(); + std::vector> threads_; std::vector> modules_; ProcessReaderFuchsia process_reader_; std::map annotations_simple_map_; diff --git a/snapshot/fuchsia/thread_snapshot_fuchsia.cc b/snapshot/fuchsia/thread_snapshot_fuchsia.cc new file mode 100644 index 00000000..ce1aeb33 --- /dev/null +++ b/snapshot/fuchsia/thread_snapshot_fuchsia.cc @@ -0,0 +1,96 @@ +// Copyright 2018 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/fuchsia/thread_snapshot_fuchsia.h" + +#include "base/logging.h" + +namespace crashpad { +namespace internal { + +ThreadSnapshotFuchsia::ThreadSnapshotFuchsia() + : ThreadSnapshot(), + context_arch_(), + context_(), + stack_(), + thread_id_(ZX_KOID_INVALID), + thread_specific_data_address_(0), + initialized_() {} + +ThreadSnapshotFuchsia::~ThreadSnapshotFuchsia() {} + +bool ThreadSnapshotFuchsia::Initialize( + ProcessReaderFuchsia* process_reader, + const ProcessReaderFuchsia::Thread& thread) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + +#if defined(ARCH_CPU_X86_64) + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_arch_; +// TODO(scottmg): Implement context capture for x64. +#elif defined(ARCH_CPU_ARM64) + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_arch_; +// TODO(scottmg): Implement context capture for arm64. +#else +#error Port. +#endif + + // TODO(scottmg): https://crashpad.chromium.org/bug/196. Initialize stack_ and + // TLS address here. API request for stack range filed upstream at ZX-1748. + + thread_id_ = thread.id; + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ThreadSnapshotFuchsia::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +const MemorySnapshot* ThreadSnapshotFuchsia::Stack() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &stack_; +} + +uint64_t ThreadSnapshotFuchsia::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +int ThreadSnapshotFuchsia::SuspendCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // There is not (currently) a suspend count for threads on Fuchsia. + return 0; +} + +int ThreadSnapshotFuchsia::Priority() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // There is not (currently) thread priorities on Fuchsia. + return 0; +} + +uint64_t ThreadSnapshotFuchsia::ThreadSpecificDataAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_specific_data_address_; +} + +std::vector ThreadSnapshotFuchsia::ExtraMemory() const { + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/fuchsia/thread_snapshot_fuchsia.h b/snapshot/fuchsia/thread_snapshot_fuchsia.h new file mode 100644 index 00000000..db975976 --- /dev/null +++ b/snapshot/fuchsia/thread_snapshot_fuchsia.h @@ -0,0 +1,81 @@ +// Copyright 2018 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_FUCHSIA_THREAD_SNAPSHOT_FUCHSIA_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_THREAD_SNAPSHOT_FUCHSIA_H_ + +#include +#include + +#include "base/macros.h" +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/fuchsia/process_reader_fuchsia.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/memory_snapshot_generic.h" +#include "snapshot/thread_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A ThreadSnapshot of a thread on a Fuchsia system. +class ThreadSnapshotFuchsia final : public ThreadSnapshot { + public: + ThreadSnapshotFuchsia(); + ~ThreadSnapshotFuchsia() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReaderFuchsia for the process + //! containing the thread. + //! \param[in] thread The thread within the ProcessReaderFuchsia for + //! which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! a message logged. + bool Initialize(ProcessReaderFuchsia* process_reader, + const ProcessReaderFuchsia::Thread& thread); + + // ThreadSnapshot: + + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector ExtraMemory() const override; + + private: +#if defined(ARCH_CPU_X86_64) + CPUContextX86_64 context_arch_; +#elif defined(ARCH_CPU_ARM64) + CPUContextARM64 context_arch_; +#else +#error Port. +#endif + CPUContext context_; + MemorySnapshotGeneric stack_; + zx_koid_t thread_id_; + zx_vaddr_t thread_specific_data_address_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotFuchsia); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_THREAD_SNAPSHOT_FUCHSIA_H_ From a869ae18d256e9db138b76f3e2ddaab0bb1100ac Mon Sep 17 00:00:00 2001 From: Victor Costan Date: Sun, 25 Feb 2018 23:19:23 -0800 Subject: [PATCH 200/326] Workaround for death test failure when in threadsafe mode on Mac. Google Test has recently switched the default death test style from "fast" to "threadsafe". This is a better default, and Chrome will adopt it on all platforms except for Android. In threadsafe mode, the death test in client/simple_string_dictionary_test.cc consistently crashes with the wrong expectation on Mac. Fortunately, breaking the test up into two smaller tests makes the failures go away, and also adds a bit of clarity into what is being tested. Bug: crashpad:221 Change-Id: I2416647948815cfe46a003da8209af8b7278de2a Reviewed-on: https://chromium-review.googlesource.com/936043 Commit-Queue: Victor Costan Reviewed-by: Scott Graham --- client/simple_string_dictionary_test.cc | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/client/simple_string_dictionary_test.cc b/client/simple_string_dictionary_test.cc index 6f85e854..5fdeb5bf 100644 --- a/client/simple_string_dictionary_test.cc +++ b/client/simple_string_dictionary_test.cc @@ -253,21 +253,30 @@ TEST(SimpleStringDictionary, OutOfSpace) { #if DCHECK_IS_ON() -TEST(SimpleStringDictionaryDeathTest, NullKey) { +TEST(SimpleStringDictionaryDeathTest, SetKeyValueWithNullKey) { TSimpleStringDictionary<4, 6, 6> map; ASSERT_DEATH_CHECK(map.SetKeyValue(nullptr, "hello"), "key"); +} +TEST(SimpleStringDictionaryDeathTest, GetValueForKeyWithNullKey) { + TSimpleStringDictionary<4, 6, 6> map; map.SetKeyValue("hi", "there"); ASSERT_DEATH_CHECK(map.GetValueForKey(nullptr), "key"); EXPECT_STREQ("there", map.GetValueForKey("hi")); - - ASSERT_DEATH_CHECK(map.GetValueForKey(nullptr), "key"); - map.RemoveKey("hi"); - EXPECT_EQ(map.GetCount(), 0u); } #endif +// The tests above, without DEATH_CHECK assertions. +TEST(SimpleStringDictionaryDeathTest, GetValueForKeyWithoutNullKey) { + TSimpleStringDictionary<4, 6, 6> map; + + map.SetKeyValue("hi", "there"); + EXPECT_STREQ("there", map.GetValueForKey("hi")); + map.RemoveKey("hi"); + EXPECT_EQ(map.GetCount(), 0u); +} + } // namespace } // namespace test } // namespace crashpad From 8a12f589305072ed35acbe835260aafd3c10a788 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 26 Feb 2018 09:34:37 -0800 Subject: [PATCH 201/326] fuchsia: Take bots off CQ pending flake investigation Bug: crashpad:219 Change-Id: I98c4edbff5e5739aa3f4bb89e2e0ecf2f79206bf Reviewed-on: https://chromium-review.googlesource.com/937809 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- infra/config/cq.cfg | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg index 42b56d9a..8328d0e5 100644 --- a/infra/config/cq.cfg +++ b/infra/config/cq.cfg @@ -37,8 +37,10 @@ verifiers { builders { name: "crashpad_try_mac_rel" } builders { name: "crashpad_try_win_dbg" } builders { name: "crashpad_try_win_rel" } - builders { name: "crashpad_try_fuchsia_x64_dbg" } - builders { name: "crashpad_try_fuchsia_x64_rel" } + # https://bugs.chromium.org/p/crashpad/issues/detail?id=219 QEMU runs are + # flaking; remove from CQ while being investigated. + #builders { name: "crashpad_try_fuchsia_x64_dbg" } + #builders { name: "crashpad_try_fuchsia_x64_rel" } # https://crbug.com/743139 - disabled until we can move these to swarming, # at which point we can just remove them. #builders { name: "crashpad_try_win_x86_dbg" } From 9affa2a0e72db4314190f6dd46960f36fb954fc7 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 26 Feb 2018 14:07:09 -0800 Subject: [PATCH 202/326] Optionally stub out the libcurl-based implementation of HTTPTransport Bug: crashpad:220 Change-Id: I6556cd5a32ea95c3dc8c5906e0857c83dc88cd9a Reviewed-on: https://chromium-review.googlesource.com/938492 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza Reviewed-by: Mark Mentovai --- util/BUILD.gn | 26 ++++++++++++++----- ...port_fuchsia.cc => http_transport_none.cc} | 0 2 files changed, 20 insertions(+), 6 deletions(-) rename util/net/{http_transport_fuchsia.cc => http_transport_none.cc} (100%) diff --git a/util/BUILD.gn b/util/BUILD.gn index 342f0f63..3bf59081 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -14,6 +14,13 @@ import("../build/crashpad_buildconfig.gni") +declare_args() { + if (crashpad_is_linux) { + # Whether the libcurl-based HTTPTransport implementation should be built. + enable_http_transport_libcurl = true + } +} + if (crashpad_is_mac) { if (crashpad_is_in_chromium) { import("//build/config/sysroot.gni") @@ -245,7 +252,11 @@ static_library("util") { } if (crashpad_is_linux) { - sources += [ "net/http_transport_libcurl.cc" ] + if (enable_http_transport_libcurl) { + sources += [ "net/http_transport_libcurl.cc" ] + } else { + sources += [ "net/http_transport_none.cc" ] + } } if (crashpad_is_linux || crashpad_is_android) { @@ -377,7 +388,7 @@ static_library("util") { if (crashpad_is_fuchsia) { sources += [ "misc/paths_fuchsia.cc", - "net/http_transport_fuchsia.cc", + "net/http_transport_none.cc", "process/process_memory_fuchsia.cc", "process/process_memory_fuchsia.h", ] @@ -408,7 +419,7 @@ static_library("util") { include_dirs += [ "$root_build_dir/gen" ] } - if (crashpad_is_linux) { + if (crashpad_is_linux && enable_http_transport_libcurl) { libs = [ "curl" ] } @@ -478,7 +489,8 @@ source_set("util_test") { ] } - if (!crashpad_is_android && !crashpad_is_fuchsia) { + if (!crashpad_is_android && !crashpad_is_fuchsia && + (!crashpad_is_linux || enable_http_transport_libcurl)) { # Android and Fuchsia will each require an HTTPTransport implementation # (libcurl isn’t in either’s SDK) and a solution to # http_transport_test_server.py, because Python isn’t available on either. @@ -486,8 +498,10 @@ source_set("util_test") { # the build host with a method to forward requests from the device to the # host. # - # TODO(scottmg): Fuchsia will also require an implementation of - # MultiprocessExec for testing. + # Linux optionally compiles in a libcurl-based HTTPTransport, but since curl + # isn't in a base Debian sysroot (which is what Chromium builds against), + # maintain an option to exclude that, for now. + # https://crashpad.chromium.org/bug/220. sources += [ "net/http_transport_test.cc" ] } diff --git a/util/net/http_transport_fuchsia.cc b/util/net/http_transport_none.cc similarity index 100% rename from util/net/http_transport_fuchsia.cc rename to util/net/http_transport_none.cc From dec23bef576966dc03f60179d39cc32d5414ddfb Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 27 Feb 2018 15:29:38 -0800 Subject: [PATCH 203/326] win gn: reintroduce flags to disable warnings These flags were moved to mini_chromium's build/BUILD.gn, but that configuration is not present when building in chromium. Change-Id: I0d03c7461869882cf2ee7544ecd3d100eb189160 Reviewed-on: https://chromium-review.googlesource.com/940436 Reviewed-by: Scott Graham Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- handler/BUILD.gn | 2 ++ util/BUILD.gn | 2 ++ 2 files changed, 4 insertions(+) diff --git a/handler/BUILD.gn b/handler/BUILD.gn index c817393b..b3370fca 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -241,6 +241,8 @@ if (crashpad_is_win) { "win/crashy_signal.cc", ] + cflags = [ "/wd4702" ] # Unreachable code. + deps = [ "../client", "../third_party/mini_chromium:base", diff --git a/util/BUILD.gn b/util/BUILD.gn index 3bf59081..bb3a9e48 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -430,6 +430,8 @@ static_library("util") { "winhttp.lib", ] + cflags = [ "/wd4201" ] # nonstandard extension used: nameless struct/union. + if (current_cpu == "x86") { asmflags = [ "/safeseh" ] } From 746ce1a637038e4dbc2f6d5616d4d233f9d9b47b Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 28 Feb 2018 10:58:33 -0800 Subject: [PATCH 204/326] Roll mini_chromium to 987bde8 Includes: 987bde8 Linux GN: Optionally link statically against libstdc++ Bug: crashpad:30, crashpad:79, crashpad:220 Change-Id: If15d2224239166138aa5dcfe531ff269b7ed22fe Reviewed-on: https://chromium-review.googlesource.com/941543 Reviewed-by: Mark Mentovai Commit-Queue: Scott Graham --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index bae97c3a..e2204bc6 100644 --- a/DEPS +++ b/DEPS @@ -29,7 +29,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'b4128fb9a0684369c9d144a2210496b7583c01a6', + '987bde826c04158c11190f3764db6b9f5ce870bb', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', From 493e29bc3df0c6e4507537a580e0b4687705e0b1 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 28 Feb 2018 11:33:42 -0800 Subject: [PATCH 205/326] win: Use correct format specifier This caused an error with clang, but not msvc. At first I thought this might be a discrepancy between the warning levels used, but it appears msvc was okay with this because ints are the same size as longs. Change-Id: I798284fef9aa70b1bfda73308b9babe1779e8f4b Reviewed-on: https://chromium-review.googlesource.com/941723 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- handler/win/loader_lock_dll.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler/win/loader_lock_dll.cc b/handler/win/loader_lock_dll.cc index cfa098b2..63c145cf 100644 --- a/handler/win/loader_lock_dll.cc +++ b/handler/win/loader_lock_dll.cc @@ -55,7 +55,7 @@ __declspec(noreturn) void LogFatal(const char* file, va_end(va); if (get_last_error) { - fprintf(stderr, ": error %u", last_error); + fprintf(stderr, ": error %lu", last_error); } fputs("\n", stderr); From a45e88602b90899d16a1f9b09866f3655a7682f9 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 28 Feb 2018 13:15:49 -0800 Subject: [PATCH 206/326] Skip ELF notes with a p_vaddr of zero Don't attempt to read data if the note isn't in an allocated segment. See investigation starting at https://bugs.chromium.org/p/crashpad/issues/detail?id=220#c27 for details. Bug: crashpad:220, crashpad:30, crashpad:196 Change-Id: I60eaacb83ad00ef33bde9079d25cc23a59bdf2c8 Reviewed-on: https://chromium-review.googlesource.com/941507 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- snapshot/elf/elf_image_reader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapshot/elf/elf_image_reader.cc b/snapshot/elf/elf_image_reader.cc index 2fc96089..c6816605 100644 --- a/snapshot/elf/elf_image_reader.cc +++ b/snapshot/elf/elf_image_reader.cc @@ -164,7 +164,7 @@ class ElfImageReader::ProgramHeaderTableSpecific VMSize* size) const override { INITIALIZATION_STATE_DCHECK_VALID(initialized_); for (size_t index = *start_index; index < table_.size(); ++index) { - if (table_[index].p_type == PT_NOTE) { + if (table_[index].p_type == PT_NOTE && table_[index].p_vaddr != 0) { *start_index = index + 1; *address = table_[index].p_vaddr; *size = table_[index].p_memsz; From 71d90608828da0b6c987f018c96306bfa964a65a Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 28 Feb 2018 13:27:41 -0800 Subject: [PATCH 207/326] Add Linux trybots to CQ Bug: crashpad:220 Change-Id: I46e470ba6966f4b07165fc30828f2a0387444a29 Reviewed-on: https://chromium-review.googlesource.com/941510 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- infra/config/cq.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg index 8328d0e5..64213fc3 100644 --- a/infra/config/cq.cfg +++ b/infra/config/cq.cfg @@ -37,6 +37,8 @@ verifiers { builders { name: "crashpad_try_mac_rel" } builders { name: "crashpad_try_win_dbg" } builders { name: "crashpad_try_win_rel" } + builders { name: "crashpad_try_linux_dbg" } + builders { name: "crashpad_try_linux_rel" } # https://bugs.chromium.org/p/crashpad/issues/detail?id=219 QEMU runs are # flaking; remove from CQ while being investigated. #builders { name: "crashpad_try_fuchsia_x64_dbg" } From 449506d59c6af9e03bd3cdbc65cdca8df2627202 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 1 Mar 2018 09:36:59 -0800 Subject: [PATCH 208/326] Roll mini_chromium to ef0df11 Includes: win: remove flags not present in chromium Change-Id: Ib5ec58ea8e406a9c151bc5901ebaa418fb8c8c20 Reviewed-on: https://chromium-review.googlesource.com/943722 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index e2204bc6..d118f600 100644 --- a/DEPS +++ b/DEPS @@ -29,7 +29,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '987bde826c04158c11190f3764db6b9f5ce870bb', + 'ef0df1119b40cfa2773d5960e239d4b960310869', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', From 82777cff584837dfa6e0d6a828f45b0565359022 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 1 Mar 2018 13:04:17 -0800 Subject: [PATCH 209/326] win: fix warnings when building with clang Change-Id: I15eeeb3d16490054351c1c641acb4159d0a13d89 Reviewed-on: https://chromium-review.googlesource.com/944192 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- handler/win/crash_other_program.cc | 2 +- handler/win/crashy_test_program.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/handler/win/crash_other_program.cc b/handler/win/crash_other_program.cc index 55c06994..bbd25af5 100644 --- a/handler/win/crash_other_program.cc +++ b/handler/win/crash_other_program.cc @@ -122,7 +122,7 @@ int CrashOtherProgram(int argc, wchar_t* argv[]) { DWORD exit_code = child.WaitForExit(); if (exit_code != expect_exit_code) { LOG(ERROR) << base::StringPrintf( - "incorrect exit code, expected 0x%x, observed 0x%x", + "incorrect exit code, expected 0x%lx, observed 0x%lx", expect_exit_code, exit_code); return EXIT_FAILURE; diff --git a/handler/win/crashy_test_program.cc b/handler/win/crashy_test_program.cc index a5532da8..5555cac3 100644 --- a/handler/win/crashy_test_program.cc +++ b/handler/win/crashy_test_program.cc @@ -145,7 +145,7 @@ void AllocateExtraMemoryToBeSaved( constexpr size_t kNumInts = 2000; int* extra_memory = new int[kNumInts]; g_extra_memory_pointer = extra_memory; - for (int i = 0; i < kNumInts; ++i) + for (size_t i = 0; i < kNumInts; ++i) extra_memory[i] = i * 13 + 2; extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumInts); extra_ranges->Insert(&g_extra_memory_pointer, sizeof(g_extra_memory_pointer)); @@ -157,7 +157,7 @@ void AllocateExtraUnsavedMemory(crashpad::SimpleAddressRangeBag* extra_ranges) { constexpr size_t kNumInts = 2000; int* extra_memory = new int[kNumInts]; g_extra_memory_not_saved = extra_memory; - for (int i = 0; i < kNumInts; ++i) + for (size_t i = 0; i < kNumInts; ++i) extra_memory[i] = i * 17 + 7; extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumInts); extra_ranges->Insert(&g_extra_memory_not_saved, From 23b2156fb694fcc8b2da03fa9e1d02cf81f53fe9 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Fri, 2 Mar 2018 21:45:29 -0500 Subject: [PATCH 210/326] =?UTF-8?q?Don=E2=80=99t=20read=20beyond=20a=20Str?= =?UTF-8?q?ingPiece=E2=80=99s=20bounds=20in=20StringToNumber()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implementations requires NUL-termination for the underlying buffer, so just use std::string everywhere, rather than trying to detect whether strings are already NUL-terminated. Bug: chromium:817982, chromium:818376 Change-Id: I4c8dcb5ed15ebca4c531f9a5d0ee865228dc0959 Reviewed-on: https://chromium-review.googlesource.com/947742 Reviewed-by: Mark Mentovai Commit-Queue: Mark Mentovai --- util/linux/memory_map.cc | 2 +- util/mach/symbolic_constants_mach.cc | 11 +++++++---- util/posix/symbolic_constants_posix.cc | 2 +- util/stdlib/string_number_conversion.cc | 18 +++++------------- util/stdlib/string_number_conversion.h | 10 +++++----- util/stdlib/string_number_conversion_test.cc | 12 ++---------- 6 files changed, 21 insertions(+), 34 deletions(-) diff --git a/util/linux/memory_map.cc b/util/linux/memory_map.cc index cb7bfe1e..f2df865a 100644 --- a/util/linux/memory_map.cc +++ b/util/linux/memory_map.cc @@ -36,7 +36,7 @@ namespace { // Simply adding a StringToNumber for longs doesn't work since sometimes long // and int64_t are actually the same type, resulting in a redefinition error. template -bool LocalStringToNumber(const base::StringPiece& string, Type* number) { +bool LocalStringToNumber(const std::string& string, Type* number) { static_assert(sizeof(Type) == sizeof(int) || sizeof(Type) == sizeof(int64_t), "Unexpected Type size"); diff --git a/util/mach/symbolic_constants_mach.cc b/util/mach/symbolic_constants_mach.cc index b7296be2..8f95915e 100644 --- a/util/mach/symbolic_constants_mach.cc +++ b/util/mach/symbolic_constants_mach.cc @@ -239,7 +239,8 @@ bool StringToException(const base::StringPiece& string, } if (options & kAllowNumber) { - return StringToNumber(string, reinterpret_cast(exception)); + return StringToNumber(std::string(string.data(), string.length()), + reinterpret_cast(exception)); } return false; @@ -352,7 +353,7 @@ bool StringToExceptionMask(const base::StringPiece& string, } if (options & kAllowNumber) { - return StringToNumber(string, + return StringToNumber(std::string(string.data(), string.length()), reinterpret_cast(exception_mask)); } @@ -452,7 +453,8 @@ bool StringToExceptionBehavior(const base::StringPiece& string, if (options & kAllowNumber) { exception_behavior_t temp_behavior; - if (!StringToNumber(sp, reinterpret_cast(&temp_behavior))) { + if (!StringToNumber(std::string(sp.data(), sp.length()), + reinterpret_cast(&temp_behavior))) { return false; } build_behavior |= temp_behavior; @@ -539,7 +541,8 @@ bool StringToThreadStateFlavor(const base::StringPiece& string, } if (options & kAllowNumber) { - return StringToNumber(string, reinterpret_cast(flavor)); + return StringToNumber(std::string(string.data(), string.length()), + reinterpret_cast(flavor)); } return false; diff --git a/util/posix/symbolic_constants_posix.cc b/util/posix/symbolic_constants_posix.cc index 51cc583b..c973c146 100644 --- a/util/posix/symbolic_constants_posix.cc +++ b/util/posix/symbolic_constants_posix.cc @@ -161,7 +161,7 @@ bool StringToSignal(const base::StringPiece& string, } if (options & kAllowNumber) { - return StringToNumber(string, signal); + return StringToNumber(std::string(string.data(), string.length()), signal); } return false; diff --git a/util/stdlib/string_number_conversion.cc b/util/stdlib/string_number_conversion.cc index c3352bee..859a8333 100644 --- a/util/stdlib/string_number_conversion.cc +++ b/util/stdlib/string_number_conversion.cc @@ -116,7 +116,7 @@ struct StringToUnsignedInt64Traits }; template -bool StringToIntegerInternal(const base::StringPiece& string, +bool StringToIntegerInternal(const std::string& string, typename Traits::IntType* number) { using IntType = typename Traits::IntType; using LongType = typename Traits::LongType; @@ -127,14 +127,6 @@ bool StringToIntegerInternal(const base::StringPiece& string, return false; } - if (string[string.length()] != '\0') { - // The implementations use the C standard library’s conversion routines, - // which rely on the strings having a trailing NUL character. std::string - // will NUL-terminate. - std::string terminated_string(string.data(), string.length()); - return StringToIntegerInternal(terminated_string, number); - } - errno = 0; char* end; LongType result = Traits::Convert(string.data(), &end, 0); @@ -152,19 +144,19 @@ bool StringToIntegerInternal(const base::StringPiece& string, namespace crashpad { -bool StringToNumber(const base::StringPiece& string, int* number) { +bool StringToNumber(const std::string& string, int* number) { return StringToIntegerInternal(string, number); } -bool StringToNumber(const base::StringPiece& string, unsigned int* number) { +bool StringToNumber(const std::string& string, unsigned int* number) { return StringToIntegerInternal(string, number); } -bool StringToNumber(const base::StringPiece& string, int64_t* number) { +bool StringToNumber(const std::string& string, int64_t* number) { return StringToIntegerInternal(string, number); } -bool StringToNumber(const base::StringPiece& string, uint64_t* number) { +bool StringToNumber(const std::string& string, uint64_t* number) { return StringToIntegerInternal(string, number); } diff --git a/util/stdlib/string_number_conversion.h b/util/stdlib/string_number_conversion.h index b7bdcce8..b5f1d44a 100644 --- a/util/stdlib/string_number_conversion.h +++ b/util/stdlib/string_number_conversion.h @@ -15,7 +15,7 @@ #ifndef CRASHPAD_UTIL_STDLIB_STRING_NUMBER_CONVERSION_H_ #define CRASHPAD_UTIL_STDLIB_STRING_NUMBER_CONVERSION_H_ -#include "base/strings/string_piece.h" +#include namespace crashpad { @@ -54,10 +54,10 @@ namespace crashpad { //! allow arbitrary bases based on whether the string begins with a prefix //! indicating its base. The functions here are provided for situations //! where such prefix recognition is desirable. -bool StringToNumber(const base::StringPiece& string, int* number); -bool StringToNumber(const base::StringPiece& string, unsigned int* number); -bool StringToNumber(const base::StringPiece& string, int64_t* number); -bool StringToNumber(const base::StringPiece& string, uint64_t* number); +bool StringToNumber(const std::string& string, int* number); +bool StringToNumber(const std::string& string, unsigned int* number); +bool StringToNumber(const std::string& string, int64_t* number); +bool StringToNumber(const std::string& string, uint64_t* number); //! \} } // namespace crashpad diff --git a/util/stdlib/string_number_conversion_test.cc b/util/stdlib/string_number_conversion_test.cc index dd17c00d..d855c8d7 100644 --- a/util/stdlib/string_number_conversion_test.cc +++ b/util/stdlib/string_number_conversion_test.cc @@ -114,13 +114,9 @@ TEST(StringNumberConversion, StringToInt) { // is split to avoid MSVC warning: // "decimal digit terminates octal escape sequence". static constexpr char input[] = "6\000" "6"; - base::StringPiece input_string(input, arraysize(input) - 1); + std::string input_string(input, arraysize(input) - 1); int output; EXPECT_FALSE(StringToNumber(input_string, &output)); - - // Ensure that a NUL is not required at the end of the string. - EXPECT_TRUE(StringToNumber(base::StringPiece("66", 1), &output)); - EXPECT_EQ(output, 6); } TEST(StringNumberConversion, StringToUnsignedInt) { @@ -212,13 +208,9 @@ TEST(StringNumberConversion, StringToUnsignedInt) { // is split to avoid MSVC warning: // "decimal digit terminates octal escape sequence". static constexpr char input[] = "6\000" "6"; - base::StringPiece input_string(input, arraysize(input) - 1); + std::string input_string(input, arraysize(input) - 1); unsigned int output; EXPECT_FALSE(StringToNumber(input_string, &output)); - - // Ensure that a NUL is not required at the end of the string. - EXPECT_TRUE(StringToNumber(base::StringPiece("66", 1), &output)); - EXPECT_EQ(output, 6u); } TEST(StringNumberConversion, StringToInt64) { From 4375233ad2dad48f2332ecc20221df10f98b668a Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Fri, 2 Mar 2018 19:50:10 -0800 Subject: [PATCH 211/326] win: fix 64-bit build The 64-bit win build broke in https://chromium-review.googlesource.com/c/crashpad/crashpad/+/944192 but it was missed because we're missing 64-bit win buildbot coverage. Change-Id: Ic3c40006c15bb85408bc869a0b595a652b9ac14e Reviewed-on: https://chromium-review.googlesource.com/947716 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- handler/win/crashy_test_program.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/handler/win/crashy_test_program.cc b/handler/win/crashy_test_program.cc index 5555cac3..bed19b3c 100644 --- a/handler/win/crashy_test_program.cc +++ b/handler/win/crashy_test_program.cc @@ -39,8 +39,8 @@ namespace crashpad { -int* g_extra_memory_pointer; -int* g_extra_memory_not_saved; +size_t* g_extra_memory_pointer; +size_t* g_extra_memory_not_saved; namespace { @@ -142,29 +142,29 @@ void SomeCrashyFunction() { void AllocateExtraMemoryToBeSaved( crashpad::SimpleAddressRangeBag* extra_ranges) { - constexpr size_t kNumInts = 2000; - int* extra_memory = new int[kNumInts]; + constexpr size_t kNumVals = 2000; + size_t* extra_memory = new size_t[kNumVals]; g_extra_memory_pointer = extra_memory; - for (size_t i = 0; i < kNumInts; ++i) + for (size_t i = 0; i < kNumVals; ++i) extra_memory[i] = i * 13 + 2; - extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumInts); + extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumVals); extra_ranges->Insert(&g_extra_memory_pointer, sizeof(g_extra_memory_pointer)); } void AllocateExtraUnsavedMemory(crashpad::SimpleAddressRangeBag* extra_ranges) { // Allocate some extra memory, and then Insert() but also Remove() it so we // can confirm it doesn't get saved. - constexpr size_t kNumInts = 2000; - int* extra_memory = new int[kNumInts]; + constexpr size_t kNumVals = 2000; + size_t* extra_memory = new size_t[kNumVals]; g_extra_memory_not_saved = extra_memory; - for (size_t i = 0; i < kNumInts; ++i) + for (size_t i = 0; i < kNumVals; ++i) extra_memory[i] = i * 17 + 7; - extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumInts); + extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumVals); extra_ranges->Insert(&g_extra_memory_not_saved, sizeof(g_extra_memory_not_saved)); // We keep the pointer's memory, but remove the pointed-to memory. - extra_ranges->Remove(extra_memory, sizeof(extra_memory[0]) * kNumInts); + extra_ranges->Remove(extra_memory, sizeof(extra_memory[0]) * kNumVals); } int CrashyMain(int argc, wchar_t* argv[]) { From 8175825f45fde989ecd05b7c04963a26ea9c4e9c Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Fri, 9 Mar 2018 13:20:09 -0800 Subject: [PATCH 212/326] win: use version.lib instead of mincore.lib Also enable wd4702: unreachable code for zlib and gtest. Change-Id: Ie1603b16e96f29b6ac82a1122c5ab5a8942eef24 Reviewed-on: https://chromium-review.googlesource.com/955895 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- third_party/gtest/BUILD.gn | 4 ++++ third_party/zlib/BUILD.gn | 1 + util/BUILD.gn | 6 +++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/third_party/gtest/BUILD.gn b/third_party/gtest/BUILD.gn index 1a026119..db5f4a56 100644 --- a/third_party/gtest/BUILD.gn +++ b/third_party/gtest/BUILD.gn @@ -138,6 +138,10 @@ if (crashpad_is_in_chromium) { ":gtest", ":gtest_main", ] + + if (crashpad_is_win) { + cflags = [ "/wd4702" ] # unreachable code + } } test("gtest_environment_test") { diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn index db0f99a1..0723ba3e 100644 --- a/third_party/zlib/BUILD.gn +++ b/third_party/zlib/BUILD.gn @@ -89,6 +89,7 @@ if (zlib_source == "external") { "/wd4245", # conversion from 't1' to 't2', signed/unsigned mismatch "/wd4267", # conversion from 'size_t' to 't', possible loss of data "/wd4324", # structure was padded due to alignment specifier + "/wd4702", # unreachable code ] } else { defines += [ diff --git a/util/BUILD.gn b/util/BUILD.gn index bb3a9e48..4c90785f 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -425,8 +425,12 @@ static_library("util") { if (crashpad_is_win) { libs = [ - "mincore.lib", "user32.lib", + + # TODO(jperaza): version.lib is needed for Windows 7 compatibility. + # mincore.lib may be linked against instead when targeting Windows 8+. + "version.lib", + "winhttp.lib", ] From 07da37aec70e2180bd4c1b4b406f3beaae3bc4e2 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Mon, 12 Mar 2018 13:42:43 -0700 Subject: [PATCH 213/326] win: fix crashy test program end_to_end_test.py expects g_extra_memory_pointer data to be 32-bit, so use a fixed-size type. Change-Id: I5798bc8a895d7e02461671fd31e59dd178f6a6a4 Reviewed-on: https://chromium-review.googlesource.com/957792 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- handler/win/crashy_test_program.cc | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/handler/win/crashy_test_program.cc b/handler/win/crashy_test_program.cc index bed19b3c..a4f4797b 100644 --- a/handler/win/crashy_test_program.cc +++ b/handler/win/crashy_test_program.cc @@ -21,6 +21,7 @@ #include #include +#include #include #include "base/files/file_path.h" @@ -39,8 +40,8 @@ namespace crashpad { -size_t* g_extra_memory_pointer; -size_t* g_extra_memory_not_saved; +uint32_t* g_extra_memory_pointer; +uint32_t* g_extra_memory_not_saved; namespace { @@ -143,10 +144,12 @@ void SomeCrashyFunction() { void AllocateExtraMemoryToBeSaved( crashpad::SimpleAddressRangeBag* extra_ranges) { constexpr size_t kNumVals = 2000; - size_t* extra_memory = new size_t[kNumVals]; + auto extra_memory = new uint32_t[kNumVals]; g_extra_memory_pointer = extra_memory; for (size_t i = 0; i < kNumVals; ++i) - extra_memory[i] = i * 13 + 2; + extra_memory[i] = + static_cast::type>( + i * 13 + 2); extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumVals); extra_ranges->Insert(&g_extra_memory_pointer, sizeof(g_extra_memory_pointer)); } @@ -155,10 +158,12 @@ void AllocateExtraUnsavedMemory(crashpad::SimpleAddressRangeBag* extra_ranges) { // Allocate some extra memory, and then Insert() but also Remove() it so we // can confirm it doesn't get saved. constexpr size_t kNumVals = 2000; - size_t* extra_memory = new size_t[kNumVals]; + auto extra_memory = new uint32_t[kNumVals]; g_extra_memory_not_saved = extra_memory; for (size_t i = 0; i < kNumVals; ++i) - extra_memory[i] = i * 17 + 7; + extra_memory[i] = + static_cast::type>( + i * 17 + 7); extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumVals); extra_ranges->Insert(&g_extra_memory_not_saved, sizeof(g_extra_memory_not_saved)); From c27a1aaea0861852c6d92945b68856586e0cd51d Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Mon, 12 Mar 2018 14:06:36 -0700 Subject: [PATCH 214/326] win: Fix -Wmicrosoft-cast warning Standard C++ doesn't allow implicit conversion between function pointers and void*. MSVC does allow that, so clang-cl also allows it but emits a -Wmicrosoft-cast warning. We want to enable this warning to make the compiler behave more similar on different platforms, so add an explicit cast to void*. (GetProcAddress() returns FARPROC, a function pointer type.) Upstreamed from: https://chromium-review.googlesource.com/c/chromium/src/+/953743 Change-Id: I3ed4e23395e1e01b31b7cf945ddb6f93e4e69d45 Reviewed-on: https://chromium-review.googlesource.com/959545 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- test/scoped_module_handle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/scoped_module_handle.h b/test/scoped_module_handle.h index 20b5e153..9909d1c8 100644 --- a/test/scoped_module_handle.h +++ b/test/scoped_module_handle.h @@ -43,7 +43,7 @@ class ScopedModuleHandle { using ModuleHandle = HMODULE; static void* LookUpSymbol(ModuleHandle handle, const char* symbol_name) { - return GetProcAddress(handle, symbol_name); + return reinterpret_cast(GetProcAddress(handle, symbol_name)); } #endif From 132a610184731dfc9365f3e3b08f72055e884045 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Fri, 16 Mar 2018 11:38:37 -0700 Subject: [PATCH 215/326] elf: Use compiler macros in crashpad info note build/build_config.h sometimes includes other headers which aren't appropriate for .S files. Bug: crashpad:30 Change-Id: Ie039e08599137d157c60482c72d6eba6a5566ef5 Reviewed-on: https://chromium-review.googlesource.com/966876 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- client/crashpad_info_note.S | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client/crashpad_info_note.S b/client/crashpad_info_note.S index 8084db94..4c29298d 100644 --- a/client/crashpad_info_note.S +++ b/client/crashpad_info_note.S @@ -16,7 +16,6 @@ // of finding the instance of CrashpadInfo g_crashpad_info without requiring // that symbol to be in the dynamic symbol table. -#include "build/build_config.h" #include "util/misc/elf_note_types.h" // namespace crashpad { @@ -45,16 +44,16 @@ name: name_end: .balign NOTE_ALIGN desc: -#if defined(ARCH_CPU_64_BITS) +#if defined(__LP64__) .quad CRASHPAD_INFO_SYMBOL #else -#if defined(ARCH_CPU_LITTLE_ENDIAN) +#if defined(__LITTLE_ENDIAN__) .long CRASHPAD_INFO_SYMBOL .long 0 #else .long 0 .long CRASHPAD_INFO_SYMBOL -#endif // ARCH_CPU_LITTLE_ENDIAN -#endif // ARCH_CPU_64_BITS +#endif // __LITTLE_ENDIAN__ +#endif // __LP64__ desc_end: .size CRASHPAD_NOTE_REFERENCE, .-CRASHPAD_NOTE_REFERENCE From f5b486de7416a50e2460112725ccec43fcc559f4 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Fri, 16 Mar 2018 10:52:55 -0700 Subject: [PATCH 216/326] linux: Make StartHandler methods static Bug: crashpad:30 Change-Id: I3b9e9f149ea8190c3b725691f4fb320eca9cbad0 Reviewed-on: https://chromium-review.googlesource.com/966887 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- client/crashpad_client.h | 4 ++-- client/crashpad_client_linux.cc | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 3cd806a1..267a6e20 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -132,7 +132,7 @@ class CrashpadClient { //! specified in this parameter. //! //! \return `true` on success, `false` on failure with a message logged. - bool StartHandlerAtCrash( + static bool StartHandlerAtCrash( const base::FilePath& handler, const base::FilePath& database, const base::FilePath& metrics_dir, @@ -164,7 +164,7 @@ class CrashpadClient { //! be used with an ExceptionHandlerClient. //! //! \return `true` on success, `false` on failure with a message logged. - bool StartHandlerForClient( + static bool StartHandlerForClient( const base::FilePath& handler, const base::FilePath& database, const base::FilePath& metrics_dir, diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc index ca13c297..22f7e3d8 100644 --- a/client/crashpad_client_linux.cc +++ b/client/crashpad_client_linux.cc @@ -215,6 +215,7 @@ bool CrashpadClient::StartHandler( return false; } +// static bool CrashpadClient::StartHandlerAtCrash( const base::FilePath& handler, const base::FilePath& database, @@ -235,6 +236,7 @@ bool CrashpadClient::StartHandlerAtCrash( return false; } +// static bool CrashpadClient::StartHandlerForClient( const base::FilePath& handler, const base::FilePath& database, From f5483cb99fd43696ef6f8b0d8164d719e0a189b3 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 20 Mar 2018 10:01:23 -0700 Subject: [PATCH 217/326] linux: Use HANDLE_EINTR for sendmsg and recvmsg Change-Id: I382d7e02bc11a3955688966da01802535c68c34e Reviewed-on: https://chromium-review.googlesource.com/971165 Reviewed-by: Mark Mentovai --- handler/linux/exception_handler_server.cc | 2 +- util/linux/exception_handler_client.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/handler/linux/exception_handler_server.cc b/handler/linux/exception_handler_server.cc index 6bb55cda..04f561a5 100644 --- a/handler/linux/exception_handler_server.cc +++ b/handler/linux/exception_handler_server.cc @@ -348,7 +348,7 @@ bool ExceptionHandlerServer::ReceiveClientMessage(Event* event) { msg.msg_controllen = sizeof(cmsg_buf); msg.msg_flags = 0; - int res = recvmsg(event->fd.get(), &msg, 0); + int res = HANDLE_EINTR(recvmsg(event->fd.get(), &msg, 0)); if (res < 0) { PLOG(ERROR) << "recvmsg"; return false; diff --git a/util/linux/exception_handler_client.cc b/util/linux/exception_handler_client.cc index 65f68093..46ce1c14 100644 --- a/util/linux/exception_handler_client.cc +++ b/util/linux/exception_handler_client.cc @@ -92,7 +92,7 @@ int ExceptionHandlerClient::SendCrashDumpRequest( cmsg->cmsg_len = CMSG_LEN(sizeof(creds)); *reinterpret_cast(CMSG_DATA(cmsg)) = creds; - if (sendmsg(server_sock_, &msg, MSG_NOSIGNAL) < 0) { + if (HANDLE_EINTR(sendmsg(server_sock_, &msg, MSG_NOSIGNAL)) < 0) { PLOG(ERROR) << "sendmsg"; return errno; } From cf9e96b856cb19c76a15e4c1bb8f99b0c26f46ee Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 20 Mar 2018 15:14:16 -0700 Subject: [PATCH 218/326] elf: Use compiler macros in crashpad info size test note Bug: crashpad:30 Change-Id: If5bdd15dfc050ef57df0e3b59dd6a5e74d4a9b22 Reviewed-on: https://chromium-review.googlesource.com/972367 Reviewed-by: Mark Mentovai --- snapshot/crashpad_info_size_test_note.S | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/snapshot/crashpad_info_size_test_note.S b/snapshot/crashpad_info_size_test_note.S index a355a831..96b996db 100644 --- a/snapshot/crashpad_info_size_test_note.S +++ b/snapshot/crashpad_info_size_test_note.S @@ -16,7 +16,6 @@ // of finding the instance of CrashpadInfo g_crashpad_info without requiring // that symbol to be in the dynamic symbol table. -#include "build/build_config.h" #include "util/misc/elf_note_types.h" // namespace crashpad { @@ -41,16 +40,16 @@ name: name_end: .balign NOTE_ALIGN desc: -#if defined(ARCH_CPU_64_BITS) +#if defined(__LP64__) .quad TEST_CRASHPAD_INFO_SYMBOL #else -#if defined(ARCH_CPU_LITTLE_ENDIAN) +#if defined(__LITTLE_ENDIAN__) .long TEST_CRASHPAD_INFO_SYMBOL .long 0 #else .long 0 .long TEST_CRASHPAD_INFO_SYMBOL -#endif // ARCH_CPU_LITTLE_ENDIAN -#endif // ARCH_CPU_64_BITS +#endif // __LITTLE_ENDIAN__ +#endif // __LP64__ desc_end: .size info_size_test_note, .-info_size_test_note From afd3186280d91cd4b02b7ea4024215497e4a014e Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 20 Mar 2018 15:38:50 -0700 Subject: [PATCH 219/326] Roll mini_chromium to d42eb41 Change-Id: I18c0a3cfcc82725f19023aa3d78a937719fd2d49 Reviewed-on: https://chromium-review.googlesource.com/972424 Reviewed-by: Mark Mentovai --- DEPS | 2 +- snapshot/linux/exception_snapshot_linux_test.cc | 1 + util/linux/auxiliary_vector_test.cc | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/DEPS b/DEPS index d118f600..94847ae8 100644 --- a/DEPS +++ b/DEPS @@ -29,7 +29,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'ef0df1119b40cfa2773d5960e239d4b960310869', + 'd42eb410123667fe94427986d2616795897efa0c', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/snapshot/linux/exception_snapshot_linux_test.cc b/snapshot/linux/exception_snapshot_linux_test.cc index 4add607d..c53d69e9 100644 --- a/snapshot/linux/exception_snapshot_linux_test.cc +++ b/snapshot/linux/exception_snapshot_linux_test.cc @@ -21,6 +21,7 @@ #include #include +#include "base/bit_cast.h" #include "base/macros.h" #include "base/strings/stringprintf.h" #include "gtest/gtest.h" diff --git a/util/linux/auxiliary_vector_test.cc b/util/linux/auxiliary_vector_test.cc index e40eb33a..e3bad3fc 100644 --- a/util/linux/auxiliary_vector_test.cc +++ b/util/linux/auxiliary_vector_test.cc @@ -20,6 +20,7 @@ #include +#include "base/bit_cast.h" #include "base/macros.h" #include "gtest/gtest.h" #include "test/errors.h" From 6d4626090db24cc4c05946c3b43962596e522d9a Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 21 Mar 2018 11:34:07 -0700 Subject: [PATCH 220/326] linux: Add a second CaptureContext symbol name glibc 2.26 defines ucontext_t from a struct ucontext_t while Bionic and older versions of glibc use a struct ucontext. Bug: crashpad:30 Change-Id: I473c317dbdbbedfad601c7594cfa7df7f7c01cb9 Reviewed-on: https://chromium-review.googlesource.com/972613 Reviewed-by: Mark Mentovai --- util/misc/capture_context_linux.S | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/util/misc/capture_context_linux.S b/util/misc/capture_context_linux.S index c16d0c74..5541643f 100644 --- a/util/misc/capture_context_linux.S +++ b/util/misc/capture_context_linux.S @@ -15,10 +15,17 @@ // namespace crashpad { // void CaptureContext(ucontext_t* context); // } // namespace crashpad -#define CAPTURECONTEXT_SYMBOL _ZN8crashpad14CaptureContextEP8ucontext + +// The type name for a ucontext_t varies by libc implementation and version. +// Bionic and glibc 2.25 typedef ucontext_t from struct ucontext. glibc 2.26+ +// typedef ucontext_t from struct ucontext_t. Alias the symbol names to maintain +// compatibility with both possibilities. +#define CAPTURECONTEXT_SYMBOL _ZN8crashpad14CaptureContextEP10ucontext_t +#define CAPTURECONTEXT_SYMBOL2 _ZN8crashpad14CaptureContextEP8ucontext .text .globl CAPTURECONTEXT_SYMBOL + .globl CAPTURECONTEXT_SYMBOL2 #if defined(__i386__) || defined(__x86_64__) .balign 16, 0x90 #elif defined(__arm__) || defined(__aarch64__) @@ -26,6 +33,7 @@ #endif CAPTURECONTEXT_SYMBOL: +CAPTURECONTEXT_SYMBOL2: #if defined(__i386__) From 9c89cd99f3357f194f0b2bf873e46bf8b59e1c77 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 21 Mar 2018 19:18:07 -0700 Subject: [PATCH 221/326] gn: add templates for executables and loadable_modules When building in chromium, executables and loadable_modules should depend on: //build/config:exe_and_shlib_deps which, among other things, may be needed to introduce a dependency on a custom libc++. Bug: crashpad:30 Change-Id: Ic46a3cf5b46bdac09cca22950f9236e0776ba44a Reviewed-on: https://chromium-review.googlesource.com/974713 Reviewed-by: Mark Mentovai --- build/crashpad_buildconfig.gni | 42 ++++++++++++++++++++++++++++++++++ handler/BUILD.gn | 30 ++++++++++++------------ snapshot/BUILD.gn | 24 +++++++++---------- test/BUILD.gn | 2 +- tools/BUILD.gn | 14 ++++++------ util/BUILD.gn | 4 ++-- 6 files changed, 79 insertions(+), 37 deletions(-) diff --git a/build/crashpad_buildconfig.gni b/build/crashpad_buildconfig.gni index e9ac5713..c10ad30e 100644 --- a/build/crashpad_buildconfig.gni +++ b/build/crashpad_buildconfig.gni @@ -60,3 +60,45 @@ if (crashpad_is_in_chromium) { crashpad_is_clang = mini_chromium_is_clang } + +template("crashpad_executable") { + executable(target_name) { + forward_variables_from(invoker, "*", [ "configs", "remove_configs" ]) + if (defined(invoker.remove_configs)) { + configs -= invoker.remove_configs + } + + if (defined(invoker.configs)) { + configs += invoker.configs + } + + if (!defined(deps)) { + deps = [] + } + + if (crashpad_is_in_chromium) { + deps += [ "//build/config:exe_and_shlib_deps" ] + } + } +} + +template("crashpad_loadable_module") { + loadable_module(target_name) { + forward_variables_from(invoker, "*", [ "configs", "remove_configs" ]) + if (defined(invoker.remove_configs)) { + configs -= invoker.remove_configs + } + + if (defined(invoker.configs)) { + configs += invoker.configs + } + + if (!defined(deps)) { + deps = [] + } + + if (crashpad_is_in_chromium) { + deps += [ "//build/config:exe_and_shlib_deps" ] + } + } +} diff --git a/handler/BUILD.gn b/handler/BUILD.gn index b3370fca..ccbb0b55 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -115,7 +115,7 @@ source_set("handler_test") { } } -executable("crashpad_handler") { +crashpad_executable("crashpad_handler") { sources = [ "main.cc", ] @@ -147,18 +147,18 @@ executable("crashpad_handler") { if (crashpad_is_win) { if (crashpad_is_in_chromium) { - configs -= [ "//build/config/win:console" ] - configs += [ "//build/config/win:windowed" ] + remove_configs = [ "//build/config/win:console" ] + configs = [ "//build/config/win:windowed" ] } else { - configs -= + remove_configs = [ "//third_party/mini_chromium/mini_chromium/build:win_console" ] - configs += + configs = [ "//third_party/mini_chromium/mini_chromium/build:win_windowed" ] } } } -executable("crashpad_handler_test_extended_handler") { +crashpad_executable("crashpad_handler_test_extended_handler") { testonly = true sources = [ @@ -176,7 +176,7 @@ executable("crashpad_handler_test_extended_handler") { } if (crashpad_is_win) { - executable("crashpad_handler_com") { + crashpad_executable("crashpad_handler_com") { sources = [ "main.cc", ] @@ -206,7 +206,7 @@ if (crashpad_is_win) { ] } - executable("crash_other_program") { + crashpad_executable("crash_other_program") { testonly = true sources = [ @@ -221,7 +221,7 @@ if (crashpad_is_win) { ] } - executable("crashy_program") { + crashpad_executable("crashy_program") { testonly = true sources = [ @@ -234,7 +234,7 @@ if (crashpad_is_win) { ] } - executable("crashy_signal") { + crashpad_executable("crashy_signal") { testonly = true sources = [ @@ -249,7 +249,7 @@ if (crashpad_is_win) { ] } - executable("fake_handler_that_crashes_at_startup") { + crashpad_executable("fake_handler_that_crashes_at_startup") { testonly = true sources = [ @@ -257,7 +257,7 @@ if (crashpad_is_win) { ] } - executable("hanging_program") { + crashpad_executable("hanging_program") { testonly = true sources = [ @@ -270,7 +270,7 @@ if (crashpad_is_win) { ] } - loadable_module("loader_lock_dll") { + crashpad_loadable_module("loader_lock_dll") { testonly = true sources = [ @@ -278,7 +278,7 @@ if (crashpad_is_win) { ] } - executable("self_destroying_program") { + crashpad_executable("self_destroying_program") { testonly = true sources = [ @@ -295,7 +295,7 @@ if (crashpad_is_win) { if (current_cpu == "x86") { # Cannot create an x64 DLL with embedded debug info. - executable("crashy_z7_loader") { + crashpad_executable("crashy_z7_loader") { testonly = true sources = [ diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 5ccc735e..e12cc6d6 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -401,7 +401,7 @@ source_set("snapshot_test") { } } -loadable_module("crashpad_snapshot_test_module") { +crashpad_loadable_module("crashpad_snapshot_test_module") { testonly = true sources = [ "crashpad_info_client_options_test_module.cc", @@ -412,7 +412,7 @@ loadable_module("crashpad_snapshot_test_module") { ] } -loadable_module("crashpad_snapshot_test_module_large") { +crashpad_loadable_module("crashpad_snapshot_test_module_large") { testonly = true sources = [ "crashpad_info_size_test_module.cc", @@ -428,7 +428,7 @@ loadable_module("crashpad_snapshot_test_module_large") { deps += [ "../third_party/mini_chromium:base" ] } -loadable_module("crashpad_snapshot_test_module_small") { +crashpad_loadable_module("crashpad_snapshot_test_module_small") { testonly = true sources = [ "crashpad_info_size_test_module.cc", @@ -445,7 +445,7 @@ loadable_module("crashpad_snapshot_test_module_small") { } if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { - loadable_module("crashpad_snapshot_test_both_dt_hash_styles") { + crashpad_loadable_module("crashpad_snapshot_test_both_dt_hash_styles") { testonly = true sources = [ "hash_types_test.cc", @@ -457,14 +457,14 @@ if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { } if (crashpad_is_mac) { - loadable_module("crashpad_snapshot_test_module_crashy_initializer") { + crashpad_loadable_module("crashpad_snapshot_test_module_crashy_initializer") { testonly = true sources = [ "mac/mach_o_image_annotations_reader_test_module_crashy_initializer.cc", ] } - executable("crashpad_snapshot_test_no_op") { + crashpad_executable("crashpad_snapshot_test_no_op") { testonly = true sources = [ "mac/mach_o_image_annotations_reader_test_no_op.cc", @@ -473,7 +473,7 @@ if (crashpad_is_mac) { } if (crashpad_is_win) { - executable("crashpad_snapshot_test_annotations") { + crashpad_executable("crashpad_snapshot_test_annotations") { testonly = true sources = [ "win/crashpad_snapshot_test_annotations.cc", @@ -485,7 +485,7 @@ if (crashpad_is_win) { ] } - executable("crashpad_snapshot_test_crashing_child") { + crashpad_executable("crashpad_snapshot_test_crashing_child") { testonly = true sources = [ "win/crashpad_snapshot_test_crashing_child.cc", @@ -498,7 +498,7 @@ if (crashpad_is_win) { ] } - executable("crashpad_snapshot_test_dump_without_crashing") { + crashpad_executable("crashpad_snapshot_test_dump_without_crashing") { testonly = true sources = [ "win/crashpad_snapshot_test_dump_without_crashing.cc", @@ -511,7 +511,7 @@ if (crashpad_is_win) { ] } - executable("crashpad_snapshot_test_extra_memory_ranges") { + crashpad_executable("crashpad_snapshot_test_extra_memory_ranges") { testonly = true sources = [ "win/crashpad_snapshot_test_extra_memory_ranges.cc", @@ -523,7 +523,7 @@ if (crashpad_is_win) { ] } - executable("crashpad_snapshot_test_image_reader") { + crashpad_executable("crashpad_snapshot_test_image_reader") { testonly = true sources = [ "win/crashpad_snapshot_test_image_reader.cc", @@ -544,7 +544,7 @@ if (crashpad_is_win) { } } - loadable_module("crashpad_snapshot_test_image_reader_module") { + crashpad_loadable_module("crashpad_snapshot_test_image_reader_module") { testonly = true sources = [ "win/crashpad_snapshot_test_image_reader_module.cc", diff --git a/test/BUILD.gn b/test/BUILD.gn index 0808096e..2c894603 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -171,7 +171,7 @@ source_set("test_test") { ] } -executable("crashpad_test_test_multiprocess_exec_test_child") { +crashpad_executable("crashpad_test_test_multiprocess_exec_test_child") { sources = [ "multiprocess_exec_test_child.cc", ] diff --git a/tools/BUILD.gn b/tools/BUILD.gn index 54188198..a711bdf7 100644 --- a/tools/BUILD.gn +++ b/tools/BUILD.gn @@ -27,7 +27,7 @@ source_set("tool_support") { ] } -executable("crashpad_database_util") { +crashpad_executable("crashpad_database_util") { sources = [ "crashpad_database_util.cc", ] @@ -42,7 +42,7 @@ executable("crashpad_database_util") { ] } -executable("crashpad_http_upload") { +crashpad_executable("crashpad_http_upload") { sources = [ "crashpad_http_upload.cc", ] @@ -56,7 +56,7 @@ executable("crashpad_http_upload") { ] } -executable("generate_dump") { +crashpad_executable("generate_dump") { sources = [ "generate_dump.cc", ] @@ -92,7 +92,7 @@ executable("generate_dump") { } if (crashpad_is_mac) { - executable("catch_exception_tool") { + crashpad_executable("catch_exception_tool") { sources = [ "mac/catch_exception_tool.cc", ] @@ -105,7 +105,7 @@ if (crashpad_is_mac) { ] } - executable("exception_port_tool") { + crashpad_executable("exception_port_tool") { sources = [ "mac/exception_port_tool.cc", ] @@ -130,7 +130,7 @@ if (crashpad_is_mac) { ] } - executable("on_demand_service_tool") { + crashpad_executable("on_demand_service_tool") { sources = [ "mac/on_demand_service_tool.mm", ] @@ -148,7 +148,7 @@ if (crashpad_is_mac) { ] } - executable("run_with_crashpad") { + crashpad_executable("run_with_crashpad") { sources = [ "mac/run_with_crashpad.cc", ] diff --git a/util/BUILD.gn b/util/BUILD.gn index 4c90785f..ea071afd 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -617,14 +617,14 @@ source_set("util_test") { } if (crashpad_is_win) { - executable("crashpad_util_test_process_info_test_child") { + crashpad_executable("crashpad_util_test_process_info_test_child") { testonly = true sources = [ "win/process_info_test_child.cc", ] } - executable("crashpad_util_test_safe_terminate_process_test_child") { + crashpad_executable("crashpad_util_test_safe_terminate_process_test_child") { testonly = true sources = [ "win/safe_terminate_process_test_child.cc", From 6b23575b34bd4cd06ae9ee55a557b6568efd236b Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Fri, 23 Mar 2018 12:26:44 -0700 Subject: [PATCH 222/326] linux: verify whether a broker has been successfully forked Also fix an error in checking that PtraceClient was initialized. Bug: crashpad:30 Change-Id: I1928340a2a642c2d831f0152bb9faaa12afb07e8 Reviewed-on: https://chromium-review.googlesource.com/978630 Reviewed-by: Mark Mentovai --- .../linux/crash_report_exception_handler.cc | 2 +- handler/linux/exception_handler_server.cc | 35 ++++++++++++++----- handler/linux/exception_handler_server.h | 4 +-- .../linux/exception_handler_server_test.cc | 2 +- util/linux/exception_handler_client.cc | 7 ++-- 5 files changed, 35 insertions(+), 15 deletions(-) diff --git a/handler/linux/crash_report_exception_handler.cc b/handler/linux/crash_report_exception_handler.cc index 3aa46981..e72819cd 100644 --- a/handler/linux/crash_report_exception_handler.cc +++ b/handler/linux/crash_report_exception_handler.cc @@ -63,7 +63,7 @@ bool CrashReportExceptionHandler::HandleExceptionWithBroker( Metrics::ExceptionEncountered(); PtraceClient client; - if (client.Initialize(broker_sock, client_process_id)) { + if (!client.Initialize(broker_sock, client_process_id)) { Metrics::ExceptionCaptureResult( Metrics::CaptureResult::kBrokeredPtraceFailed); return false; diff --git a/handler/linux/exception_handler_server.cc b/handler/linux/exception_handler_server.cc index 04f561a5..17fc18c8 100644 --- a/handler/linux/exception_handler_server.cc +++ b/handler/linux/exception_handler_server.cc @@ -126,8 +126,10 @@ class PtraceStrategyDeciderImpl : public PtraceStrategyDecider { Strategy ChooseStrategy(int sock, const ucred& client_credentials) override { switch (GetPtraceScope()) { case PtraceScope::kClassic: - return getuid() == client_credentials.uid ? Strategy::kDirectPtrace - : Strategy::kForkBroker; + if (getuid() == client_credentials.uid) { + return Strategy::kDirectPtrace; + } + return TryForkingBroker(sock); case PtraceScope::kRestricted: if (!SendMessageToClient(sock, @@ -143,7 +145,7 @@ class PtraceStrategyDeciderImpl : public PtraceStrategyDecider { if (status != 0) { errno = status; PLOG(ERROR) << "Handler Client SetPtracer"; - return Strategy::kForkBroker; + return TryForkingBroker(sock); } return Strategy::kDirectPtrace; @@ -163,6 +165,26 @@ class PtraceStrategyDeciderImpl : public PtraceStrategyDecider { DCHECK(false); } + + private: + static Strategy TryForkingBroker(int client_sock) { + if (!SendMessageToClient(client_sock, + ServerToClientMessage::kTypeForkBroker)) { + return Strategy::kError; + } + + Errno status; + if (!LoggingReadFileExactly(client_sock, &status, sizeof(status))) { + return Strategy::kError; + } + + if (status != 0) { + errno = status; + PLOG(ERROR) << "Handler Client ForkBroker"; + return Strategy::kNoPtrace; + } + return Strategy::kUseBroker; + } }; } // namespace @@ -427,12 +449,7 @@ bool ExceptionHandlerServer::HandleCrashDumpRequest( client_info.exception_information_address); break; - case PtraceStrategyDecider::Strategy::kForkBroker: - if (!SendMessageToClient(client_sock, - ServerToClientMessage::kTypeForkBroker)) { - return false; - } - + case PtraceStrategyDecider::Strategy::kUseBroker: delegate_->HandleExceptionWithBroker( client_process_id, client_info.exception_information_address, diff --git a/handler/linux/exception_handler_server.h b/handler/linux/exception_handler_server.h index fcafb887..1c02a441 100644 --- a/handler/linux/exception_handler_server.h +++ b/handler/linux/exception_handler_server.h @@ -46,8 +46,8 @@ class PtraceStrategyDecider { //! \brief The handler should `ptrace`-attach the client directly. kDirectPtrace, - //! \brief The client should `fork` a PtraceBroker for the handler. - kForkBroker, + //! \brief The client has `fork`ed a PtraceBroker for the handler. + kUseBroker, }; //! \brief Chooses an appropriate `ptrace` strategy. diff --git a/handler/linux/exception_handler_server_test.cc b/handler/linux/exception_handler_server_test.cc index 5b0c8cea..b68e7384 100644 --- a/handler/linux/exception_handler_server_test.cc +++ b/handler/linux/exception_handler_server_test.cc @@ -289,7 +289,7 @@ TEST_F(ExceptionHandlerServerTest, RequestCrashDumpNoPtrace) { } TEST_F(ExceptionHandlerServerTest, RequestCrashDumpForkBroker) { - ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kForkBroker, + ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kUseBroker, true); } diff --git a/util/linux/exception_handler_client.cc b/util/linux/exception_handler_client.cc index 46ce1c14..cdfd0c5c 100644 --- a/util/linux/exception_handler_client.cc +++ b/util/linux/exception_handler_client.cc @@ -112,11 +112,14 @@ int ExceptionHandlerClient::WaitForCrashDumpComplete() { Signals::InstallDefaultHandler(SIGCHLD); pid_t pid = fork(); - if (pid < 0) { - Errno error = errno; + if (pid <= 0) { + Errno error = pid < 0 ? errno : 0; if (!WriteFile(server_sock_, &error, sizeof(error))) { return errno; } + } + + if (pid < 0) { continue; } From 5754f608cb571de0e51313ec0d5a1ddefa3997b2 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Mon, 26 Mar 2018 17:26:08 -0700 Subject: [PATCH 223/326] android: unset source filters to use linux files Also disable testing reading AT_ENTRY on Android. Bug: crashpad:30 Change-Id: I10353bbbb3ff28721a2c05d69463df5eac4df281 Reviewed-on: https://chromium-review.googlesource.com/980811 Reviewed-by: Mark Mentovai --- client/BUILD.gn | 1 + handler/BUILD.gn | 1 + snapshot/BUILD.gn | 1 + test/BUILD.gn | 1 + util/BUILD.gn | 2 ++ util/linux/auxiliary_vector_test.cc | 7 +++++++ 6 files changed, 13 insertions(+) diff --git a/client/BUILD.gn b/client/BUILD.gn index cb4161f5..ddd521f2 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -44,6 +44,7 @@ static_library("client") { } if (crashpad_is_linux || crashpad_is_android) { + set_sources_assignment_filter([]) sources += [ "crashpad_client_linux.cc", "simulate_crash_linux.h", diff --git a/handler/BUILD.gn b/handler/BUILD.gn index ccbb0b55..78a4b092 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -40,6 +40,7 @@ static_library("handler") { } if (crashpad_is_linux || crashpad_is_android) { + set_sources_assignment_filter([]) sources += [ "linux/crash_report_exception_handler.cc", "linux/crash_report_exception_handler.h", diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index e12cc6d6..27758890 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -104,6 +104,7 @@ static_library("snapshot") { } if (crashpad_is_linux || crashpad_is_android) { + set_sources_assignment_filter([]) sources += [ "linux/cpu_context_linux.cc", "linux/cpu_context_linux.h", diff --git a/test/BUILD.gn b/test/BUILD.gn index 2c894603..dc2e2a75 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -69,6 +69,7 @@ static_library("test") { } if (crashpad_is_linux || crashpad_is_android) { + set_sources_assignment_filter([]) sources += [ "linux/fake_ptrace_connection.cc", "linux/fake_ptrace_connection.h", diff --git a/util/BUILD.gn b/util/BUILD.gn index ea071afd..a7034924 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -260,6 +260,7 @@ static_library("util") { } if (crashpad_is_linux || crashpad_is_android) { + set_sources_assignment_filter([]) sources += [ "linux/address_types.h", "linux/auxiliary_vector.cc", @@ -548,6 +549,7 @@ source_set("util_test") { } if (crashpad_is_linux || crashpad_is_android) { + set_sources_assignment_filter([]) sources += [ "linux/auxiliary_vector_test.cc", "linux/memory_map_test.cc", diff --git a/util/linux/auxiliary_vector_test.cc b/util/linux/auxiliary_vector_test.cc index e3bad3fc..382cb67b 100644 --- a/util/linux/auxiliary_vector_test.cc +++ b/util/linux/auxiliary_vector_test.cc @@ -22,6 +22,7 @@ #include "base/bit_cast.h" #include "base/macros.h" +#include "build/build_config.h" #include "gtest/gtest.h" #include "test/errors.h" #include "test/multiprocess.h" @@ -31,9 +32,13 @@ #include "util/numeric/int128.h" #include "util/process/process_memory_linux.h" +#if !defined(OS_ANDROID) +// TODO(jperaza): This symbol isn't defined when building in chromium for +// Android. There may be another symbol to use. extern "C" { extern void _start(); } // extern "C" +#endif namespace crashpad { namespace test { @@ -63,9 +68,11 @@ void TestAgainstCloneOrSelf(pid_t pid) { ASSERT_TRUE(aux.GetValue(AT_BASE, &interp_base)); EXPECT_TRUE(mappings.FindMapping(interp_base)); +#if !defined(OS_ANDROID) LinuxVMAddress entry_addr; ASSERT_TRUE(aux.GetValue(AT_ENTRY, &entry_addr)); EXPECT_EQ(entry_addr, FromPointerCast(_start)); +#endif uid_t uid; ASSERT_TRUE(aux.GetValue(AT_UID, &uid)); From 58e4bbecc246543b60e4bc4bbdccd2e24d3157c9 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Mon, 26 Mar 2018 19:54:26 -0700 Subject: [PATCH 224/326] win, gn: use new lists when using templated targets The crashpad_{executable, loadable_module} templates won't have pre-existing configs lists to modify. Use configs and remove_configs to merge changes into default configs when using the templates. Change-Id: Id7c0b1991c9d0ac55022b427feb59df28668b959 Reviewed-on: https://chromium-review.googlesource.com/981778 Reviewed-by: Mark Mentovai --- snapshot/BUILD.gn | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 27758890..9bb49ae4 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -539,8 +539,8 @@ if (crashpad_is_win) { if (symbol_level == 0) { # The tests that use this executable rely on at least minimal debug # info. - configs -= [ "//build/config/compiler:default_symbols" ] - configs += [ "//build/config/compiler:minimal_symbols" ] + remove_configs = [ "//build/config/compiler:default_symbols" ] + configs = [ "//build/config/compiler:minimal_symbols" ] } } } @@ -557,8 +557,8 @@ if (crashpad_is_win) { if (crashpad_is_in_chromium) { if (symbol_level == 0) { # The tests that use this module rely on at least minimal debug info. - configs -= [ "//build/config/compiler:default_symbols" ] - configs += [ "//build/config/compiler:minimal_symbols" ] + remove_configs = [ "//build/config/compiler:default_symbols" ] + configs = [ "//build/config/compiler:minimal_symbols" ] } } } From 7bd613e55835de1f8d29f24de4e4271bdb8c01d4 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 28 Mar 2018 08:37:22 -0700 Subject: [PATCH 225/326] linux: prefix args with crashpad and build tools on android 1. Prefix enable_http_transport_libcurl with crashpad for use in chromium .gn files. 2. Make tools build on Android using http_transport_none.cc Bug: crashpad:30 Change-Id: I0a9878fe9f5b8fbc13a52f93df273fb1de8160f3 Reviewed-on: https://chromium-review.googlesource.com/984038 Reviewed-by: Mark Mentovai --- tools/generate_dump.cc | 4 ++-- util/BUILD.gn | 10 +++++----- util/util.gyp | 6 ++++++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/tools/generate_dump.cc b/tools/generate_dump.cc index c1fbde84..cce0823b 100644 --- a/tools/generate_dump.cc +++ b/tools/generate_dump.cc @@ -47,7 +47,7 @@ #include "util/win/xp_compat.h" #elif defined(OS_FUCHSIA) #include "snapshot/fuchsia/process_snapshot_fuchsia.h" -#elif defined(OS_LINUX) +#elif defined(OS_LINUX) || defined(OS_ANDROID) #include "snapshot/linux/process_snapshot_linux.h" #endif // OS_MACOSX @@ -201,7 +201,7 @@ int GenerateDumpMain(int argc, char* argv[]) { if (!process_snapshot.Initialize(ZX_HANDLE_INVALID)) { return EXIT_FAILURE; } -#elif defined(OS_LINUX) +#elif defined(OS_LINUX) || defined(OS_ANDROID) // TODO(jperaza): https://crashpad.chromium.org/bug/30. ProcessSnapshotLinux process_snapshot; if (!process_snapshot.Initialize(nullptr)) { diff --git a/util/BUILD.gn b/util/BUILD.gn index a7034924..3836e804 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -17,7 +17,7 @@ import("../build/crashpad_buildconfig.gni") declare_args() { if (crashpad_is_linux) { # Whether the libcurl-based HTTPTransport implementation should be built. - enable_http_transport_libcurl = true + crashpad_enable_http_transport_libcurl = true } } @@ -251,8 +251,8 @@ static_library("util") { sources += get_target_outputs(":mig") } - if (crashpad_is_linux) { - if (enable_http_transport_libcurl) { + if (crashpad_is_linux || crashpad_is_android) { + if (crashpad_is_linux && crashpad_enable_http_transport_libcurl) { sources += [ "net/http_transport_libcurl.cc" ] } else { sources += [ "net/http_transport_none.cc" ] @@ -420,7 +420,7 @@ static_library("util") { include_dirs += [ "$root_build_dir/gen" ] } - if (crashpad_is_linux && enable_http_transport_libcurl) { + if (crashpad_is_linux && crashpad_enable_http_transport_libcurl) { libs = [ "curl" ] } @@ -497,7 +497,7 @@ source_set("util_test") { } if (!crashpad_is_android && !crashpad_is_fuchsia && - (!crashpad_is_linux || enable_http_transport_libcurl)) { + (!crashpad_is_linux || crashpad_enable_http_transport_libcurl)) { # Android and Fuchsia will each require an HTTPTransport implementation # (libcurl isn’t in either’s SDK) and a solution to # http_transport_test_server.py, because Python isn’t available on either. diff --git a/util/util.gyp b/util/util.gyp index e0394e2a..45de7a62 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -177,6 +177,7 @@ 'net/http_transport.h', 'net/http_transport_libcurl.cc', 'net/http_transport_mac.mm', + 'net/http_transport_none.cc', 'net/http_transport_win.cc', 'net/url.cc', 'net/url.h', @@ -393,6 +394,11 @@ 'net/http_transport_libcurl.cc', ], }], + ['OS!="android"', { + 'sources!': [ + 'net/http_transport_none.cc', + ], + }], ['OS!="linux" and OS!="android"', { 'sources/': [ ['exclude', '^process/'], From 246ecc6686f3d8d6d03b8acb7c0812a37e842231 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 29 Mar 2018 08:56:30 -0700 Subject: [PATCH 226/326] linux: disable libcurl when in chromium Bug: crashpad:30 Change-Id: I12007417d27b482f70879d91743fddc06851140e Reviewed-on: https://chromium-review.googlesource.com/986503 Reviewed-by: Mark Mentovai --- util/BUILD.gn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/BUILD.gn b/util/BUILD.gn index 3836e804..1e2c968c 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -17,7 +17,7 @@ import("../build/crashpad_buildconfig.gni") declare_args() { if (crashpad_is_linux) { # Whether the libcurl-based HTTPTransport implementation should be built. - crashpad_enable_http_transport_libcurl = true + crashpad_enable_http_transport_libcurl = !crashpad_is_in_chromium } } From d108fd04a5e6ec62740a457ff26380bbd2f244a1 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 3 Apr 2018 15:01:44 -0700 Subject: [PATCH 227/326] linux: Add PtraceConnection::ReadFileContents Some files, such as /proc/[pid]/maps, may not be accessible to the handler. This enables the handler access to the contents of those files via the broker. This change reads maps and auxv using ReadFileContents. Bug: crashpad:30 Change-Id: Ia19b498bae473c616ea794ab51c3f22afd5795be Reviewed-on: https://chromium-review.googlesource.com/989406 Reviewed-by: Mark Mentovai --- snapshot/elf/elf_image_reader_test.cc | 17 ++- snapshot/linux/debug_rendezvous_test.cc | 34 +++--- snapshot/linux/process_reader_linux.cc | 6 +- snapshot/linux/process_reader_linux_test.cc | 7 +- test/linux/fake_ptrace_connection.cc | 7 ++ test/linux/fake_ptrace_connection.h | 3 + util/linux/auxiliary_vector.cc | 17 ++- util/linux/auxiliary_vector.h | 10 +- util/linux/auxiliary_vector_test.cc | 23 ++-- util/linux/direct_ptrace_connection.cc | 8 ++ util/linux/direct_ptrace_connection.h | 2 + util/linux/memory_map.cc | 6 +- util/linux/memory_map.h | 7 +- util/linux/memory_map_test.cc | 36 ++++-- util/linux/ptrace_broker.cc | 129 +++++++++++++++++--- util/linux/ptrace_broker.h | 72 +++++++++-- util/linux/ptrace_broker_test.cc | 30 +++++ util/linux/ptrace_client.cc | 109 ++++++++++++++--- util/linux/ptrace_client.h | 4 + util/linux/ptrace_connection.h | 12 ++ 20 files changed, 424 insertions(+), 115 deletions(-) diff --git a/snapshot/elf/elf_image_reader_test.cc b/snapshot/elf/elf_image_reader_test.cc index 91cce068..d64fd5c4 100644 --- a/snapshot/elf/elf_image_reader_test.cc +++ b/snapshot/elf/elf_image_reader_test.cc @@ -39,6 +39,7 @@ #elif defined(OS_LINUX) || defined(OS_ANDROID) +#include "test/linux/fake_ptrace_connection.h" #include "util/linux/auxiliary_vector.h" #include "util/linux/memory_map.h" @@ -62,7 +63,6 @@ namespace { void LocateExecutable(ProcessType process, ProcessMemory* memory, - bool is_64_bit, VMAddress* elf_address) { uintptr_t debug_address; zx_status_t status = zx_object_get_property(process, @@ -90,18 +90,17 @@ void LocateExecutable(ProcessType process, #elif defined(OS_LINUX) || defined(OS_ANDROID) -void LocateExecutable(ProcessType process, +void LocateExecutable(PtraceConnection* connection, ProcessMemory* memory, - bool is_64_bit, VMAddress* elf_address) { AuxiliaryVector aux; - ASSERT_TRUE(aux.Initialize(process, is_64_bit)); + ASSERT_TRUE(aux.Initialize(connection)); VMAddress phdrs; ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs)); MemoryMap memory_map; - ASSERT_TRUE(memory_map.Initialize(process)); + ASSERT_TRUE(memory_map.Initialize(connection)); const MemoryMap::Mapping* phdr_mapping = memory_map.FindMapping(phdrs); ASSERT_TRUE(phdr_mapping); const MemoryMap::Mapping* exe_mapping = @@ -139,7 +138,13 @@ void ReadThisExecutableInTarget(ProcessType process, ASSERT_TRUE(range.Initialize(&memory, am_64_bit)); VMAddress elf_address; - LocateExecutable(process, &memory, am_64_bit, &elf_address); +#if defined(OS_LINUX) || defined(OS_ANDROID) + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(process)); + LocateExecutable(&connection, &memory, &elf_address); +#elif defined(OS_FUCHSIA) + LocateExecutable(process, &memory, &elf_address); +#endif ASSERT_NO_FATAL_FAILURE(); ElfImageReader reader; diff --git a/snapshot/linux/debug_rendezvous_test.cc b/snapshot/linux/debug_rendezvous_test.cc index 91f2cede..f9920ab4 100644 --- a/snapshot/linux/debug_rendezvous_test.cc +++ b/snapshot/linux/debug_rendezvous_test.cc @@ -27,9 +27,11 @@ #include "build/build_config.h" #include "gtest/gtest.h" #include "snapshot/elf/elf_image_reader.h" +#include "test/linux/fake_ptrace_connection.h" #include "test/multiprocess.h" #include "util/linux/address_types.h" #include "util/linux/auxiliary_vector.h" +#include "util/linux/direct_ptrace_connection.h" #include "util/linux/memory_map.h" #include "util/process/process_memory_linux.h" #include "util/process/process_memory_range.h" @@ -57,18 +59,18 @@ int AndroidRuntimeAPI() { } #endif // OS_ANDROID -void TestAgainstTarget(pid_t pid, bool is_64_bit) { +void TestAgainstTarget(PtraceConnection* connection) { // Use ElfImageReader on the main executable which can tell us the debug // address. glibc declares the symbol _r_debug in link.h which we can use to // get the address, but Android does not. AuxiliaryVector aux; - ASSERT_TRUE(aux.Initialize(pid, is_64_bit)); + ASSERT_TRUE(aux.Initialize(connection)); LinuxVMAddress phdrs; ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs)); MemoryMap mappings; - ASSERT_TRUE(mappings.Initialize(pid)); + ASSERT_TRUE(mappings.Initialize(connection)); const MemoryMap::Mapping* phdr_mapping = mappings.FindMapping(phdrs); ASSERT_TRUE(phdr_mapping); @@ -77,9 +79,9 @@ void TestAgainstTarget(pid_t pid, bool is_64_bit) { LinuxVMAddress elf_address = exe_mapping->range.Base(); ProcessMemoryLinux memory; - ASSERT_TRUE(memory.Initialize(pid)); + ASSERT_TRUE(memory.Initialize(connection->GetProcessID())); ProcessMemoryRange range; - ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); + ASSERT_TRUE(range.Initialize(&memory, connection->Is64Bit())); ElfImageReader exe_reader; ASSERT_TRUE(exe_reader.Initialize(range, elf_address)); @@ -107,7 +109,7 @@ void TestAgainstTarget(pid_t pid, bool is_64_bit) { // glibc's loader does not set the name for the executable. EXPECT_TRUE(debug.Executable()->name.empty()); CheckedLinuxAddressRange exe_range( - is_64_bit, exe_reader.Address(), exe_reader.Size()); + connection->Is64Bit(), exe_reader.Address(), exe_reader.Size()); EXPECT_TRUE(exe_range.ContainsValue(debug.Executable()->dynamic_array)); #endif // OS_ANDROID @@ -179,19 +181,16 @@ void TestAgainstTarget(pid_t pid, bool is_64_bit) { } CheckedLinuxAddressRange module_range( - is_64_bit, module_reader.Address(), module_reader.Size()); + connection->Is64Bit(), module_reader.Address(), module_reader.Size()); EXPECT_TRUE(module_range.ContainsValue(module.dynamic_array)); } } TEST(DebugRendezvous, Self) { -#if defined(ARCH_CPU_64_BITS) - constexpr bool is_64_bit = true; -#else - constexpr bool is_64_bit = false; -#endif + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(getpid())); - TestAgainstTarget(getpid(), is_64_bit); + TestAgainstTarget(&connection); } class ChildTest : public Multiprocess { @@ -201,13 +200,10 @@ class ChildTest : public Multiprocess { private: void MultiprocessParent() { -#if defined(ARCH_CPU_64_BITS) - constexpr bool is_64_bit = true; -#else - constexpr bool is_64_bit = false; -#endif + DirectPtraceConnection connection; + ASSERT_TRUE(connection.Initialize(ChildPID())); - TestAgainstTarget(ChildPID(), is_64_bit); + TestAgainstTarget(&connection); } void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } diff --git a/snapshot/linux/process_reader_linux.cc b/snapshot/linux/process_reader_linux.cc index 40b14068..f59b54f1 100644 --- a/snapshot/linux/process_reader_linux.cc +++ b/snapshot/linux/process_reader_linux.cc @@ -199,11 +199,11 @@ bool ProcessReaderLinux::Initialize(PtraceConnection* connection) { return false; } - pid_t pid = connection->GetProcessID(); - if (!memory_map_.Initialize(pid)) { + if (!memory_map_.Initialize(connection_)) { return false; } + pid_t pid = connection->GetProcessID(); if (!process_memory_.Initialize(pid)) { return false; } @@ -332,7 +332,7 @@ void ProcessReaderLinux::InitializeModules() { INITIALIZATION_STATE_DCHECK_VALID(initialized_); AuxiliaryVector aux; - if (!aux.Initialize(ProcessID(), is_64_bit_)) { + if (!aux.Initialize(connection_)) { return; } diff --git a/snapshot/linux/process_reader_linux_test.cc b/snapshot/linux/process_reader_linux_test.cc index 11a606a9..6c0b98af 100644 --- a/snapshot/linux/process_reader_linux_test.cc +++ b/snapshot/linux/process_reader_linux_test.cc @@ -242,10 +242,11 @@ using ThreadMap = std::map; void ExpectThreads(const ThreadMap& thread_map, const std::vector& threads, - const pid_t pid) { + PtraceConnection* connection) { ASSERT_EQ(threads.size(), thread_map.size()); + MemoryMap memory_map; - ASSERT_TRUE(memory_map.Initialize(pid)); + ASSERT_TRUE(memory_map.Initialize(connection)); for (const auto& thread : threads) { SCOPED_TRACE( @@ -306,7 +307,7 @@ class ChildThreadTest : public Multiprocess { ASSERT_TRUE(process_reader.Initialize(&connection)); const std::vector& threads = process_reader.Threads(); - ExpectThreads(thread_map, threads, ChildPID()); + ExpectThreads(thread_map, threads, &connection); } void MultiprocessChild() override { diff --git a/test/linux/fake_ptrace_connection.cc b/test/linux/fake_ptrace_connection.cc index 26d03711..831842eb 100644 --- a/test/linux/fake_ptrace_connection.cc +++ b/test/linux/fake_ptrace_connection.cc @@ -16,6 +16,7 @@ #include "build/build_config.h" #include "gtest/gtest.h" +#include "util/file/file_io.h" namespace crashpad { namespace test { @@ -70,5 +71,11 @@ bool FakePtraceConnection::GetThreadInfo(pid_t tid, ThreadInfo* info) { return attached; } +bool FakePtraceConnection::ReadFileContents(const base::FilePath& path, + std::string* contents) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return LoggingReadEntireFile(path, contents); +} + } // namespace test } // namespace crashpad diff --git a/test/linux/fake_ptrace_connection.h b/test/linux/fake_ptrace_connection.h index e24bfa58..04911dd4 100644 --- a/test/linux/fake_ptrace_connection.h +++ b/test/linux/fake_ptrace_connection.h @@ -53,6 +53,9 @@ class FakePtraceConnection : public PtraceConnection { //! \brief Does not modify \a info. bool GetThreadInfo(pid_t tid, ThreadInfo* info) override; + bool ReadFileContents(const base::FilePath& path, + std::string* contents) override; + private: std::set attachments_; pid_t pid_; diff --git a/util/linux/auxiliary_vector.cc b/util/linux/auxiliary_vector.cc index 143ab6e3..d3d5ebdf 100644 --- a/util/linux/auxiliary_vector.cc +++ b/util/linux/auxiliary_vector.cc @@ -20,6 +20,7 @@ #include "base/files/file_path.h" #include "base/logging.h" #include "util/file/file_reader.h" +#include "util/file/string_file.h" #include "util/stdlib/map_insert.h" namespace crashpad { @@ -28,18 +29,22 @@ AuxiliaryVector::AuxiliaryVector() : values_() {} AuxiliaryVector::~AuxiliaryVector() {} -bool AuxiliaryVector::Initialize(pid_t pid, bool is_64_bit) { - return is_64_bit ? Read(pid) : Read(pid); +bool AuxiliaryVector::Initialize(PtraceConnection* connection) { + return connection->Is64Bit() ? Read(connection) + : Read(connection); } template -bool AuxiliaryVector::Read(pid_t pid) { +bool AuxiliaryVector::Read(PtraceConnection* connection) { char path[32]; - snprintf(path, sizeof(path), "/proc/%d/auxv", pid); - FileReader aux_file; - if (!aux_file.Open(base::FilePath(path))) { + snprintf(path, sizeof(path), "/proc/%d/auxv", connection->GetProcessID()); + + std::string contents; + if (!connection->ReadFileContents(base::FilePath(path), &contents)) { return false; } + StringFile aux_file; + aux_file.SetString(contents); ULong type; ULong value; diff --git a/util/linux/auxiliary_vector.h b/util/linux/auxiliary_vector.h index 65d0e3cf..a6a44c48 100644 --- a/util/linux/auxiliary_vector.h +++ b/util/linux/auxiliary_vector.h @@ -21,6 +21,7 @@ #include "base/logging.h" #include "base/macros.h" +#include "util/linux/ptrace_connection.h" #include "util/misc/reinterpret_bytes.h" namespace crashpad { @@ -32,16 +33,15 @@ class AuxiliaryVector { ~AuxiliaryVector(); //! \brief Initializes this object with the auxiliary vector for the process - //! with process ID \a pid. + //! connected via \a connection. //! //! This method must be called successfully prior to calling any other method //! in this class. //! - //! \param[in] pid The process ID of a target process. - //! \param[in] is_64_bit Whether the target process is 64-bit. + //! \param[in] connection A connection to the target process. //! //! \return `true` on success, `false` on failure with a message logged. - bool Initialize(pid_t pid, bool is_64_bit); + bool Initialize(PtraceConnection* connection); //! \brief Retrieve a value from the vector. //! @@ -64,7 +64,7 @@ class AuxiliaryVector { private: template - bool Read(pid_t pid); + bool Read(PtraceConnection* connection); DISALLOW_COPY_AND_ASSIGN(AuxiliaryVector); }; diff --git a/util/linux/auxiliary_vector_test.cc b/util/linux/auxiliary_vector_test.cc index 382cb67b..3a69ead1 100644 --- a/util/linux/auxiliary_vector_test.cc +++ b/util/linux/auxiliary_vector_test.cc @@ -25,6 +25,7 @@ #include "build/build_config.h" #include "gtest/gtest.h" #include "test/errors.h" +#include "test/linux/fake_ptrace_connection.h" #include "test/multiprocess.h" #include "util/linux/address_types.h" #include "util/linux/memory_map.h" @@ -45,16 +46,14 @@ namespace test { namespace { void TestAgainstCloneOrSelf(pid_t pid) { -#if defined(ARCH_CPU_64_BITS) - constexpr bool am_64_bit = true; -#else - constexpr bool am_64_bit = false; -#endif + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(pid)); + AuxiliaryVector aux; - ASSERT_TRUE(aux.Initialize(pid, am_64_bit)); + ASSERT_TRUE(aux.Initialize(&connection)); MemoryMap mappings; - ASSERT_TRUE(mappings.Initialize(pid)); + ASSERT_TRUE(mappings.Initialize(&connection)); LinuxVMAddress phdrs; ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs)); @@ -168,13 +167,11 @@ class AuxVecTester : public AuxiliaryVector { }; TEST(AuxiliaryVector, SignedBit) { -#if defined(ARCH_CPU_64_BITS) - constexpr bool am_64_bit = true; -#else - constexpr bool am_64_bit = false; -#endif + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(getpid())); + AuxVecTester aux; - ASSERT_TRUE(aux.Initialize(getpid(), am_64_bit)); + ASSERT_TRUE(&connection); constexpr uint64_t type = 0x0000000012345678; constexpr int32_t neg1_32 = -1; diff --git a/util/linux/direct_ptrace_connection.cc b/util/linux/direct_ptrace_connection.cc index 27ad5b11..f2cf40a3 100644 --- a/util/linux/direct_ptrace_connection.cc +++ b/util/linux/direct_ptrace_connection.cc @@ -16,6 +16,8 @@ #include +#include "util/file/file_io.h" + namespace crashpad { DirectPtraceConnection::DirectPtraceConnection() @@ -63,4 +65,10 @@ bool DirectPtraceConnection::GetThreadInfo(pid_t tid, ThreadInfo* info) { return ptracer_.GetThreadInfo(tid, info); } +bool DirectPtraceConnection::ReadFileContents(const base::FilePath& path, + std::string* contents) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return LoggingReadEntireFile(path, contents); +} + } // namespace crashpad diff --git a/util/linux/direct_ptrace_connection.h b/util/linux/direct_ptrace_connection.h index f1d7ab5f..fca8cafa 100644 --- a/util/linux/direct_ptrace_connection.h +++ b/util/linux/direct_ptrace_connection.h @@ -52,6 +52,8 @@ class DirectPtraceConnection : public PtraceConnection { bool Attach(pid_t tid) override; bool Is64Bit() override; bool GetThreadInfo(pid_t tid, ThreadInfo* info) override; + bool ReadFileContents(const base::FilePath& path, + std::string* contents) override; private: std::vector> attachments_; diff --git a/util/linux/memory_map.cc b/util/linux/memory_map.cc index f2df865a..a8da66c9 100644 --- a/util/linux/memory_map.cc +++ b/util/linux/memory_map.cc @@ -221,7 +221,7 @@ bool MemoryMap::Mapping::Equals(const Mapping& other) const { shareable == other.shareable; } -bool MemoryMap::Initialize(pid_t pid) { +bool MemoryMap::Initialize(PtraceConnection* connection) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); // If the maps file is not read atomically, entries can be read multiple times @@ -235,8 +235,8 @@ bool MemoryMap::Initialize(pid_t pid) { do { std::string contents; char path[32]; - snprintf(path, sizeof(path), "/proc/%d/maps", pid); - if (!LoggingReadEntireFile(base::FilePath(path), &contents)) { + snprintf(path, sizeof(path), "/proc/%d/maps", connection->GetProcessID()); + if (!connection->ReadFileContents(base::FilePath(path), &contents)) { return false; } diff --git a/util/linux/memory_map.h b/util/linux/memory_map.h index 6e5b40c8..c8276ba6 100644 --- a/util/linux/memory_map.h +++ b/util/linux/memory_map.h @@ -22,6 +22,7 @@ #include "util/linux/address_types.h" #include "util/linux/checked_linux_address_range.h" +#include "util/linux/ptrace_connection.h" #include "util/misc/initialization_state_dcheck.h" namespace crashpad { @@ -54,15 +55,15 @@ class MemoryMap { ~MemoryMap(); //! \brief Initializes this object with information about the mapped memory - //! regions in the process whose ID is \a pid. + //! regions in the process connected via \a connection. //! //! This method must be called successfully prior to calling any other method //! in this class. This method may only be called once. //! - //! \param[in] pid The process ID to obtain information for. + //! \param[in] connection A connection to the process create a map for. //! //! \return `true` on success, `false` on failure with a message logged. - bool Initialize(pid_t pid); + bool Initialize(PtraceConnection* connection); //! \return The Mapping containing \a address or `nullptr` if no match is //! found. The caller does not take ownership of this object. It is scoped diff --git a/util/linux/memory_map_test.cc b/util/linux/memory_map_test.cc index 8578a7e5..a056589a 100644 --- a/util/linux/memory_map_test.cc +++ b/util/linux/memory_map_test.cc @@ -26,10 +26,11 @@ #include "gtest/gtest.h" #include "test/errors.h" #include "test/file.h" +#include "test/linux/fake_ptrace_connection.h" #include "test/multiprocess.h" #include "test/scoped_temp_dir.h" #include "util/file/file_io.h" -#include "util/linux/scoped_ptrace_attach.h" +#include "util/linux/direct_ptrace_connection.h" #include "util/misc/clock.h" #include "util/misc/from_pointer_cast.h" #include "util/posix/scoped_mmap.h" @@ -46,8 +47,12 @@ TEST(MemoryMap, SelfBasic) { MAP_SHARED | MAP_ANON, -1, 0)); + + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(getpid())); + MemoryMap map; - ASSERT_TRUE(map.Initialize(getpid())); + ASSERT_TRUE(map.Initialize(&connection)); auto stack_address = FromPointerCast(&map); const MemoryMap::Mapping* mapping = map.FindMapping(stack_address); @@ -118,11 +123,11 @@ class MapChildTest : public Multiprocess { std::string mapped_file_name(path_length, std::string::value_type()); CheckedReadFileExactly(ReadPipeHandle(), &mapped_file_name[0], path_length); - ScopedPtraceAttach attachment; - attachment.ResetAttach(ChildPID()); + DirectPtraceConnection connection; + ASSERT_TRUE(connection.Initialize(ChildPID())); MemoryMap map; - ASSERT_TRUE(map.Initialize(ChildPID())); + ASSERT_TRUE(map.Initialize(&connection)); const MemoryMap::Mapping* mapping = map.FindMapping(code_address); ASSERT_TRUE(mapping); @@ -268,8 +273,11 @@ TEST(MemoryMap, SelfLargeMapFile) { ASSERT_NO_FATAL_FAILURE( InitializeMappings(&mappings, kNumMappings, page_size)); + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(getpid())); + MemoryMap map; - ASSERT_TRUE(map.Initialize(getpid())); + ASSERT_TRUE(map.Initialize(&connection)); ExpectMappings( map, mappings.addr_as(), kNumMappings, page_size); @@ -292,11 +300,11 @@ class MapRunningChildTest : public Multiprocess { // Let the child get back to its work SleepNanoseconds(1000); - ScopedPtraceAttach attachment; - attachment.ResetAttach(ChildPID()); + DirectPtraceConnection connection; + ASSERT_TRUE(connection.Initialize(ChildPID())); MemoryMap map; - ASSERT_TRUE(map.Initialize(ChildPID())); + ASSERT_TRUE(map.Initialize(&connection)); // We should at least find the original mappings. The extra mappings may // or not be found depending on scheduling. @@ -349,8 +357,11 @@ TEST(MemoryMap, MapRunningChild) { // file. The second page should not. void ExpectFindFileMmapStart(LinuxVMAddress mapping_start, LinuxVMSize page_size) { + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(getpid())); + MemoryMap map; - ASSERT_TRUE(map.Initialize(getpid())); + ASSERT_TRUE(map.Initialize(&connection)); auto mapping1 = map.FindMapping(mapping_start); ASSERT_TRUE(mapping1); @@ -391,8 +402,11 @@ TEST(MemoryMap, FindFileMmapStart) { // Basic { + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(getpid())); + MemoryMap map; - ASSERT_TRUE(map.Initialize(getpid())); + ASSERT_TRUE(map.Initialize(&connection)); auto mapping1 = map.FindMapping(mapping_start); ASSERT_TRUE(mapping1); diff --git a/util/linux/ptrace_broker.cc b/util/linux/ptrace_broker.cc index 8810a48f..67647c52 100644 --- a/util/linux/ptrace_broker.cc +++ b/util/linux/ptrace_broker.cc @@ -14,15 +14,22 @@ #include "util/linux/ptrace_broker.h" +#include +#include +#include #include +#include + #include "base/logging.h" +#include "base/posix/eintr_wrapper.h" #include "util/file/file_io.h" namespace crashpad { PtraceBroker::PtraceBroker(int sock, bool is_64_bit) : ptracer_(is_64_bit, /* can_log= */ false), + file_root_("/proc/"), attachments_(nullptr), attach_count_(0), attach_capacity_(0), @@ -32,12 +39,40 @@ PtraceBroker::PtraceBroker(int sock, bool is_64_bit) PtraceBroker::~PtraceBroker() = default; +void PtraceBroker::SetFileRoot(const char* new_root) { + DCHECK_EQ(new_root[strlen(new_root) - 1], '/'); + file_root_ = new_root; +} + int PtraceBroker::Run() { int result = RunImpl(); ReleaseAttachments(); return result; } +bool PtraceBroker::AllocateAttachments() { + constexpr size_t page_size = 4096; + constexpr size_t alloc_size = + (sizeof(ScopedPtraceAttach) + page_size - 1) & ~(page_size - 1); + void* alloc = sbrk(alloc_size); + if (reinterpret_cast(alloc) == -1) { + return false; + } + + if (attachments_ == nullptr) { + attachments_ = reinterpret_cast(alloc); + } + + attach_capacity_ += alloc_size / sizeof(ScopedPtraceAttach); + return true; +} + +void PtraceBroker::ReleaseAttachments() { + for (size_t index = 0; index < attach_count_; ++index) { + attachments_[index].Reset(); + } +} + int PtraceBroker::RunImpl() { while (true) { Request request = {}; @@ -114,6 +149,24 @@ int PtraceBroker::RunImpl() { continue; } + case Request::kTypeReadFile: { + ScopedFileHandle handle; + int result = ReceiveAndOpenFilePath(request.path.path_length, &handle); + if (result != 0) { + return result; + } + + if (!handle.is_valid()) { + continue; + } + + result = SendFileContents(handle.get()); + if (result != 0) { + return result; + } + continue; + } + case Request::kTypeReadMemory: { int result = SendMemory(request.tid, request.iov.base, request.iov.size); @@ -132,6 +185,46 @@ int PtraceBroker::RunImpl() { } } +int PtraceBroker::SendError(Errno err) { + return WriteFile(sock_, &err, sizeof(err)) ? 0 : errno; +} + +int PtraceBroker::SendReadError(Errno err) { + int32_t rv = -1; + if (!WriteFile(sock_, &rv, sizeof(rv))) { + return errno; + } + return SendError(err); +} + +int PtraceBroker::SendOpenResult(OpenResult result) { + return WriteFile(sock_, &result, sizeof(result)) ? 0 : errno; +} + +int PtraceBroker::SendFileContents(FileHandle handle) { + char buffer[4096]; + int32_t rv; + do { + rv = ReadFile(handle, buffer, sizeof(buffer)); + + if (rv < 0) { + return SendReadError(errno); + } + + if (!WriteFile(sock_, &rv, sizeof(rv))) { + return errno; + } + + if (rv > 0) { + if (!WriteFile(sock_, buffer, static_cast(rv))) { + return errno; + } + } + } while (rv > 0); + + return 0; +} + int PtraceBroker::SendMemory(pid_t pid, VMAddress address, VMSize size) { char buffer[4096]; while (size > 0) { @@ -161,27 +254,31 @@ int PtraceBroker::SendMemory(pid_t pid, VMAddress address, VMSize size) { return 0; } -bool PtraceBroker::AllocateAttachments() { - constexpr size_t page_size = 4096; - constexpr size_t alloc_size = - (sizeof(ScopedPtraceAttach) + page_size - 1) & ~(page_size - 1); - void* alloc = sbrk(alloc_size); - if (reinterpret_cast(alloc) == -1) { - return false; +int PtraceBroker::ReceiveAndOpenFilePath(VMSize path_length, + ScopedFileHandle* handle) { + char path[std::max(4096, PATH_MAX)]; + + if (path_length >= sizeof(path)) { + return SendOpenResult(kOpenResultTooLong); } - if (attachments_ == nullptr) { - attachments_ = reinterpret_cast(alloc); + if (!ReadFileExactly(sock_, path, path_length)) { + return errno; + } + path[path_length] = '\0'; + + if (strncmp(path, file_root_, strlen(file_root_)) != 0) { + return SendOpenResult(kOpenResultAccessDenied); } - attach_capacity_ += alloc_size / sizeof(ScopedPtraceAttach); - return true; -} - -void PtraceBroker::ReleaseAttachments() { - for (size_t index = 0; index < attach_count_; ++index) { - attachments_[index].Reset(); + ScopedFileHandle local_handle( + HANDLE_EINTR(open(path, O_RDONLY | O_CLOEXEC | O_NOCTTY))); + if (!local_handle.is_valid()) { + return SendOpenResult(static_cast(errno)); } + + handle->reset(local_handle.release()); + return SendOpenResult(kOpenResultSuccess); } } // namespace crashpad diff --git a/util/linux/ptrace_broker.h b/util/linux/ptrace_broker.h index 96cbee58..a8d0e952 100644 --- a/util/linux/ptrace_broker.h +++ b/util/linux/ptrace_broker.h @@ -20,6 +20,7 @@ #include #include "base/macros.h" +#include "util/file/file_io.h" #include "util/linux/exception_handler_protocol.h" #include "util/linux/ptrace_connection.h" #include "util/linux/ptracer.h" @@ -69,6 +70,14 @@ class PtraceBroker { //! to zero, followed by an Errno. kTypeReadMemory, + //! \brief Read a file's contents. The data is returned in a series of + //! messages. The first message is an OpenResult, indicating the + //! validity of the received file path. If the OpenResult is + //! kOpenResultSuccess, each subsequent message begins with an int32_t + //! indicating the number of bytes read, 0 for end-of-file, or -1 for + //! errors, followed by an Errno. On success, the bytes read follow. + kTypeReadFile, + //! \brief Causes the broker to return from Run(), detaching all attached //! threads. Does not respond. kTypeExit @@ -78,14 +87,41 @@ class PtraceBroker { //! kTypeGetThreadInfo, and kTypeReadMemory. pid_t tid; - //! \brief Specifies the memory region to read for a kTypeReadMemory request. - struct { - //! \brief The base address of the memory region. - VMAddress base; + union { + //! \brief Specifies the memory region to read for a kTypeReadMemory + //! request. + struct { + //! \brief The base address of the memory region. + VMAddress base; - //! \brief The size of the memory region. - VMSize size; - } iov; + //! \brief The size of the memory region. + VMSize size; + } iov; + + // \brief Specifies the file path to read for a kTypeReadFile request. + struct { + //! \brief The number of bytes in #path. The path should not include a + //! `NUL`-terminator. + VMSize path_length; + + //! \brief The file path to read. + char path[]; + } path; + }; + }; + + //! \brief A result used in operations that accept paths. + //! + //! Positive values of this enum are reserved for sending errno values. + enum OpenResult : int32_t { + //! \brief Access to the path is denied. + kOpenResultAccessDenied = -2, + + //! \brief The path name is too long. + kOpenResultTooLong = -1, + + //! \brief The file was successfully opened. + kOpenResultSuccess = 0, }; //! \brief The response sent for a Request with type kTypeGetThreadInfo. @@ -109,6 +145,18 @@ class PtraceBroker { ~PtraceBroker(); + //! \brief Restricts the broker to serving the contents of files under \a + //! root. + //! + //! If this method is not called, the broker defaults to only serving files + //! under "/proc/". + //! + //! \param[in] root A NUL-terminated c-string containing the path to the new + //! root. \a root must not be `nullptr`, must end in a '/', and the caller + //! should ensure that \a root remains valid for the lifetime of the + //! broker. + void SetFileRoot(const char* root); + //! \brief Begin serving requests on the configured socket. //! //! This method returns when a PtraceBrokerRequest with type kTypeExit is @@ -121,12 +169,18 @@ class PtraceBroker { int Run(); private: - int RunImpl(); - int SendMemory(pid_t pid, VMAddress address, VMSize size); bool AllocateAttachments(); void ReleaseAttachments(); + int RunImpl(); + int SendError(Errno err); + int SendReadError(Errno err); + int SendOpenResult(OpenResult result); + int SendFileContents(FileHandle handle); + int SendMemory(pid_t pid, VMAddress address, VMSize size); + int ReceiveAndOpenFilePath(VMSize path_length, ScopedFileHandle* handle); Ptracer ptracer_; + const char* file_root_; ScopedPtraceAttach* attachments_; size_t attach_count_; size_t attach_capacity_; diff --git a/util/linux/ptrace_broker_test.cc b/util/linux/ptrace_broker_test.cc index 749df759..65fef9d9 100644 --- a/util/linux/ptrace_broker_test.cc +++ b/util/linux/ptrace_broker_test.cc @@ -23,8 +23,10 @@ #include "build/build_config.h" #include "gtest/gtest.h" +#include "test/filesystem.h" #include "test/linux/get_tls.h" #include "test/multiprocess.h" +#include "test/scoped_temp_dir.h" #include "util/file/file_io.h" #include "util/linux/ptrace_client.h" #include "util/posix/scoped_mmap.h" @@ -143,6 +145,23 @@ class SameBitnessTest : public Multiprocess { ScopedFileHandle broker_sock(socks[0]); ScopedFileHandle client_sock(socks[1]); + ScopedTempDir temp_dir; + base::FilePath file_path(temp_dir.path().Append("test_file")); + std::string expected_file_contents; + { + expected_file_contents.resize(4097); + for (size_t i = 0; i < expected_file_contents.size(); ++i) { + expected_file_contents[i] = static_cast(i % 256); + } + ScopedFileHandle handle( + LoggingOpenFileForWrite(file_path, + FileWriteMode::kCreateOrFail, + FilePermissions::kWorldReadable)); + ASSERT_TRUE(LoggingWriteFile(handle.get(), + expected_file_contents.data(), + expected_file_contents.size())); + } + #if defined(ARCH_CPU_64_BITS) constexpr bool am_64_bit = true; #else @@ -192,6 +211,17 @@ class SameBitnessTest : public Multiprocess { EXPECT_FALSE(client.Read(mapping_.addr_as() + mapping_.len(), sizeof(unmapped), &unmapped)); + + std::string file_root = temp_dir.path().value() + '/'; + broker.SetFileRoot(file_root.c_str()); + std::string file_contents; + ASSERT_TRUE(client.ReadFileContents(file_path, &file_contents)); + EXPECT_EQ(file_contents, expected_file_contents); + + ScopedTempDir temp_dir2; + base::FilePath test_file2(temp_dir2.path().Append("test_file2")); + ASSERT_TRUE(CreateFile(test_file2)); + EXPECT_FALSE(client.ReadFileContents(test_file2, &file_contents)); } } diff --git a/util/linux/ptrace_client.cc b/util/linux/ptrace_client.cc index 3cdfcbe2..ef880b2e 100644 --- a/util/linux/ptrace_client.cc +++ b/util/linux/ptrace_client.cc @@ -101,6 +101,42 @@ bool PtraceClient::Initialize(int sock, pid_t pid) { return true; } +bool PtraceClient::Read(VMAddress address, size_t size, void* buffer) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + char* buffer_c = reinterpret_cast(buffer); + + PtraceBroker::Request request; + request.type = PtraceBroker::Request::kTypeReadMemory; + request.tid = pid_; + request.iov.base = address; + request.iov.size = size; + + if (!LoggingWriteFile(sock_, &request, sizeof(request))) { + return false; + } + + while (size > 0) { + VMSize bytes_read; + if (!LoggingReadFileExactly(sock_, &bytes_read, sizeof(bytes_read))) { + return false; + } + + if (!bytes_read) { + ReceiveAndLogError(sock_, "PtraceBroker ReadMemory"); + return false; + } + + if (!LoggingReadFileExactly(sock_, buffer_c, bytes_read)) { + return false; + } + + size -= bytes_read; + buffer_c += bytes_read; + } + + return true; +} + pid_t PtraceClient::GetProcessID() { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return pid_; @@ -140,40 +176,77 @@ bool PtraceClient::GetThreadInfo(pid_t tid, ThreadInfo* info) { return false; } -bool PtraceClient::Read(VMAddress address, size_t size, void* buffer) { +bool PtraceClient::ReadFileContents(const base::FilePath& path, + std::string* contents) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - char* buffer_c = reinterpret_cast(buffer); PtraceBroker::Request request; - request.type = PtraceBroker::Request::kTypeReadMemory; - request.tid = pid_; - request.iov.base = address; - request.iov.size = size; + request.type = PtraceBroker::Request::kTypeReadFile; + request.path.path_length = path.value().size(); - if (!LoggingWriteFile(sock_, &request, sizeof(request))) { + if (!LoggingWriteFile(sock_, &request, sizeof(request)) || + !SendFilePath(path.value().c_str(), request.path.path_length)) { return false; } - while (size > 0) { - VMSize bytes_read; - if (!LoggingReadFileExactly(sock_, &bytes_read, sizeof(bytes_read))) { + std::string local_contents; + int32_t read_result; + do { + if (!LoggingReadFileExactly(sock_, &read_result, sizeof(read_result))) { return false; } - if (!bytes_read) { - ReceiveAndLogError(sock_, "PtraceBroker ReadMemory"); + if (read_result < 0) { + ReceiveAndLogError(sock_, "ReadFileContents"); return false; } - if (!LoggingReadFileExactly(sock_, buffer_c, bytes_read)) { - return false; + if (read_result > 0) { + size_t old_length = local_contents.size(); + local_contents.resize(old_length + read_result); + if (!LoggingReadFileExactly( + sock_, &local_contents[old_length], read_result)) { + return false; + } } + } while (read_result > 0); - size -= bytes_read; - buffer_c += bytes_read; - } - + contents->swap(local_contents); return true; } +bool PtraceClient::SendFilePath(const char* path, size_t length) { + if (!LoggingWriteFile(sock_, path, length)) { + return false; + } + + PtraceBroker::OpenResult result; + if (!LoggingReadFileExactly(sock_, &result, sizeof(result))) { + return false; + } + + switch (result) { + case PtraceBroker::kOpenResultAccessDenied: + LOG(ERROR) << "Broker Open: access denied"; + return false; + + case PtraceBroker::kOpenResultTooLong: + LOG(ERROR) << "Broker Open: path too long"; + return false; + + case PtraceBroker::kOpenResultSuccess: + return true; + + default: + if (result < 0) { + LOG(ERROR) << "Broker Open: invalid result " << result; + DCHECK(false); + } else { + errno = result; + PLOG(ERROR) << "Broker Open"; + } + return false; + } +} + } // namespace crashpad diff --git a/util/linux/ptrace_client.h b/util/linux/ptrace_client.h index 397c891f..8260412d 100644 --- a/util/linux/ptrace_client.h +++ b/util/linux/ptrace_client.h @@ -70,8 +70,12 @@ class PtraceClient : public PtraceConnection { bool Attach(pid_t tid) override; bool Is64Bit() override; bool GetThreadInfo(pid_t tid, ThreadInfo* info) override; + bool ReadFileContents(const base::FilePath& path, + std::string* contents) override; private: + bool SendFilePath(const char* path, size_t length); + int sock_; pid_t pid_; bool is_64_bit_; diff --git a/util/linux/ptrace_connection.h b/util/linux/ptrace_connection.h index d09cd20d..e3111112 100644 --- a/util/linux/ptrace_connection.h +++ b/util/linux/ptrace_connection.h @@ -17,6 +17,9 @@ #include +#include + +#include "base/files/file_path.h" #include "util/linux/thread_info.h" namespace crashpad { @@ -45,6 +48,15 @@ class PtraceConnection { //! \param[out] info Information about the thread. //! \return `true` on success. `false` on failure with a message logged. virtual bool GetThreadInfo(pid_t tid, ThreadInfo* info) = 0; + + //! \brief Reads the entire contents of a file. + //! + //! \param[in] path The path of the file to read. + //! \param[out] contents The file contents, valid if this method returns + //! `true`. + //! \return `true` on success. `false` on failure with a message logged. + virtual bool ReadFileContents(const base::FilePath& path, + std::string* contents) = 0; }; } // namespace crashpad From 08ce02c3527674f331d4ebfd5ac0bdc4748c4661 Mon Sep 17 00:00:00 2001 From: Jose Dapena Paz Date: Wed, 4 Apr 2018 18:13:02 +0200 Subject: [PATCH 228/326] Fix crashpad build on non Android ARMEL with a recent GLIBC. user_vfp is not declared anymore in sys/user.h, but in specific internal kernel asm user.h in GLIBC. So building crashpad on ARMEL with such a GLIBC will fail to build. Also, sys/ptrace.h will not include the declarations for PTRACE_GET_THREAD_AREA and PTRACE_GETVFPREG in arm (they are in asm/ptrace.h and not included from sys/ptrace.h). So provide compatibility declarations accordingly for arm architecture. Change-Id: I58ab5274a66d84c0cbc9e9e9b23e0abc37bf67e5 Reviewed-on: https://chromium-review.googlesource.com/996073 Reviewed-by: Mark Mentovai --- AUTHORS | 1 + compat/linux/sys/ptrace.h | 19 ++++++++++++++++--- util/linux/thread_info.h | 2 ++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index eb3534a6..3ad137fd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -9,3 +9,4 @@ Google Inc. Opera Software ASA Vewd Software AS +LG Electronics, Inc. diff --git a/compat/linux/sys/ptrace.h b/compat/linux/sys/ptrace.h index e68125b5..07806b08 100644 --- a/compat/linux/sys/ptrace.h +++ b/compat/linux/sys/ptrace.h @@ -20,11 +20,24 @@ #include // https://sourceware.org/bugzilla/show_bug.cgi?id=22433 -#if !defined(PTRACE_GET_THREAD_AREA) && \ - defined(__GLIBC__) && (defined(__i386__) || defined(__x86_64__)) +#if !defined(PTRACE_GET_THREAD_AREA) && defined(__GLIBC__) +#if defined(__i386__) || defined(__x86_64__) static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = static_cast<__ptrace_request>(25); #define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA -#endif // !PTRACE_GET_THREAD_AREA && __GLIBC__ && (__i386__ || __x86_64__) +#elif defined(__arm__) || defined(__arm64__) +static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = + static_cast<__ptrace_request>(22); +#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA +#endif +#endif // !PTRACE_GET_THREAD_AREA && defined(__GLIBC__) + +// https://sourceware.org/bugzilla/show_bug.cgi?id=22433 +#if !defined(PTRACE_GETVFPREGS) && \ + defined(__GLIBC__) && (defined(__arm__) || defined(__arm64__)) +static constexpr __ptrace_request PTRACE_GETVFPREGS = + static_cast<__ptrace_request>(27); +#define PTRACE_GETVFPREGS PTRACE_GETVFPREGS +#endif #endif // CRASHPAD_COMPAT_LINUX_SYS_PTRACE_H_ diff --git a/util/linux/thread_info.h b/util/linux/thread_info.h index 94424dd4..91d0082f 100644 --- a/util/linux/thread_info.h +++ b/util/linux/thread_info.h @@ -232,7 +232,9 @@ union FloatContext { "Size mismatch"); #elif defined(ARCH_CPU_ARMEL) static_assert(sizeof(f32_t::fpregs) == sizeof(user_fpregs), "Size mismatch"); +#if !defined(__GLIBC__) static_assert(sizeof(f32_t::vfp) == sizeof(user_vfp), "Size mismatch"); +#endif #elif defined(ARCH_CPU_ARM64) static_assert(sizeof(f64) == sizeof(user_fpsimd_struct), "Size mismatch"); #else From 74a56c256b101eb1ded190cbda225a82c3f1872d Mon Sep 17 00:00:00 2001 From: Fabrice de Gans-Riberi Date: Wed, 4 Apr 2018 15:12:50 -0700 Subject: [PATCH 229/326] Prepare for removal of is_posix from the Fuchsia build This also rolls crashpad/third_party/mini_chromium/mini_chromium/ d42eb4101..6e0fdb2e4 (2 commits) https://chromium.googlesource.com/chromium/mini_chromium/+log/d42eb4101236..6e0fdb2e4966 $ git log d42eb4101..6e0fdb2e4 --date=short --no-merges --format='%ad %ae %s' 2018-04-04 scottmg@chromium.org Repair Linux-with-sysroot build after clang update 2018-04-04 fdegans Prepare for |is_posix| switch in the Fuchsia build Created with: roll-dep crashpad/third_party/mini_chromium/mini_chromium Bug: chromium:812974 Change-Id: I3d8efc3124c97981eda63f104a7adfb670babab7 Reviewed-on: https://chromium-review.googlesource.com/988231 Reviewed-by: Mark Mentovai --- DEPS | 2 +- snapshot/BUILD.gn | 2 +- test/BUILD.gn | 7 +++---- util/BUILD.gn | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/DEPS b/DEPS index 94847ae8..116e5b0b 100644 --- a/DEPS +++ b/DEPS @@ -29,7 +29,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'd42eb410123667fe94427986d2616795897efa0c', + '6e0fdb2e4966ec44b1ce7b8464fd7c80d1b59203', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 9bb49ae4..44bb53f6 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -56,7 +56,7 @@ static_library("snapshot") { "unloaded_module_snapshot.h", ] - if (crashpad_is_posix) { + if (crashpad_is_posix || crashpad_is_fuchsia) { sources += [ "posix/timezone.cc", "posix/timezone.h", diff --git a/test/BUILD.gn b/test/BUILD.gn index dc2e2a75..a8d65954 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -44,7 +44,7 @@ static_library("test") { "test_paths.h", ] - if (crashpad_is_posix) { + if (crashpad_is_posix || crashpad_is_fuchsia) { sources += [ "scoped_temp_dir_posix.cc" ] if (!crashpad_is_fuchsia) { @@ -140,6 +140,8 @@ source_set("test_test") { "test_paths_test.cc", ] + # TODO(crbug.com/812974): Remove !crashpad_is_fuchsia when Fuchsia is no + # longer treated as a posix platform. if (crashpad_is_posix && !crashpad_is_fuchsia) { sources += [ "multiprocess_posix_test.cc" ] } @@ -155,9 +157,6 @@ source_set("test_test") { ] } - if (!crashpad_is_fuchsia) { - } - deps = [ ":test", "../compat", diff --git a/util/BUILD.gn b/util/BUILD.gn index 1e2c968c..e8f2dc08 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -160,7 +160,7 @@ static_library("util") { "thread/worker_thread.h", ] - if (crashpad_is_posix) { + if (crashpad_is_posix || crashpad_is_fuchsia) { sources += [ "file/directory_reader_posix.cc", "file/file_io_posix.cc", @@ -512,7 +512,7 @@ source_set("util_test") { sources += [ "net/http_transport_test.cc" ] } - if (crashpad_is_posix) { + if (crashpad_is_posix || crashpad_is_fuchsia) { if (!crashpad_is_fuchsia) { sources += [ "posix/process_info_test.cc", From ba0bd63254a7dd1dd059380e2746f79832815b5d Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 4 Apr 2018 15:03:43 -0700 Subject: [PATCH 230/326] Use clang ref:goma instead of ref:latest This is considered a "stable-latest" by the Fuchsia toolchain team that maintains this package. Bug: crashpad:30, crashpad:196 Change-Id: I24a57abc9c0eaaab9b003b204ee56e73fad88f11 Reviewed-on: https://chromium-review.googlesource.com/996308 Reviewed-by: Mark Mentovai --- DEPS | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 116e5b0b..e6719e2a 100644 --- a/DEPS +++ b/DEPS @@ -120,6 +120,8 @@ hooks = [ # Linux build host and vice-versa. https://crbug.com/789364. This package is # only updated when the solution in .gclient includes an entry like: # "custom_vars": { "pull_linux_clang": True } + # The ref used is "goma". This is like "latest", but is considered a more + # stable latest by the Fuchsia toolchain team. 'name': 'clang_linux', 'pattern': '.', 'condition': 'checkout_linux and pull_linux_clang', @@ -129,7 +131,7 @@ hooks = [ # sic, using Fuchsia team's generic build of clang for linux-amd64 to # build for linux-amd64 target too. 'fuchsia/clang/linux-amd64', - 'latest', + 'goma', '-root', 'crashpad/third_party/linux/clang/linux-amd64', '-log-level', 'info', ], @@ -137,7 +139,7 @@ hooks = [ { # If using a local clang ("pull_linux_clang" above), also pull down a # sysroot. - 'name': 'clang_linux', + 'name': 'sysroot_linux', 'pattern': '.', 'condition': 'checkout_linux and pull_linux_clang', 'action': [ @@ -147,6 +149,7 @@ hooks = [ { # Same rationale for using "install" rather than "ensure" as for first clang # package. https://crbug.com/789364. + # Same rationale for using "goma" instead of "latest" as clang_linux above. 'name': 'fuchsia_clang_mac', 'pattern': '.', 'condition': 'checkout_fuchsia and host_os == "mac"', @@ -154,7 +157,7 @@ hooks = [ 'cipd', 'install', 'fuchsia/clang/mac-amd64', - 'latest', + 'goma', '-root', 'crashpad/third_party/fuchsia/clang/mac-amd64', '-log-level', 'info', ], @@ -162,6 +165,7 @@ hooks = [ { # Same rationale for using "install" rather than "ensure" as for first clang # package. https://crbug.com/789364. + # Same rationale for using "goma" instead of "latest" as clang_linux above. 'name': 'fuchsia_clang_linux', 'pattern': '.', 'condition': 'checkout_fuchsia and host_os == "linux"', @@ -169,7 +173,7 @@ hooks = [ 'cipd', 'install', 'fuchsia/clang/linux-amd64', - 'latest', + 'goma', '-root', 'crashpad/third_party/fuchsia/clang/linux-amd64', '-log-level', 'info', ], From 1ebedb05dde4fccb9d9459062a3a9fa9cb28bd5a Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 4 Apr 2018 16:29:46 -0700 Subject: [PATCH 231/326] linux: Fix failing exception handler server test The test broke at: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/978630 which moved responsibility for sending a ForkBroker request to PtraceDecider. MockPtraceDecider wasn't updated to send this request, so no broker was forked. Change-Id: I8eddcc57c7b45419a72f1239c1cc9ab27e4ac2d2 Reviewed-on: https://chromium-review.googlesource.com/996715 Reviewed-by: Mark Mentovai --- .../linux/exception_handler_server_test.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/handler/linux/exception_handler_server_test.cc b/handler/linux/exception_handler_server_test.cc index b68e7384..247ac8c6 100644 --- a/handler/linux/exception_handler_server_test.cc +++ b/handler/linux/exception_handler_server_test.cc @@ -141,6 +141,25 @@ class MockPtraceStrategyDecider : public PtraceStrategyDecider { ~MockPtraceStrategyDecider() {} Strategy ChooseStrategy(int sock, const ucred& client_credentials) override { + if (strategy_ == Strategy::kUseBroker) { + ServerToClientMessage message = {}; + message.type = ServerToClientMessage::kTypeForkBroker; + + Errno status; + bool result = LoggingWriteFile(sock, &message, sizeof(message)) && + LoggingReadFileExactly(sock, &status, sizeof(status)); + EXPECT_TRUE(result); + + if (!result) { + return Strategy::kError; + } + + if (status != 0) { + errno = status; + ADD_FAILURE() << ErrnoMessage("Handler Client ForkBroker"); + return Strategy::kNoPtrace; + } + } return strategy_; } From 10fd672bde9d8c4283da732b8ac0a58decbcc533 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 5 Apr 2018 13:00:15 -0700 Subject: [PATCH 232/326] linux: Enable brokered memory reading This change: 1. Updates the broker's memory reading protocol to enable short reads. 2. Updates Ptracer to allow short reads. 3. Updates the broker to allow reading from a memory file. 4. Updates the broker's default file root to be "/proc/[pid]/". 5. Adds PtraceConnection::Memory() to produce a suitable memory reader for a connection type. Bug: crashpad:30 Change-Id: I8c004016065d981acd1fa74ad1b8e51ce07c7c85 Reviewed-on: https://chromium-review.googlesource.com/991455 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- snapshot/linux/process_reader_linux.cc | 6 - snapshot/linux/process_reader_linux.h | 4 +- test/linux/fake_ptrace_connection.cc | 15 +++ test/linux/fake_ptrace_connection.h | 7 ++ util/linux/direct_ptrace_connection.cc | 10 ++ util/linux/direct_ptrace_connection.h | 3 + util/linux/exception_handler_client.cc | 2 +- util/linux/ptrace_broker.cc | 113 +++++++++++++++--- util/linux/ptrace_broker.h | 39 ++++-- util/linux/ptrace_broker_test.cc | 159 +++++++++++++++---------- util/linux/ptrace_client.cc | 134 +++++++++++++++------ util/linux/ptrace_client.h | 44 ++++--- util/linux/ptrace_connection.h | 7 ++ util/linux/ptracer.cc | 69 +++++++---- util/linux/ptracer.h | 19 ++- util/process/process_memory.h | 3 +- 16 files changed, 442 insertions(+), 192 deletions(-) diff --git a/snapshot/linux/process_reader_linux.cc b/snapshot/linux/process_reader_linux.cc index f59b54f1..8e25e77d 100644 --- a/snapshot/linux/process_reader_linux.cc +++ b/snapshot/linux/process_reader_linux.cc @@ -182,7 +182,6 @@ ProcessReaderLinux::ProcessReaderLinux() threads_(), modules_(), elf_readers_(), - process_memory_(), is_64_bit_(false), initialized_threads_(false), initialized_modules_(false), @@ -203,11 +202,6 @@ bool ProcessReaderLinux::Initialize(PtraceConnection* connection) { return false; } - pid_t pid = connection->GetProcessID(); - if (!process_memory_.Initialize(pid)) { - return false; - } - is_64_bit_ = process_info_.Is64Bit(); INITIALIZATION_STATE_SET_VALID(initialized_); diff --git a/snapshot/linux/process_reader_linux.h b/snapshot/linux/process_reader_linux.h index 2e89655e..27e2cea8 100644 --- a/snapshot/linux/process_reader_linux.h +++ b/snapshot/linux/process_reader_linux.h @@ -32,7 +32,6 @@ #include "util/misc/initialization_state_dcheck.h" #include "util/posix/process_info.h" #include "util/process/process_memory.h" -#include "util/process/process_memory_linux.h" namespace crashpad { @@ -103,7 +102,7 @@ class ProcessReaderLinux { pid_t ParentProcessID() const { return process_info_.ParentProcessID(); } //! \brief Return a memory reader for the target process. - ProcessMemory* Memory() { return &process_memory_; } + ProcessMemory* Memory() { return connection_->Memory(); } //! \brief Return a memory map of the target process. MemoryMap* GetMemoryMap() { return &memory_map_; } @@ -146,7 +145,6 @@ class ProcessReaderLinux { std::vector threads_; std::vector modules_; std::vector> elf_readers_; - ProcessMemoryLinux process_memory_; bool is_64_bit_; bool initialized_threads_; bool initialized_modules_; diff --git a/test/linux/fake_ptrace_connection.cc b/test/linux/fake_ptrace_connection.cc index 831842eb..022b1327 100644 --- a/test/linux/fake_ptrace_connection.cc +++ b/test/linux/fake_ptrace_connection.cc @@ -14,6 +14,8 @@ #include "test/linux/fake_ptrace_connection.h" +#include + #include "build/build_config.h" #include "gtest/gtest.h" #include "util/file/file_io.h" @@ -77,5 +79,18 @@ bool FakePtraceConnection::ReadFileContents(const base::FilePath& path, return LoggingReadEntireFile(path, contents); } +ProcessMemory* FakePtraceConnection::Memory() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!memory_) { + auto mem = std::make_unique(); + if (mem->Initialize(pid_)) { + memory_ = std::move(mem); + } else { + ADD_FAILURE(); + } + } + return memory_.get(); +} + } // namespace test } // namespace crashpad diff --git a/test/linux/fake_ptrace_connection.h b/test/linux/fake_ptrace_connection.h index 04911dd4..6597c473 100644 --- a/test/linux/fake_ptrace_connection.h +++ b/test/linux/fake_ptrace_connection.h @@ -17,11 +17,13 @@ #include +#include #include #include "base/macros.h" #include "util/linux/ptrace_connection.h" #include "util/misc/initialization_state_dcheck.h" +#include "util/process/process_memory_linux.h" namespace crashpad { namespace test { @@ -56,8 +58,13 @@ class FakePtraceConnection : public PtraceConnection { bool ReadFileContents(const base::FilePath& path, std::string* contents) override; + //! \brief Attempts to create a ProcessMemory when called, calling + //! ADD_FAILURE() and returning `nullptr` on failure. + ProcessMemory* Memory() override; + private: std::set attachments_; + std::unique_ptr memory_; pid_t pid_; bool is_64_bit_; InitializationStateDcheck initialized_; diff --git a/util/linux/direct_ptrace_connection.cc b/util/linux/direct_ptrace_connection.cc index f2cf40a3..a3df425f 100644 --- a/util/linux/direct_ptrace_connection.cc +++ b/util/linux/direct_ptrace_connection.cc @@ -23,6 +23,7 @@ namespace crashpad { DirectPtraceConnection::DirectPtraceConnection() : PtraceConnection(), attachments_(), + memory_(), pid_(-1), ptracer_(/* can_log= */ true), initialized_() {} @@ -37,6 +38,10 @@ bool DirectPtraceConnection::Initialize(pid_t pid) { } pid_ = pid; + if (!memory_.Initialize(pid)) { + return false; + } + INITIALIZATION_STATE_SET_VALID(initialized_); return true; } @@ -71,4 +76,9 @@ bool DirectPtraceConnection::ReadFileContents(const base::FilePath& path, return LoggingReadEntireFile(path, contents); } +ProcessMemory* DirectPtraceConnection::Memory() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &memory_; +} + } // namespace crashpad diff --git a/util/linux/direct_ptrace_connection.h b/util/linux/direct_ptrace_connection.h index fca8cafa..53594ef9 100644 --- a/util/linux/direct_ptrace_connection.h +++ b/util/linux/direct_ptrace_connection.h @@ -25,6 +25,7 @@ #include "util/linux/ptracer.h" #include "util/linux/scoped_ptrace_attach.h" #include "util/misc/initialization_state_dcheck.h" +#include "util/process/process_memory_linux.h" namespace crashpad { @@ -54,9 +55,11 @@ class DirectPtraceConnection : public PtraceConnection { bool GetThreadInfo(pid_t tid, ThreadInfo* info) override; bool ReadFileContents(const base::FilePath& path, std::string* contents) override; + ProcessMemory* Memory() override; private: std::vector> attachments_; + ProcessMemoryLinux memory_; pid_t pid_; Ptracer ptracer_; InitializationStateDcheck initialized_; diff --git a/util/linux/exception_handler_client.cc b/util/linux/exception_handler_client.cc index cdfd0c5c..6333351c 100644 --- a/util/linux/exception_handler_client.cc +++ b/util/linux/exception_handler_client.cc @@ -130,7 +130,7 @@ int ExceptionHandlerClient::WaitForCrashDumpComplete() { constexpr bool am_64_bit = false; #endif // ARCH_CPU_64_BITS - PtraceBroker broker(server_sock_, am_64_bit); + PtraceBroker broker(server_sock_, getppid(), am_64_bit); _exit(broker.Run()); } diff --git a/util/linux/ptrace_broker.cc b/util/linux/ptrace_broker.cc index 67647c52..e9d30686 100644 --- a/util/linux/ptrace_broker.cc +++ b/util/linux/ptrace_broker.cc @@ -23,24 +23,65 @@ #include "base/logging.h" #include "base/posix/eintr_wrapper.h" -#include "util/file/file_io.h" namespace crashpad { -PtraceBroker::PtraceBroker(int sock, bool is_64_bit) +namespace { + +size_t FormatPID(char* buffer, pid_t pid) { + DCHECK_GE(pid, 0); + + char pid_buf[16]; + size_t length = 0; + do { + DCHECK_LT(length, sizeof(pid_buf)); + + pid_buf[length] = '0' + pid % 10; + pid /= 10; + ++length; + } while (pid > 0); + + for (size_t index = 0; index < length; ++index) { + buffer[index] = pid_buf[length - index - 1]; + } + + return length; +} + +} // namespace + +PtraceBroker::PtraceBroker(int sock, pid_t pid, bool is_64_bit) : ptracer_(is_64_bit, /* can_log= */ false), - file_root_("/proc/"), + file_root_(file_root_buffer_), attachments_(nullptr), attach_count_(0), attach_capacity_(0), - sock_(sock) { + memory_file_(), + sock_(sock), + memory_pid_(pid), + tried_opening_mem_file_(false) { AllocateAttachments(); + + static constexpr char kProc[] = "/proc/"; + size_t root_length = strlen(kProc); + memcpy(file_root_buffer_, kProc, root_length); + + if (pid >= 0) { + root_length += FormatPID(file_root_buffer_ + root_length, pid); + DCHECK_LT(root_length, sizeof(file_root_buffer_)); + file_root_buffer_[root_length] = '/'; + ++root_length; + } + + DCHECK_LT(root_length, sizeof(file_root_buffer_)); + file_root_buffer_[root_length] = '\0'; } PtraceBroker::~PtraceBroker() = default; void PtraceBroker::SetFileRoot(const char* new_root) { DCHECK_EQ(new_root[strlen(new_root) - 1], '/'); + memory_pid_ = -1; file_root_ = new_root; } @@ -189,12 +230,12 @@ int PtraceBroker::SendError(Errno err) { return WriteFile(sock_, &err, sizeof(err)) ? 0 : errno; } -int PtraceBroker::SendReadError(Errno err) { +int PtraceBroker::SendReadError(ReadError error) { int32_t rv = -1; - if (!WriteFile(sock_, &rv, sizeof(rv))) { - return errno; - } - return SendError(err); + return WriteFile(sock_, &rv, sizeof(rv)) && + WriteFile(sock_, &error, sizeof(error)) + ? 0 + : errno; } int PtraceBroker::SendOpenResult(OpenResult result) { @@ -208,7 +249,7 @@ int PtraceBroker::SendFileContents(FileHandle handle) { rv = ReadFile(handle, buffer, sizeof(buffer)); if (rv < 0) { - return SendReadError(errno); + return SendReadError(static_cast(errno)); } if (!WriteFile(sock_, &rv, sizeof(rv))) { @@ -225,25 +266,59 @@ int PtraceBroker::SendFileContents(FileHandle handle) { return 0; } +void PtraceBroker::TryOpeningMemFile() { + if (tried_opening_mem_file_) { + return; + } + tried_opening_mem_file_ = true; + + if (memory_pid_ < 0) { + return; + } + + char mem_path[32]; + size_t root_length = strlen(file_root_buffer_); + static constexpr char kMem[] = "mem"; + + DCHECK_LT(root_length + strlen(kMem) + 1, sizeof(mem_path)); + memcpy(mem_path, file_root_buffer_, root_length); + // Include the trailing NUL. + memcpy(mem_path + root_length, kMem, strlen(kMem) + 1); + memory_file_.reset( + HANDLE_EINTR(open(mem_path, O_RDONLY | O_CLOEXEC | O_NOCTTY))); +} + int PtraceBroker::SendMemory(pid_t pid, VMAddress address, VMSize size) { + if (memory_pid_ >= 0 && pid != memory_pid_) { + return SendReadError(kReadErrorAccessDenied); + } + + TryOpeningMemFile(); + auto read_memory = [this, pid](VMAddress address, size_t size, char* buffer) { + return this->memory_file_.is_valid() + ? HANDLE_EINTR( + pread64(this->memory_file_.get(), buffer, size, address)) + : this->ptracer_.ReadUpTo(pid, address, size, buffer); + }; + char buffer[4096]; while (size > 0) { - VMSize bytes_read = std::min(size, VMSize{sizeof(buffer)}); + size_t to_read = std::min(size, VMSize{sizeof(buffer)}); - if (!ptracer_.ReadMemory(pid, address, bytes_read, buffer)) { - bytes_read = 0; - Errno error = errno; - if (!WriteFile(sock_, &bytes_read, sizeof(bytes_read)) || - !WriteFile(sock_, &error, sizeof(error))) { - return errno; - } - return 0; + int32_t bytes_read = read_memory(address, to_read, buffer); + + if (bytes_read < 0) { + return SendReadError(static_cast(errno)); } if (!WriteFile(sock_, &bytes_read, sizeof(bytes_read))) { return errno; } + if (bytes_read == 0) { + return 0; + } + if (!WriteFile(sock_, buffer, bytes_read)) { return errno; } diff --git a/util/linux/ptrace_broker.h b/util/linux/ptrace_broker.h index a8d0e952..2e5decdf 100644 --- a/util/linux/ptrace_broker.h +++ b/util/linux/ptrace_broker.h @@ -62,12 +62,9 @@ class PtraceBroker { kTypeGetThreadInfo, //! \brief Reads memory from the attached process. The data is returned in - //! a series of messages. Each message begins with a VMSize indicating - //! the number of bytes being returned in this message, followed by - //! the requested bytes. The broker continues to send messages until - //! either all of the requested memory has been sent or an error - //! occurs, in which case it sends a message containing a VMSize equal - //! to zero, followed by an Errno. + //! a series of messages. Each message begins with an int32_t + //! indicating the number of bytes read, 0 for end-of-file, or -1 for + //! errors, followed by a ReadError. On success the bytes read follow. kTypeReadMemory, //! \brief Read a file's contents. The data is returned in a series of @@ -98,7 +95,7 @@ class PtraceBroker { VMSize size; } iov; - // \brief Specifies the file path to read for a kTypeReadFile request. + //! \brief Specifies the file path to read for a kTypeReadFile request. struct { //! \brief The number of bytes in #path. The path should not include a //! `NUL`-terminator. @@ -124,6 +121,14 @@ class PtraceBroker { kOpenResultSuccess = 0, }; + //! \brief A result used in operations that read from memory or files. + //! + //! Positive values of this enum are reserved for sending errno values. + enum ReadError : int32_t { + //! \brief Access to this data is denied. + kReadErrorAccessDenied = -1, + }; + //! \brief The response sent for a Request with type kTypeGetThreadInfo. struct GetThreadInfoResponse { //! \brief Information about the specified thread. Only valid if #success @@ -139,9 +144,15 @@ class PtraceBroker { //! //! \param[in] sock A socket on which to read requests from a connected //! PtraceClient. Does not take ownership of the socket. + //! \param[in] pid The process ID of the process the broker is expected to + //! trace. Setting this value exends the default file root to + //! "/proc/[pid]/" and enables memory reading via /proc/[pid]/mem. The + //! broker will deny any requests to read memory from processes whose + //! processID is not \a pid. If pid is -1, the broker will serve requests + //! to read memory from any process it is able to via `ptrace PEEKDATA`. //! \param[in] is_64_bit Whether this broker should be configured to trace a //! 64-bit process. - PtraceBroker(int sock, bool is_64_bit); + PtraceBroker(int sock, pid_t pid, bool is_64_bit); ~PtraceBroker(); @@ -149,7 +160,10 @@ class PtraceBroker { //! root. //! //! If this method is not called, the broker defaults to only serving files - //! under "/proc/". + //! under "/proc/" or "/proc/[pid]/" if a pid was set. + //! + //! Calling this function disables reading from a memory file if one has not + //! already been opened. //! //! \param[in] root A NUL-terminated c-string containing the path to the new //! root. \a root must not be `nullptr`, must end in a '/', and the caller @@ -173,18 +187,23 @@ class PtraceBroker { void ReleaseAttachments(); int RunImpl(); int SendError(Errno err); - int SendReadError(Errno err); + int SendReadError(ReadError err); int SendOpenResult(OpenResult result); int SendFileContents(FileHandle handle); + void TryOpeningMemFile(); int SendMemory(pid_t pid, VMAddress address, VMSize size); int ReceiveAndOpenFilePath(VMSize path_length, ScopedFileHandle* handle); + char file_root_buffer_[32]; Ptracer ptracer_; const char* file_root_; ScopedPtraceAttach* attachments_; size_t attach_count_; size_t attach_capacity_; + ScopedFileHandle memory_file_; int sock_; + pid_t memory_pid_; + bool tried_opening_mem_file_; DISALLOW_COPY_AND_ASSIGN(PtraceBroker); }; diff --git a/util/linux/ptrace_broker_test.cc b/util/linux/ptrace_broker_test.cc index 65fef9d9..9ed1973b 100644 --- a/util/linux/ptrace_broker_test.cc +++ b/util/linux/ptrace_broker_test.cc @@ -127,6 +127,85 @@ class SameBitnessTest : public Multiprocess { } private: + void BrokerTests(bool set_broker_pid, + LinuxVMAddress child1_tls, + LinuxVMAddress child2_tls, + pid_t child2_tid, + const base::FilePath& file_dir, + const base::FilePath& test_file, + const std::string& expected_file_contents) { + int socks[2]; + ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, socks), 0); + ScopedFileHandle broker_sock(socks[0]); + ScopedFileHandle client_sock(socks[1]); + +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif // ARCH_CPU_64_BITS + + PtraceBroker broker( + broker_sock.get(), set_broker_pid ? ChildPID() : -1, am_64_bit); + RunBrokerThread broker_thread(&broker); + broker_thread.Start(); + + PtraceClient client; + ASSERT_TRUE(client.Initialize( + client_sock.get(), ChildPID(), /* try_direct_memory= */ false)); + + EXPECT_EQ(client.GetProcessID(), ChildPID()); + EXPECT_TRUE(client.Attach(child2_tid)); + EXPECT_EQ(client.Is64Bit(), am_64_bit); + + ThreadInfo info1; + ASSERT_TRUE(client.GetThreadInfo(ChildPID(), &info1)); + EXPECT_EQ(info1.thread_specific_data_address, child1_tls); + + ThreadInfo info2; + ASSERT_TRUE(client.GetThreadInfo(child2_tid, &info2)); + EXPECT_EQ(info2.thread_specific_data_address, child2_tls); + + ProcessMemory* memory = client.Memory(); + ASSERT_TRUE(memory); + + auto buffer = std::make_unique(mapping_.len()); + ASSERT_TRUE(memory->Read( + mapping_.addr_as(), mapping_.len(), buffer.get())); + auto expected_buffer = mapping_.addr_as(); + for (size_t index = 0; index < mapping_.len(); ++index) { + EXPECT_EQ(buffer[index], expected_buffer[index]); + } + + char first; + ASSERT_TRUE( + memory->Read(mapping_.addr_as(), sizeof(first), &first)); + EXPECT_EQ(first, expected_buffer[0]); + + char last; + ASSERT_TRUE(memory->Read(mapping_.addr_as() + mapping_.len() - 1, + sizeof(last), + &last)); + EXPECT_EQ(last, expected_buffer[mapping_.len() - 1]); + + char unmapped; + EXPECT_FALSE(memory->Read(mapping_.addr_as() + mapping_.len(), + sizeof(unmapped), + &unmapped)); + + std::string file_root = file_dir.value() + '/'; + broker.SetFileRoot(file_root.c_str()); + + std::string file_contents; + ASSERT_TRUE(client.ReadFileContents(test_file, &file_contents)); + EXPECT_EQ(file_contents, expected_file_contents); + + ScopedTempDir temp_dir2; + base::FilePath test_file2(temp_dir2.path().Append("test_file2")); + ASSERT_TRUE(CreateFile(test_file2)); + EXPECT_FALSE(client.ReadFileContents(test_file2, &file_contents)); + } + void MultiprocessParent() override { LinuxVMAddress child1_tls; ASSERT_TRUE(LoggingReadFileExactly( @@ -140,11 +219,6 @@ class SameBitnessTest : public Multiprocess { ASSERT_TRUE(LoggingReadFileExactly( ReadPipeHandle(), &child2_tls, sizeof(child2_tls))); - int socks[2]; - ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, socks), 0); - ScopedFileHandle broker_sock(socks[0]); - ScopedFileHandle client_sock(socks[1]); - ScopedTempDir temp_dir; base::FilePath file_path(temp_dir.path().Append("test_file")); std::string expected_file_contents; @@ -162,67 +236,20 @@ class SameBitnessTest : public Multiprocess { expected_file_contents.size())); } -#if defined(ARCH_CPU_64_BITS) - constexpr bool am_64_bit = true; -#else - constexpr bool am_64_bit = false; -#endif // ARCH_CPU_64_BITS - PtraceBroker broker(broker_sock.get(), am_64_bit); - RunBrokerThread broker_thread(&broker); - broker_thread.Start(); - - { - PtraceClient client; - ASSERT_TRUE(client.Initialize(client_sock.get(), ChildPID())); - - EXPECT_EQ(client.GetProcessID(), ChildPID()); - EXPECT_TRUE(client.Attach(child2_tid)); - EXPECT_EQ(client.Is64Bit(), am_64_bit); - - ThreadInfo info1; - ASSERT_TRUE(client.GetThreadInfo(ChildPID(), &info1)); - EXPECT_EQ(info1.thread_specific_data_address, child1_tls); - - ThreadInfo info2; - ASSERT_TRUE(client.GetThreadInfo(child2_tid, &info2)); - EXPECT_EQ(info2.thread_specific_data_address, child2_tls); - - auto buffer = std::make_unique(mapping_.len()); - ASSERT_TRUE(client.Read( - mapping_.addr_as(), mapping_.len(), buffer.get())); - auto expected_buffer = mapping_.addr_as(); - for (size_t index = 0; index < mapping_.len(); ++index) { - EXPECT_EQ(buffer[index], expected_buffer[index]); - } - - char first; - ASSERT_TRUE( - client.Read(mapping_.addr_as(), sizeof(first), &first)); - EXPECT_EQ(first, expected_buffer[0]); - - char last; - ASSERT_TRUE( - client.Read(mapping_.addr_as() + mapping_.len() - 1, - sizeof(last), - &last)); - EXPECT_EQ(last, expected_buffer[mapping_.len() - 1]); - - char unmapped; - EXPECT_FALSE(client.Read(mapping_.addr_as() + mapping_.len(), - sizeof(unmapped), - &unmapped)); - - std::string file_root = temp_dir.path().value() + '/'; - broker.SetFileRoot(file_root.c_str()); - std::string file_contents; - ASSERT_TRUE(client.ReadFileContents(file_path, &file_contents)); - EXPECT_EQ(file_contents, expected_file_contents); - - ScopedTempDir temp_dir2; - base::FilePath test_file2(temp_dir2.path().Append("test_file2")); - ASSERT_TRUE(CreateFile(test_file2)); - EXPECT_FALSE(client.ReadFileContents(test_file2, &file_contents)); - } + BrokerTests(true, + child1_tls, + child2_tls, + child2_tid, + temp_dir.path(), + file_path, + expected_file_contents); + BrokerTests(false, + child1_tls, + child2_tls, + child2_tid, + temp_dir.path(), + file_path, + expected_file_contents); } void MultiprocessChild() override { diff --git a/util/linux/ptrace_client.cc b/util/linux/ptrace_client.cc index ef880b2e..b5ccebaf 100644 --- a/util/linux/ptrace_client.cc +++ b/util/linux/ptrace_client.cc @@ -15,12 +15,14 @@ #include "util/linux/ptrace_client.h" #include +#include #include #include "base/logging.h" #include "util/file/file_io.h" #include "util/linux/ptrace_broker.h" +#include "util/process/process_memory_linux.h" namespace crashpad { @@ -36,6 +38,27 @@ bool ReceiveAndLogError(int sock, const std::string& operation) { return true; } +bool ReceiveAndLogReadError(int sock, const std::string& operation) { + PtraceBroker::ReadError err; + if (!LoggingReadFileExactly(sock, &err, sizeof(err))) { + return false; + } + switch (err) { + case PtraceBroker::kReadErrorAccessDenied: + LOG(ERROR) << operation << " access denied"; + return true; + default: + if (err <= 0) { + LOG(ERROR) << operation << " invalid error " << err; + DCHECK(false); + return false; + } + errno = err; + PLOG(ERROR) << operation; + return true; + } +} + bool AttachImpl(int sock, pid_t tid) { PtraceBroker::Request request; request.type = PtraceBroker::Request::kTypeAttach; @@ -61,6 +84,7 @@ bool AttachImpl(int sock, pid_t tid) { PtraceClient::PtraceClient() : PtraceConnection(), + memory_(), sock_(kInvalidFileHandle), pid_(-1), is_64_bit_(false), @@ -74,7 +98,7 @@ PtraceClient::~PtraceClient() { } } -bool PtraceClient::Initialize(int sock, pid_t pid) { +bool PtraceClient::Initialize(int sock, pid_t pid, bool try_direct_memory) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); sock_ = sock; pid_ = pid; @@ -97,46 +121,20 @@ bool PtraceClient::Initialize(int sock, pid_t pid) { } is_64_bit_ = is_64_bit == kBoolTrue; + if (try_direct_memory) { + auto direct_mem = std::make_unique(); + if (direct_mem->Initialize(pid)) { + memory_.reset(direct_mem.release()); + } + } + if (!memory_) { + memory_ = std::make_unique(this); + } + INITIALIZATION_STATE_SET_VALID(initialized_); return true; } -bool PtraceClient::Read(VMAddress address, size_t size, void* buffer) { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - char* buffer_c = reinterpret_cast(buffer); - - PtraceBroker::Request request; - request.type = PtraceBroker::Request::kTypeReadMemory; - request.tid = pid_; - request.iov.base = address; - request.iov.size = size; - - if (!LoggingWriteFile(sock_, &request, sizeof(request))) { - return false; - } - - while (size > 0) { - VMSize bytes_read; - if (!LoggingReadFileExactly(sock_, &bytes_read, sizeof(bytes_read))) { - return false; - } - - if (!bytes_read) { - ReceiveAndLogError(sock_, "PtraceBroker ReadMemory"); - return false; - } - - if (!LoggingReadFileExactly(sock_, buffer_c, bytes_read)) { - return false; - } - - size -= bytes_read; - buffer_c += bytes_read; - } - - return true; -} - pid_t PtraceClient::GetProcessID() { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return pid_; @@ -197,7 +195,7 @@ bool PtraceClient::ReadFileContents(const base::FilePath& path, } if (read_result < 0) { - ReceiveAndLogError(sock_, "ReadFileContents"); + ReceiveAndLogReadError(sock_, "ReadFileContents"); return false; } @@ -215,6 +213,66 @@ bool PtraceClient::ReadFileContents(const base::FilePath& path, return true; } +ProcessMemory* PtraceClient::Memory() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return memory_.get(); +} + +PtraceClient::BrokeredMemory::BrokeredMemory(PtraceClient* client) + : ProcessMemory(), client_(client) {} + +PtraceClient::BrokeredMemory::~BrokeredMemory() = default; + +ssize_t PtraceClient::BrokeredMemory::ReadUpTo(VMAddress address, + size_t size, + void* buffer) const { + return client_->ReadUpTo(address, size, buffer); +} + +ssize_t PtraceClient::ReadUpTo(VMAddress address, + size_t size, + void* buffer) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + char* buffer_c = reinterpret_cast(buffer); + + PtraceBroker::Request request; + request.type = PtraceBroker::Request::kTypeReadMemory; + request.tid = pid_; + request.iov.base = address; + request.iov.size = size; + + if (!LoggingWriteFile(sock_, &request, sizeof(request))) { + return false; + } + + ssize_t total_read = 0; + while (size > 0) { + int32_t bytes_read; + if (!LoggingReadFileExactly(sock_, &bytes_read, sizeof(bytes_read))) { + return -1; + } + + if (bytes_read < 0) { + ReceiveAndLogReadError(sock_, "PtraceBroker ReadMemory"); + return -1; + } + + if (bytes_read == 0) { + return total_read; + } + + if (!LoggingReadFileExactly(sock_, buffer_c, bytes_read)) { + return -1; + } + + size -= bytes_read; + buffer_c += bytes_read; + total_read += bytes_read; + } + + return total_read; +} + bool PtraceClient::SendFilePath(const char* path, size_t length) { if (!LoggingWriteFile(sock_, path, length)) { return false; diff --git a/util/linux/ptrace_client.h b/util/linux/ptrace_client.h index 8260412d..a7abc0cf 100644 --- a/util/linux/ptrace_client.h +++ b/util/linux/ptrace_client.h @@ -17,10 +17,13 @@ #include +#include + #include "base/macros.h" #include "util/linux/ptrace_connection.h" #include "util/misc/address_types.h" #include "util/misc/initialization_state_dcheck.h" +#include "util/process/process_memory.h" namespace crashpad { @@ -43,26 +46,11 @@ class PtraceClient : public PtraceConnection { //! ownership of the socket. //! \param[in] pid The process ID of the process to form a PtraceConnection //! with. + //! \param[in] try_direct_memory If `true` the client will attempt to support + //! memory reading operations by directly acessing the target process' + //! /proc/[pid]/mem file. //! \return `true` on success. `false` on failure with a message logged. - bool Initialize(int sock, pid_t pid); - - //! \brief Copies memory from the target process into a caller-provided buffer - //! in the current process. - //! - //! TODO(jperaza): In order for this to be usable, PtraceConnection will need - //! to surface it, possibly by inheriting from ProcessMemory, or providing a - //! method to return a ProcessMemory*. - //! - //! \param[in] address The address, in the target process' address space, of - //! the memory region to copy. - //! \param[in] size The size, in bytes, of the memory region to copy. - //! \a buffer must be at least this size. - //! \param[out] buffer The buffer into which the contents of the other - //! process' memory will be copied. - //! - //! \return `true` on success, with \a buffer filled appropriately. `false` on - //! failure, with a message logged. - bool Read(VMAddress address, size_t size, void* buffer); + bool Initialize(int sock, pid_t pid, bool try_direct_memory = true); // PtraceConnection: @@ -72,10 +60,28 @@ class PtraceClient : public PtraceConnection { bool GetThreadInfo(pid_t tid, ThreadInfo* info) override; bool ReadFileContents(const base::FilePath& path, std::string* contents) override; + ProcessMemory* Memory() override; private: + class BrokeredMemory : public ProcessMemory { + public: + explicit BrokeredMemory(PtraceClient* client); + ~BrokeredMemory(); + + ssize_t ReadUpTo(VMAddress address, + size_t size, + void* buffer) const override; + + private: + PtraceClient* client_; + + DISALLOW_COPY_AND_ASSIGN(BrokeredMemory); + }; + + ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const; bool SendFilePath(const char* path, size_t length); + std::unique_ptr memory_; int sock_; pid_t pid_; bool is_64_bit_; diff --git a/util/linux/ptrace_connection.h b/util/linux/ptrace_connection.h index e3111112..86dc2dd9 100644 --- a/util/linux/ptrace_connection.h +++ b/util/linux/ptrace_connection.h @@ -21,6 +21,7 @@ #include "base/files/file_path.h" #include "util/linux/thread_info.h" +#include "util/process/process_memory.h" namespace crashpad { @@ -57,6 +58,12 @@ class PtraceConnection { //! \return `true` on success. `false` on failure with a message logged. virtual bool ReadFileContents(const base::FilePath& path, std::string* contents) = 0; + + //! \brief Returns a memory reader for the connected process. + //! + //! The caller does not take ownership of the reader. The reader is valid for + //! the lifetime of the PtraceConnection that created it. + virtual ProcessMemory* Memory() = 0; }; } // namespace crashpad diff --git a/util/linux/ptracer.cc b/util/linux/ptracer.cc index 84447362..63bee1e0 100644 --- a/util/linux/ptracer.cc +++ b/util/linux/ptracer.cc @@ -375,10 +375,11 @@ bool Ptracer::GetThreadInfo(pid_t tid, ThreadInfo* info) { can_log_); } -bool Ptracer::ReadMemory(pid_t pid, - LinuxVMAddress address, - size_t size, - char* buffer) { +ssize_t Ptracer::ReadUpTo(pid_t pid, + LinuxVMAddress address, + size_t size, + char* buffer) { + size_t bytes_read = 0; while (size > 0) { errno = 0; @@ -386,46 +387,66 @@ bool Ptracer::ReadMemory(pid_t pid, *reinterpret_cast(buffer) = ptrace(PTRACE_PEEKDATA, pid, address, nullptr); + if (errno == EIO) { + ssize_t last_bytes = ReadLastBytes(pid, address, size, buffer); + return last_bytes >= 0 ? bytes_read + last_bytes : -1; + } + if (errno != 0) { PLOG_IF(ERROR, can_log_) << "ptrace"; - return false; + return -1; } size -= sizeof(long); buffer += sizeof(long); address += sizeof(long); + bytes_read += sizeof(long); } else { long word = ptrace(PTRACE_PEEKDATA, pid, address, nullptr); if (errno == 0) { memcpy(buffer, reinterpret_cast(&word), size); - return true; + return bytes_read + size; } - if (errno != EIO) { - PLOG_IF(ERROR, can_log_); - return false; - } - - // A read smaller than a word at the end of a mapping might spill over - // into unmapped memory. Try aligning the read so that the requested - // data is at the end of the word instead. - errno = 0; - word = - ptrace(PTRACE_PEEKDATA, pid, address - sizeof(word) + size, nullptr); - - if (errno == 0) { - memcpy( - buffer, reinterpret_cast(&word) + sizeof(word) - size, size); - return true; + if (errno == EIO) { + ssize_t last_bytes = ReadLastBytes(pid, address, size, buffer); + return last_bytes >= 0 ? bytes_read + last_bytes : -1; } PLOG_IF(ERROR, can_log_); - return false; + return -1; } } - return true; + return bytes_read; +} + +// Handles an EIO by reading at most size bytes from address into buffer if +// address was within a word of a possible page boundary, by aligning to read +// the last word of the page and extracting the desired bytes. +ssize_t Ptracer::ReadLastBytes(pid_t pid, + LinuxVMAddress address, + size_t size, + char* buffer) { + LinuxVMAddress aligned = ((address + 4095) & ~4095) - sizeof(long); + if (aligned >= address || aligned == address - sizeof(long)) { + PLOG_IF(ERROR, can_log_) << "ptrace"; + return -1; + } + DCHECK_GT(aligned, address - sizeof(long)); + + errno = 0; + long word = ptrace(PTRACE_PEEKDATA, pid, aligned, nullptr); + if (errno != 0) { + PLOG_IF(ERROR, can_log_) << "ptrace"; + return -1; + } + + size_t bytes_read = address - aligned; + size_t last_bytes = std::min(sizeof(long) - bytes_read, size); + memcpy(buffer, reinterpret_cast(&word) + bytes_read, last_bytes); + return last_bytes; } } // namespace crashpad diff --git a/util/linux/ptracer.h b/util/linux/ptracer.h index daa7a01f..33eeb3ae 100644 --- a/util/linux/ptracer.h +++ b/util/linux/ptracer.h @@ -72,20 +72,29 @@ class Ptracer { bool GetThreadInfo(pid_t tid, ThreadInfo* info); //! \brief Uses `ptrace` to read memory from the process with process ID \a - //! pid. + //! pid, up to a maximum number of bytes. //! //! The target process should already be attached before calling this method. //! \see ScopedPtraceAttach //! //! \param[in] pid The process ID whose memory to read. //! \param[in] address The base address of the region to read. - //! \param[in] size The size of the memory region to read. + //! \param[in] size The size of the memory region to read. \a buffer must be + //! at least this size. //! \param[out] buffer The buffer to fill with the data read. - //! \return `true` on success. `false` on failure with a message logged, if - //! enabled. - bool ReadMemory(pid_t pid, LinuxVMAddress address, size_t size, char* buffer); + //! \return the number of bytes read, 0 if there are no more bytes to read, or + //! -1 on failure with a message logged if logging is enabled. + ssize_t ReadUpTo(pid_t pid, + LinuxVMAddress address, + size_t size, + char* buffer); private: + ssize_t ReadLastBytes(pid_t pid, + LinuxVMAddress address, + size_t size, + char* buffer); + bool is_64_bit_; bool can_log_; InitializationStateDcheck initialized_; diff --git a/util/process/process_memory.h b/util/process/process_memory.h index 34561718..d67c3828 100644 --- a/util/process/process_memory.h +++ b/util/process/process_memory.h @@ -78,9 +78,10 @@ class ProcessMemory { return ReadCStringInternal(address, true, size, string); } + virtual ~ProcessMemory() = default; + protected: ProcessMemory() = default; - ~ProcessMemory() = default; private: //! \brief Copies memory from the target process into a caller-provided buffer From 3a20d34ac356ba4dbfffff5c77433695cecb16e3 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Thu, 5 Apr 2018 18:10:48 -0700 Subject: [PATCH 233/326] Fix build with glibc 2.27 Glibc now defines PTRACE_GET_THREAD_AREA as an enum value. Trying to define our own will result in an error: ../../third_party/crashpad/crashpad/compat/linux/sys/ptrace.h:25:35: error: redefinition of 'PTRACE_GET_THREAD_AREA' as different kind of symbol static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = ^ ../../build/linux/debian_sid_amd64-sysroot/usr/include/x86_64-linux-gnu/sys/ptrace.h:110:3: note: previous definition is here PTRACE_GET_THREAD_AREA = 25, However, glibc also defines a new macro for the corresponding value, so it's possible to detect this case: ----- ptrace.h ----- /* Get a TLS entry in the GDT. */ PTRACE_GET_THREAD_AREA = 25, #define PT_GET_THREAD_AREA PTRACE_GET_THREAD_AREA ----- ptrace.h ----- This CL prevents defining our own PTRACE_GET_THREAD_AREA when PT_GET_THREAD_AREA is defined. Bug: None Change-Id: Idf931e54dadd57788f04da47f12f0f0588a255cc Reviewed-on: https://chromium-review.googlesource.com/999161 Commit-Queue: Mark Mentovai Reviewed-by: Mark Mentovai --- compat/linux/sys/ptrace.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compat/linux/sys/ptrace.h b/compat/linux/sys/ptrace.h index 07806b08..73861576 100644 --- a/compat/linux/sys/ptrace.h +++ b/compat/linux/sys/ptrace.h @@ -20,7 +20,8 @@ #include // https://sourceware.org/bugzilla/show_bug.cgi?id=22433 -#if !defined(PTRACE_GET_THREAD_AREA) && defined(__GLIBC__) +#if !defined(PTRACE_GET_THREAD_AREA) && !defined(PT_GET_THREAD_AREA) && \ + defined(__GLIBC__) #if defined(__i386__) || defined(__x86_64__) static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = static_cast<__ptrace_request>(25); @@ -30,7 +31,7 @@ static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = static_cast<__ptrace_request>(22); #define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA #endif -#endif // !PTRACE_GET_THREAD_AREA && defined(__GLIBC__) +#endif // !PTRACE_GET_THREAD_AREA && !PT_GET_THREAD_AREA && defined(__GLIBC__) // https://sourceware.org/bugzilla/show_bug.cgi?id=22433 #if !defined(PTRACE_GETVFPREGS) && \ From 1bb4c233e3415965d5ff8e738a43275f2a20f76a Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 5 Apr 2018 18:40:53 -0700 Subject: [PATCH 234/326] linux: skip zero length mappings in the maps file A zero-length mapping was observed for a x86 process running on an x86_64 Android Lollipop (5.0) simulator: ff3c0000-ff3c0000 ---p 00000000 00:00 0 ff3c0000-ffbbf000 rw-p 00000000 00:00 0 [stack] ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso] Bug: crashpad:30 Change-Id: I1c1cb5a0910ddf3f02a93d44803e17bec4071110 Reviewed-on: https://chromium-review.googlesource.com/999112 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- util/linux/memory_map.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/util/linux/memory_map.cc b/util/linux/memory_map.cc index a8da66c9..a274790e 100644 --- a/util/linux/memory_map.cc +++ b/util/linux/memory_map.cc @@ -102,10 +102,20 @@ ParseResult ParseMapsLine(DelimitedFileReader* maps_file_reader, LOG(ERROR) << "format error"; return ParseResult::kError; } - if (end_address <= start_address) { + if (end_address < start_address) { LOG(ERROR) << "format error"; return ParseResult::kError; } + // Skip zero-length mappings. + if (end_address == start_address) { + std::string rest_of_line; + if (maps_file_reader->GetLine(&rest_of_line) != + DelimitedFileReader::Result::kSuccess) { + LOG(ERROR) << "format error"; + return ParseResult::kError; + } + return ParseResult::kSuccess; + } // TODO(jperaza): set bitness properly #if defined(ARCH_CPU_64_BITS) From a3ba96c0d4c780ab7f19ade6121266916712baaf Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 5 Apr 2018 19:09:44 -0700 Subject: [PATCH 235/326] linux: Use an empty string for unmapped module names This may be a bug in the target program or loader, but doesn't seem like something worth dying over. If a link_entry name is empty, ProcessReaderLinux::InitializeModules() will fall back to using the name of the module's mapping. In this case, the main executable's link entry name pointed into unmapped memory, but the memory map was able to identify it as app_process32. Bug: crashpad:30 Change-Id: Ic6df08132271efb809bf0bc28f23a333deb20a67 Reviewed-on: https://chromium-review.googlesource.com/999301 Commit-Queue: Joshua Peraza Reviewed-by: Mark Mentovai --- snapshot/linux/debug_rendezvous.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapshot/linux/debug_rendezvous.cc b/snapshot/linux/debug_rendezvous.cc index e47fc0c6..e27768dd 100644 --- a/snapshot/linux/debug_rendezvous.cc +++ b/snapshot/linux/debug_rendezvous.cc @@ -63,7 +63,7 @@ bool ReadLinkEntry(const ProcessMemoryRange& memory, std::string name; if (!memory.ReadCStringSizeLimited(entry.l_name, 4096, &name)) { - return false; + name.clear(); } entry_out->load_bias = entry.l_addr; From 914e7f76dd26de8e74caac5d05750beee9b48029 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Mon, 9 Apr 2018 11:50:13 -0700 Subject: [PATCH 236/326] linux, x86: Read floating point state via mcontext.fpptr Floating-point content may not begin at the start of __fpregs_mem and should be located via mcontext.fpptr, which may be `nullptr`. Bug: crashpad:30 Change-Id: Ie3116339d79f6669d757618e9e592f8480dcdcba Reviewed-on: https://chromium-review.googlesource.com/1001332 Reviewed-by: Scott Graham Commit-Queue: Joshua Peraza --- snapshot/linux/cpu_context_linux.cc | 34 +++++++++-- snapshot/linux/cpu_context_linux.h | 25 ++++---- snapshot/linux/exception_snapshot_linux.cc | 59 ++++++++++++------- .../linux/exception_snapshot_linux_test.cc | 2 + snapshot/linux/signal_context.h | 2 +- 5 files changed, 83 insertions(+), 39 deletions(-) diff --git a/snapshot/linux/cpu_context_linux.cc b/snapshot/linux/cpu_context_linux.cc index 1a6589f8..ebf9d7e9 100644 --- a/snapshot/linux/cpu_context_linux.cc +++ b/snapshot/linux/cpu_context_linux.cc @@ -77,6 +77,8 @@ void InitializeCPUContextX86_NoFloatingPoint( CPUContextX86* context) { SET_GPRS32(); + memset(&context->fxsave, 0, sizeof(context->fxsave)); + context->dr0 = 0; context->dr1 = 0; context->dr2 = 0; @@ -152,6 +154,23 @@ void InitializeCPUContextX86_64(const SignalThreadContext64& thread_context, context->dr7 = 0; } +void InitializeCPUContextX86_64_NoFloatingPoint( + const SignalThreadContext64& thread_context, + CPUContextX86_64* context) { + SET_GPRS64(); + + memset(&context->fxsave, 0, sizeof(context->fxsave)); + + context->dr0 = 0; + context->dr1 = 0; + context->dr2 = 0; + context->dr3 = 0; + context->dr4 = 0; + context->dr5 = 0; + context->dr6 = 0; + context->dr7 = 0; +} + #elif defined(ARCH_CPU_ARM_FAMILY) void InitializeCPUContextARM(const ThreadContext::t32_t& thread_context, @@ -195,6 +214,11 @@ void InitializeCPUContextARM_NoFloatingPoint( context->lr = thread_context.lr; context->pc = thread_context.pc; context->cpsr = thread_context.cpsr; + + memset(&context->fpa_regs, 0, sizeof(context->fpa_regs)); + memset(&context->vfp_regs, 0, sizeof(context->vfp_regs)); + context->have_fpa_regs = false; + context->have_vfp_regs = false; } void InitializeCPUContextARM64(const ThreadContext::t64_t& thread_context, @@ -218,6 +242,10 @@ void InitializeCPUContextARM64_NoFloatingPoint( context->sp = thread_context.sp; context->pc = thread_context.pc; context->pstate = thread_context.pstate; + + memset(&context->fpsimd, 0, sizeof(context->fpsimd)); + context->fpsr = 0; + context->fpcr = 0; } void InitializeCPUContextARM64_OnlyFPSIMD( @@ -230,12 +258,6 @@ void InitializeCPUContextARM64_OnlyFPSIMD( context->fpcr = float_context.fpcr; } -void InitializeCPUContextARM64_ClearFPSIMD(CPUContextARM64* context) { - memset(context->fpsimd, 0, sizeof(context->fpsimd)); - context->fpsr = 0; - context->fpcr = 0; -} - #endif // ARCH_CPU_X86_FAMILY } // namespace internal diff --git a/snapshot/linux/cpu_context_linux.h b/snapshot/linux/cpu_context_linux.h index 1ec4a1b4..22803763 100644 --- a/snapshot/linux/cpu_context_linux.h +++ b/snapshot/linux/cpu_context_linux.h @@ -44,8 +44,7 @@ void InitializeCPUContextX86(const SignalThreadContext32& thread_context, //! \brief Initializes GPR and debug state in a CPUContextX86 from a native //! signal context structure on Linux. //! -//! Floating point state is not initialized. Debug registers are initialized to -//! zero. +//! Floating point state and debug registers are initialized to zero. //! //! \param[in] thread_context The native thread context. //! \param[out] context The CPUContextX86 structure to initialize. @@ -69,6 +68,17 @@ void InitializeCPUContextX86_64(const SignalThreadContext64& thread_context, CPUContextX86_64* context); //! \} +//! \brief Initializes GPR and debug state in a CPUContextX86_64 from a native +//! signal context structure on Linux. +//! +//! Floating point state and debug registers are initialized to zero. +//! +//! \param[in] thread_context The native thread context. +//! \param[out] context The CPUContextX86_64 structure to initialize. +void InitializeCPUContextX86_64_NoFloatingPoint( + const SignalThreadContext64& thread_context, + CPUContextX86_64* context); + #endif // ARCH_CPU_X86_FAMILY || DOXYGEN #if defined(ARCH_CPU_ARM_FAMILY) || DOXYGEN @@ -86,7 +96,7 @@ void InitializeCPUContextARM(const ThreadContext::t32_t& thread_context, //! \brief Initializes GPR state in a CPUContextARM from a native signal context //! structure on Linux. //! -//! Floating point state is not initialized. +//! Floating point state is initialized to zero. //! //! \param[in] thread_context The native thread context. //! \param[out] context The CPUContextARM structure to initialize. @@ -107,7 +117,7 @@ void InitializeCPUContextARM64(const ThreadContext::t64_t& thread_context, //! \brief Initializes GPR state in a CPUContextARM64 from a native context //! structure on Linux. //! -//! Floating point state is not initialized. +//! Floating point state is initialized to zero. //! //! \param[in] thread_context The native thread context. //! \param[out] context The CPUContextARM64 structure to initialize. @@ -126,13 +136,6 @@ void InitializeCPUContextARM64_OnlyFPSIMD( const SignalFPSIMDContext& float_context, CPUContextARM64* context); -//! \brief Initializes FPSIMD state in a CPUContextARM64 to zero. -//! -//! General purpose registers are not initialized. -//! -//! \param[out] context The CPUContextARM64 structure to initialize. -void InitializeCPUContextARM64_ClearFPSIMD(CPUContextARM64* context); - #endif // ARCH_CPU_ARM_FAMILY || DOXYGEN } // namespace internal diff --git a/snapshot/linux/exception_snapshot_linux.cc b/snapshot/linux/exception_snapshot_linux.cc index fa2e8f94..6e2a9275 100644 --- a/snapshot/linux/exception_snapshot_linux.cc +++ b/snapshot/linux/exception_snapshot_linux.cc @@ -41,6 +41,7 @@ ExceptionSnapshotLinux::ExceptionSnapshotLinux() ExceptionSnapshotLinux::~ExceptionSnapshotLinux() {} #if defined(ARCH_CPU_X86_FAMILY) + template <> bool ExceptionSnapshotLinux::ReadContext( ProcessReaderLinux* reader, @@ -54,26 +55,35 @@ bool ExceptionSnapshotLinux::ReadContext( context_.architecture = kCPUArchitectureX86; context_.x86 = &context_union_.x86; - if (ucontext.fprs.magic == X86_FXSR_MAGIC) { - if (!reader->Memory()->Read(context_address + - offsetof(UContext, fprs) + - offsetof(SignalFloatContext32, fxsave), - sizeof(CPUContextX86::Fxsave), - &context_.x86->fxsave)) { + if (!ucontext.mcontext.fpptr) { + InitializeCPUContextX86_NoFloatingPoint(ucontext.mcontext.gprs, + context_.x86); + return true; + } + + SignalFloatContext32 fprs; + if (!reader->Memory()->Read(ucontext.mcontext.fpptr, sizeof(fprs), &fprs)) { + LOG(ERROR) << "Couldn't read float context"; + return false; + } + + if (fprs.magic == X86_FXSR_MAGIC) { + InitializeCPUContextX86_NoFloatingPoint(ucontext.mcontext.gprs, + context_.x86); + if (!reader->Memory()->Read( + ucontext.mcontext.fpptr + offsetof(SignalFloatContext32, fxsave), + sizeof(CPUContextX86::Fxsave), + &context_.x86->fxsave)) { LOG(ERROR) << "Couldn't read fxsave"; return false; } - InitializeCPUContextX86_NoFloatingPoint(ucontext.mcontext.gprs, - context_.x86); - + } else if (fprs.magic == 0xffff) { + InitializeCPUContextX86(ucontext.mcontext.gprs, fprs, context_.x86); } else { - if (ucontext.fprs.magic != 0xffff) { - LOG(ERROR) << "unexpected magic 0x" << std::hex << ucontext.fprs.magic; - return false; - } - InitializeCPUContextX86( - ucontext.mcontext.gprs, ucontext.fprs, context_.x86); + LOG(ERROR) << "unexpected magic 0x" << std::hex << fprs.magic; + return false; } + return true; } @@ -90,8 +100,19 @@ bool ExceptionSnapshotLinux::ReadContext( context_.architecture = kCPUArchitectureX86_64; context_.x86_64 = &context_union_.x86_64; - InitializeCPUContextX86_64( - ucontext.mcontext.gprs, ucontext.fprs, context_.x86_64); + if (!ucontext.mcontext.fpptr) { + InitializeCPUContextX86_64_NoFloatingPoint(ucontext.mcontext.gprs, + context_.x86_64); + return true; + } + + SignalFloatContext64 fprs; + if (!reader->Memory()->Read(ucontext.mcontext.fpptr, sizeof(fprs), &fprs)) { + LOG(ERROR) << "Couldn't read float context"; + return false; + } + + InitializeCPUContextX86_64(ucontext.mcontext.gprs, fprs, context_.x86_64); return true; } @@ -118,8 +139,6 @@ bool ExceptionSnapshotLinux::ReadContext( } InitializeCPUContextARM_NoFloatingPoint(thread_context, dest_context); - dest_context->have_fpa_regs = false; - LinuxVMAddress reserved_address = context_address + offsetof(UContext, reserved); if ((reserved_address & 7) != 0) { @@ -134,7 +153,6 @@ bool ExceptionSnapshotLinux::ReadContext( return false; } - dest_context->have_vfp_regs = false; do { CoprocessorContextHead head; if (!range.Read(reserved_address, sizeof(head), &head)) { @@ -241,7 +259,6 @@ bool ExceptionSnapshotLinux::ReadContext( case 0: LOG(WARNING) << "fpsimd not found"; - InitializeCPUContextARM64_ClearFPSIMD(dest_context); return true; default: diff --git a/snapshot/linux/exception_snapshot_linux_test.cc b/snapshot/linux/exception_snapshot_linux_test.cc index c53d69e9..5a0c6ae1 100644 --- a/snapshot/linux/exception_snapshot_linux_test.cc +++ b/snapshot/linux/exception_snapshot_linux_test.cc @@ -54,6 +54,7 @@ using NativeCPUContext = FxsaveUContext; void InitializeContext(NativeCPUContext* context) { context->ucontext.uc_mcontext.gregs[REG_EAX] = 0xabcd1234; + context->ucontext.uc_mcontext.fpregs = &context->ucontext.__fpregs_mem; // glibc and bionic use an unsigned long for status, but the kernel treats // status as two uint16_t, with the upper 16 bits called "magic" which, if set // to X86_FXSR_MAGIC, indicate that an fxsave follows. @@ -78,6 +79,7 @@ using NativeCPUContext = ucontext_t; void InitializeContext(NativeCPUContext* context) { context->uc_mcontext.gregs[REG_RAX] = 0xabcd1234abcd1234; + context->uc_mcontext.fpregs = &context->__fpregs_mem; memset(&context->__fpregs_mem, 44, sizeof(context->__fpregs_mem)); } diff --git a/snapshot/linux/signal_context.h b/snapshot/linux/signal_context.h index 0476fdfd..f9d2cb9d 100644 --- a/snapshot/linux/signal_context.h +++ b/snapshot/linux/signal_context.h @@ -206,7 +206,7 @@ struct UContext { SignalStack stack; MContext mcontext; Sigset sigmask; - typename Traits::FloatContext fprs; + char fpregs_mem[0]; }; #elif defined(ARCH_CPU_ARM_FAMILY) From e83103c80503ba07f37258f6f5e39c1afb331c98 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 6 Apr 2018 15:13:03 -0700 Subject: [PATCH 237/326] fuchsia: Implement CaptureContext() for x64 Fuchsia enables safe-stack by default in the compiler. Disable it for the test function so that a candidate RSP value can be found by using the value of locals on the stack. (This also reduces the function prolog size sufficiently for the PC comparison to work, otherwise it required 75 bytes for the delta comparison.) Bug: crashpad:196 Change-Id: I2adbcee93c90dbc415309b79e3d16e9c4635f989 Reviewed-on: https://chromium-review.googlesource.com/1000140 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- util/BUILD.gn | 14 +- util/misc/capture_context.h | 20 ++- util/misc/capture_context_fuchsia.S | 133 ++++++++++++++++++ util/misc/capture_context_test.cc | 9 ++ .../misc/capture_context_test_util_fuchsia.cc | 59 ++++++++ 5 files changed, 215 insertions(+), 20 deletions(-) create mode 100644 util/misc/capture_context_fuchsia.S create mode 100644 util/misc/capture_context_test_util_fuchsia.cc diff --git a/util/BUILD.gn b/util/BUILD.gn index e8f2dc08..5649aec1 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -388,6 +388,7 @@ static_library("util") { if (crashpad_is_fuchsia) { sources += [ + "misc/capture_context_fuchsia.S", "misc/paths_fuchsia.cc", "net/http_transport_none.cc", "process/process_memory_fuchsia.cc", @@ -454,6 +455,7 @@ source_set("util_test") { "file/filesystem_test.cc", "file/string_file_test.cc", "misc/arraysize_unsafe_test.cc", + "misc/capture_context_test.cc", "misc/capture_context_test_util.h", "misc/clock_test.cc", "misc/from_pointer_cast_test.cc", @@ -488,14 +490,6 @@ source_set("util_test") { "thread/worker_thread_test.cc", ] - if (!crashpad_is_fuchsia) { - sources += [ - # No NativeCPUContext defined for Fuchsia yet. - # https://crashpad.chromium.org/bug/196. - "misc/capture_context_test.cc", - ] - } - if (!crashpad_is_android && !crashpad_is_fuchsia && (!crashpad_is_linux || crashpad_enable_http_transport_libcurl)) { # Android and Fuchsia will each require an HTTPTransport implementation @@ -561,6 +555,10 @@ source_set("util_test") { ] } + if (crashpad_is_fuchsia) { + sources += [ "misc/capture_context_test_util_fuchsia.cc" ] + } + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { sources += [ # TODO: Port to all platforms. diff --git a/util/misc/capture_context.h b/util/misc/capture_context.h index 5c1838a1..73b80580 100644 --- a/util/misc/capture_context.h +++ b/util/misc/capture_context.h @@ -23,6 +23,8 @@ #include #elif defined(OS_LINUX) || defined(OS_ANDROID) #include +#elif defined(OS_FUCHSIA) +#include #endif // OS_MACOSX namespace crashpad { @@ -33,14 +35,10 @@ using NativeCPUContext = x86_thread_state; #endif #elif defined(OS_WIN) using NativeCPUContext = CONTEXT; -#elif defined(OS_LINUX) || defined(OS_ANDROID) +#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) using NativeCPUContext = ucontext_t; #endif // OS_MACOSX -// No NativeCPUContext defined for Fuchsia yet. -// https://crashpad.chromium.org/bug/196. -#if !defined(OS_FUCHSIA) - //! \brief Saves the CPU context. //! //! The CPU context will be captured as accurately and completely as possible, @@ -62,11 +60,11 @@ using NativeCPUContext = ucontext_t; //! register, preventing this fuction from saving the original value of that //! register. This occurs in the following circumstances: //! -//! OS | Architecture | Register -//! ------------|--------------|--------- -//! Win | x86_64 | `%%rcx` -//! macOS/Linux | x86_64 | `%%rdi` -//! Linux | ARM/ARM64 | `r0`/`x0` +//! OS | Architecture | Register +//! --------------------|--------------|--------- +//! Win | x86_64 | `%%rcx` +//! macOS/Linux/Fuchsia | x86_64 | `%%rdi` +//! Linux | ARM/ARM64 | `r0`/`x0` //! //! Additionally, the value `LR` on ARM/ARM64 will be the return address of //! this function. @@ -80,8 +78,6 @@ using NativeCPUContext = ucontext_t; //! \endcode void CaptureContext(NativeCPUContext* cpu_context); -#endif // !OS_FUCHSIA - } // namespace crashpad #endif // CRASHPAD_UTIL_MISC_CAPTURE_CONTEXT_H_ diff --git a/util/misc/capture_context_fuchsia.S b/util/misc/capture_context_fuchsia.S new file mode 100644 index 00000000..c8436ffc --- /dev/null +++ b/util/misc/capture_context_fuchsia.S @@ -0,0 +1,133 @@ +// Copyright 2018 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. + +// namespace crashpad { +// void CaptureContext(ucontext_t* context); +// } // namespace crashpad + +#define CAPTURECONTEXT_SYMBOL _ZN8crashpad14CaptureContextEP8ucontext + + .text + .globl CAPTURECONTEXT_SYMBOL +#if defined(__x86_64__) + .balign 16, 0x90 +#elif defined(__aarch64__) + .balign 4, 0x0 +#endif + +CAPTURECONTEXT_SYMBOL: + +#if defined(__x86_64__) + + .cfi_startproc + + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + .cfi_def_cfa_register %rbp + + // Note that 16-byte stack alignment is not maintained because this function + // does not call out to any other. + + // pushfq first, because some instructions (but probably none used here) + // affect %rflags. %rflags will be in -8(%rbp). + pushfq + + // General-purpose registers whose values haven’t changed can be captured + // directly. + movq %r8, 0x28(%rdi) // context->uc_mcontext.r8 + movq %r9, 0x30(%rdi) // context->uc_mcontext.r9 + movq %r10, 0x38(%rdi) // context->uc_mcontext.r10 + movq %r11, 0x40(%rdi) // context->uc_mcontext.r11 + movq %r12, 0x48(%rdi) // context->uc_mcontext.r12 + movq %r13, 0x50(%rdi) // context->uc_mcontext.r13 + movq %r14, 0x58(%rdi) // context->uc_mcontext.r14 + movq %r15, 0x60(%rdi) // context->uc_mcontext.r15 + + // Because of the calling convention, there’s no way to recover the value of + // the caller’s %rdi as it existed prior to calling this function. This + // function captures a snapshot of the register state at its return, which + // involves %rdi containing a pointer to its first argument. Callers that + // require the value of %rdi prior to calling this function should obtain it + // separately. For example: + // uint64_t rdi; + // asm("movq %%rdi, %0" : "=m"(rdi)); + movq %rdi, 0x68(%rdi) // context->uc_mcontext.rdi + + movq %rsi, 0x70(%rdi) // context->uc_mcontext.rsi + + // Use %r8 as a scratch register now that it has been saved. + // The original %rbp was saved on the stack in this function’s prologue. + movq (%rbp), %r8 + movq %r8, 0x78(%rdi) // context->uc_mcontext.rbp + + // Save the remaining general-purpose registers. + movq %rbx, 0x80(%rdi) // context->uc_mcontext.rbx + movq %rdx, 0x88(%rdi) // context->uc_mcontext.rdx + movq %rax, 0x90(%rdi) // context->uc_mcontext.rax + movq %rcx, 0x98(%rdi) // context->uc_mcontext.rcx + + // %rsp was saved in %rbp in this function’s prologue, but the caller’s %rsp + // is 16 more than this value: 8 for the original %rbp saved on the stack in + // this function’s prologue, and 8 for the return address saved on the stack + // by the call instruction that reached this function. + leaq 16(%rbp), %r8 + movq %r8, 0xa0(%rdi) // context->uc_mcontext.rsp + + // The return address saved on the stack used by the call of this function is + // likely more useful than the current RIP here. + movq 8(%rbp), %r8 + movq %r8, 0xa8(%rdi) // context->uc_mcontext.rip + + // The original %rflags was saved on the stack above. + movq -8(%rbp), %r8 + movq %r8, 0xb0(%rdi) // context->uc_mcontext.eflags + + // Save the segment registers + movw %cs, 0xb8(%rdi) // context->uc_mcontext.cs + movw %gs, 0xba(%rdi) // context->uc_mcontext.gs + movw %fs, 0xbc(%rdi) // context->uc_mcontext.fs + + xorw %ax, %ax + movw %ax, 0xbe(%rdi) // context->uc_mcontext.padding + + // Zero out the remainder of the unused pseudo-registers + xorq %r8, %r8 + movq %r8, 0xc0(%rdi) // context->uc_mcontext.err + movq %r8, 0xc8(%rdi) // context->uc_mcontext.trapno + movq %r8, 0xd0(%rdi) // context->uc_mcontext.oldmask + movq %r8, 0xd8(%rdi) // context->uc_mcontext.cr2 + + // Clean up by restoring clobbered registers, even those considered volatile + // by the ABI, so that the captured context represents the state at this + // function’s exit. + movq 0x90(%rdi), %rax + movq 0x28(%rdi), %r8 + + // TODO(scottmg): save floating-point registers. + + popfq + + popq %rbp + + ret + + .cfi_endproc + +#elif defined(__aarch64__) + + #error TODO implement + +#endif // __x86_64__ diff --git a/util/misc/capture_context_test.cc b/util/misc/capture_context_test.cc index e31883e7..6ce81002 100644 --- a/util/misc/capture_context_test.cc +++ b/util/misc/capture_context_test.cc @@ -26,6 +26,15 @@ namespace crashpad { namespace test { namespace { +#if defined(OS_FUCHSIA) +// Fuchsia uses -fsanitize=safe-stack by default, which splits local variables +// and the call stack into separate regions (see +// https://clang.llvm.org/docs/SafeStack.html). Because this test would like to +// find an approximately valid stack pointer by comparing locals to the +// captured one, disable safe-stack for this function. +__attribute__((no_sanitize("safe-stack"))) +#endif + void TestCaptureContext() { NativeCPUContext context_1; CaptureContext(&context_1); diff --git a/util/misc/capture_context_test_util_fuchsia.cc b/util/misc/capture_context_test_util_fuchsia.cc new file mode 100644 index 00000000..7f9210ed --- /dev/null +++ b/util/misc/capture_context_test_util_fuchsia.cc @@ -0,0 +1,59 @@ +// Copyright 2018 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 "util/misc/capture_context_test_util.h" + +#include "base/logging.h" +#include "gtest/gtest.h" +#include "util/misc/from_pointer_cast.h" + +namespace crashpad { +namespace test { + +#if defined(ARCH_CPU_X86_64) +static_assert(offsetof(NativeCPUContext, uc_mcontext) == 0x28, + "unexpected mcontext offset"); +static_assert(offsetof(NativeCPUContext, uc_mcontext.gregs[REG_RSP]) == 0xa0, + "unexpected rsp offset"); +static_assert(offsetof(NativeCPUContext, uc_mcontext.gregs[REG_RIP]) == 0xa8, + "unexpected rip offset"); +#endif // ARCH_CPU_X86_64 + +void SanityCheckContext(const NativeCPUContext& context) { +#if defined(ARCH_CPU_X86_64) + EXPECT_EQ(context.uc_mcontext.gregs[REG_RDI], + FromPointerCast(&context)); +#elif defined(ARCH_CPU_ARM64) + EXPECT_EQ(context.uc_mcontext.regs[0], FromPointerCast(&context)); +#endif +} + +uintptr_t ProgramCounterFromContext(const NativeCPUContext& context) { +#if defined(ARCH_CPU_X86_64) + return context.uc_mcontext.gregs[REG_RIP]; +#elif defined(ARCH_CPU_ARM64) + return context.uc_mcontext.pc; +#endif +} + +uintptr_t StackPointerFromContext(const NativeCPUContext& context) { +#if defined(ARCH_CPU_X86_64) + return context.uc_mcontext.gregs[REG_RSP]; +#elif defined(ARCH_CPU_ARM64) + return context.uc_mcontext.sp; +#endif +} + +} // namespace test +} // namespace crashpad From b08165c9e52aef35a6428982d61e37ddc851c3a2 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 9 Apr 2018 14:21:06 -0700 Subject: [PATCH 238/326] Extract BuildHandlerArgvStrings to common shared location I plan to have Fuchsia use a "StartHandlerAtCrash" style similar to Linux, so pull the argv preservation out into a location where it can be shared between crashpad_client_linux.cc and crashpad_client_fuchsia.cc (in upcoming sets). Bug: crashpad:196 Change-Id: Ie305556579d9ac2c97b205ecf63cadf069228811 Reviewed-on: https://chromium-review.googlesource.com/1002860 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- client/BUILD.gn | 10 +++-- client/client_argv_handling.cc | 74 +++++++++++++++++++++++++++++++++ client/client_argv_handling.h | 53 +++++++++++++++++++++++ client/crashpad_client_linux.cc | 50 +--------------------- 4 files changed, 134 insertions(+), 53 deletions(-) create mode 100644 client/client_argv_handling.cc create mode 100644 client/client_argv_handling.h diff --git a/client/BUILD.gn b/client/BUILD.gn index ddd521f2..9d8b736f 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -52,7 +52,11 @@ static_library("client") { } if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { - sources += [ "crashpad_info_note.S" ] + sources += [ + "client_argv_handling.cc", + "client_argv_handling.h", + "crashpad_info_note.S", + ] } if (crashpad_is_win) { @@ -64,9 +68,7 @@ static_library("client") { } if (crashpad_is_fuchsia) { - sources += [ - "crashpad_client_fuchsia.cc", - ] + sources += [ "crashpad_client_fuchsia.cc" ] } if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { diff --git a/client/client_argv_handling.cc b/client/client_argv_handling.cc new file mode 100644 index 00000000..9b813b1c --- /dev/null +++ b/client/client_argv_handling.cc @@ -0,0 +1,74 @@ +// Copyright 2018 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 "client/client_argv_handling.h" + +#include "base/strings/stringprintf.h" + +namespace crashpad { + +namespace { + +std::string FormatArgumentString(const std::string& name, + const std::string& value) { + return base::StringPrintf("--%s=%s", name.c_str(), value.c_str()); +} + +} // namespace + +void BuildHandlerArgvStrings( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + std::vector* argv_strings) { + argv_strings->clear(); + + argv_strings->push_back(handler.value()); + for (const auto& argument : arguments) { + argv_strings->push_back(argument); + } + + if (!database.empty()) { + argv_strings->push_back(FormatArgumentString("database", database.value())); + } + + if (!metrics_dir.empty()) { + argv_strings->push_back( + FormatArgumentString("metrics-dir", metrics_dir.value())); + } + + if (!url.empty()) { + argv_strings->push_back(FormatArgumentString("url", url)); + } + + for (const auto& kv : annotations) { + argv_strings->push_back( + FormatArgumentString("annotation", kv.first + '=' + kv.second)); + } +} + +void ConvertArgvStrings(const std::vector& argv_strings, + std::vector* argv) { + argv->clear(); + argv->reserve(argv_strings.size() + 1); + for (const auto& arg : argv_strings) { + argv->push_back(arg.c_str()); + } + argv->push_back(nullptr); +} + +} // namespace crashpad diff --git a/client/client_argv_handling.h b/client/client_argv_handling.h new file mode 100644 index 00000000..0ef3a154 --- /dev/null +++ b/client/client_argv_handling.h @@ -0,0 +1,53 @@ +// Copyright 2018 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_CLIENT_CLIENT_ARGV_HANDLING_H_ +#define CRASHPAD_CLIENT_CLIENT_ARGV_HANDLING_H_ + +#include +#include +#include + +#include "base/files/file_path.h" + +namespace crashpad { + +//! \brief Builds a vector of arguments suitable for invoking a handler process +//! based on arguments passed to StartHandler-type(). +//! +//! See StartHandlerAtCrash() for documentation on the input arguments. +//! +//! \param[out] A argv_strings vector of arguments suitable for starting the +//! handler with. +void BuildHandlerArgvStrings( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + std::vector* argv_strings); + +//! \brief Flattens a string vector into a const char* vector suitable for use +//! in an exec() call. +//! +//! \param[in] argv_strings Arguments to be passed to child process, typically +//! created by BuildHandlerArgvStrings(). +//! \param[out] argv argv suitable for starting the child process. +void ConvertArgvStrings(const std::vector& argv_strings, + std::vector* argv); + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_CLIENT_ARGV_HANDLING_H_ diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc index 22f7e3d8..4d0d111b 100644 --- a/client/crashpad_client_linux.cc +++ b/client/crashpad_client_linux.cc @@ -24,6 +24,7 @@ #include "base/logging.h" #include "base/strings/stringprintf.h" +#include "client/client_argv_handling.h" #include "util/file/file_io.h" #include "util/linux/exception_handler_client.h" #include "util/linux/exception_information.h" @@ -36,11 +37,6 @@ namespace crashpad { namespace { -std::string FormatArgumentString(const std::string& name, - const std::string& value) { - return base::StringPrintf("--%s=%s", name.c_str(), value.c_str()); -} - std::string FormatArgumentInt(const std::string& name, int value) { return base::StringPrintf("--%s=%d", name.c_str(), value); } @@ -49,50 +45,6 @@ std::string FormatArgumentAddress(const std::string& name, void* addr) { return base::StringPrintf("--%s=%p", name.c_str(), addr); } -void BuildHandlerArgvStrings( - const base::FilePath& handler, - const base::FilePath& database, - const base::FilePath& metrics_dir, - const std::string& url, - const std::map& annotations, - const std::vector& arguments, - std::vector* argv_strings) { - argv_strings->clear(); - - argv_strings->push_back(handler.value()); - for (const auto& argument : arguments) { - argv_strings->push_back(argument); - } - - if (!database.empty()) { - argv_strings->push_back(FormatArgumentString("database", database.value())); - } - - if (!metrics_dir.empty()) { - argv_strings->push_back( - FormatArgumentString("metrics-dir", metrics_dir.value())); - } - - if (!url.empty()) { - argv_strings->push_back(FormatArgumentString("url", url)); - } - - for (const auto& kv : annotations) { - argv_strings->push_back( - FormatArgumentString("annotation", kv.first + '=' + kv.second)); - } -} - -void ConvertArgvStrings(const std::vector& argv_strings, - std::vector* argv) { - argv->clear(); - argv->reserve(argv_strings.size() + 1); - for (const auto& arg : argv_strings) { - argv->push_back(arg.c_str()); - } - argv->push_back(nullptr); -} - class SignalHandler { public: virtual void HandleCrashFatal(int signo, From c7fe30dddbb0309930b8a078ec14c7950d64224b Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 10 Apr 2018 14:25:06 -0700 Subject: [PATCH 239/326] fuchsia: Get generate_dump to start attempting process dumps Add pid->handle mapping code to generate_dump. This is enough to get generate_dump to start capturing a dump for an arbitrary system process. It currently CHECK()s in ProcessSnapshotFuchsia on some unimplemented functionality. Bug: crashpad:196 Change-Id: Idfbaa4fbf32af63ad6db5b0b78a7a1991b82728e Reviewed-on: https://chromium-review.googlesource.com/1005804 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- tools/generate_dump.cc | 11 ++- util/BUILD.gn | 2 + util/fuchsia/koid_utilities.cc | 136 +++++++++++++++++++++++++++++++++ util/fuchsia/koid_utilities.h | 39 ++++++++++ 4 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 util/fuchsia/koid_utilities.cc create mode 100644 util/fuchsia/koid_utilities.h diff --git a/tools/generate_dump.cc b/tools/generate_dump.cc index cce0823b..52c189c6 100644 --- a/tools/generate_dump.cc +++ b/tools/generate_dump.cc @@ -46,7 +46,9 @@ #include "util/win/scoped_process_suspend.h" #include "util/win/xp_compat.h" #elif defined(OS_FUCHSIA) +#include "base/fuchsia/scoped_zx_handle.h" #include "snapshot/fuchsia/process_snapshot_fuchsia.h" +#include "util/fuchsia/koid_utilities.h" #elif defined(OS_LINUX) || defined(OS_ANDROID) #include "snapshot/linux/process_snapshot_linux.h" #endif // OS_MACOSX @@ -161,6 +163,12 @@ int GenerateDumpMain(int argc, char* argv[]) { PLOG(ERROR) << "could not open process " << options.pid; return EXIT_FAILURE; } +#elif defined(OS_FUCHSIA) + base::ScopedZxHandle task = GetProcessFromKoid(options.pid); + if (!task.is_valid()) { + LOG(ERROR) << "could not open process " << options.pid; + return EXIT_FAILURE; + } #endif // OS_MACOSX if (options.dump_path.empty()) { @@ -197,8 +205,7 @@ int GenerateDumpMain(int argc, char* argv[]) { } #elif defined(OS_FUCHSIA) ProcessSnapshotFuchsia process_snapshot; - // TODO(scottmg): https://crashpad.chromium.org/bug/196. - if (!process_snapshot.Initialize(ZX_HANDLE_INVALID)) { + if (!process_snapshot.Initialize(task.get())) { return EXIT_FAILURE; } #elif defined(OS_LINUX) || defined(OS_ANDROID) diff --git a/util/BUILD.gn b/util/BUILD.gn index 5649aec1..8cfbee37 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -388,6 +388,8 @@ static_library("util") { if (crashpad_is_fuchsia) { sources += [ + "fuchsia/koid_utilities.cc", + "fuchsia/koid_utilities.h", "misc/capture_context_fuchsia.S", "misc/paths_fuchsia.cc", "net/http_transport_none.cc", diff --git a/util/fuchsia/koid_utilities.cc b/util/fuchsia/koid_utilities.cc new file mode 100644 index 00000000..b9978524 --- /dev/null +++ b/util/fuchsia/koid_utilities.cc @@ -0,0 +1,136 @@ +// Copyright 2018 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 "util/fuchsia/koid_utilities.h" + +#include + +#include + +#include "base/files/file_path.h" +#include "base/fuchsia/fuchsia_logging.h" +#include "util/file/file_io.h" + +namespace crashpad { + +namespace { + +base::ScopedZxHandle GetRootJob() { + ScopedFileHandle sysinfo( + LoggingOpenFileForRead(base::FilePath("/dev/misc/sysinfo"))); + if (!sysinfo.is_valid()) + return base::ScopedZxHandle(); + + zx_handle_t root_job; + size_t n = ioctl_sysinfo_get_root_job(sysinfo.get(), &root_job); + if (n != sizeof(root_job)) { + LOG(ERROR) << "unexpected root job size"; + return base::ScopedZxHandle(); + } + return base::ScopedZxHandle(root_job); +} + +std::vector GetChildKoids(zx_handle_t parent, uint32_t child_kind) { + constexpr size_t kNumExtraKoids = 10u; + + size_t actual = 0; + size_t available = 0; + std::vector result; + + // This is inherently racy, but we retry with a bit of slop to try to get a + // complete list. + for (int pass = 0; pass < 5; pass++) { + if (actual <= available) + result.resize(available + kNumExtraKoids); + zx_status_t status = zx_object_get_info(parent, + child_kind, + result.data(), + result.size() * sizeof(zx_koid_t), + &actual, + &available); + if (actual == available) { + break; + } + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_object_get_info"; + break; + } + } + + result.resize(actual); + return result; +} + +// type can be ZX_INFO_JOB_CHILDREN or ZX_INFO_JOB_PROCESSES. +std::vector GetChildObjects( + const base::ScopedZxHandle& job, + zx_object_info_topic_t type) { + auto koids = GetChildKoids(job.get(), type); + + std::vector result; + result.reserve(koids.size()); + + for (zx_koid_t koid : koids) { + zx_handle_t handle; + if (zx_object_get_child(job.get(), koid, ZX_RIGHT_SAME_RIGHTS, &handle) == + ZX_OK) + result.push_back(base::ScopedZxHandle(handle)); + } + return result; +} + +bool FindProcess(const base::ScopedZxHandle& job, + zx_koid_t koid, + base::ScopedZxHandle* out) { + for (auto& proc : GetChildObjects(job, ZX_INFO_JOB_PROCESSES)) { + if (GetKoidForHandle(proc.get()) == koid) { + *out = std::move(proc); + return true; + } + } + + for (const auto& child_job : GetChildObjects(job, ZX_INFO_JOB_CHILDREN)) { + if (FindProcess(child_job, koid, out)) + return true; + } + + return false; +} + +} // namespace + +zx_koid_t GetKoidForHandle(zx_handle_t object) { + zx_info_handle_basic_t info; + zx_status_t status = zx_object_get_info( + object, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_object_get_info"; + return ZX_HANDLE_INVALID; + } + return info.koid; +} + +// TODO(scottmg): This implementation uses some debug/temporary/hacky APIs and +// ioctls that are currently the only way to go from pid to handle. This should +// hopefully eventually be replaced by more or less a single +// zx_debug_something() syscall. +base::ScopedZxHandle GetProcessFromKoid(zx_koid_t koid) { + base::ScopedZxHandle result; + if (!FindProcess(GetRootJob(), koid, &result)) { + LOG(ERROR) << "process " << koid << " not found"; + } + return result; +} + +} // namespace crashpad diff --git a/util/fuchsia/koid_utilities.h b/util/fuchsia/koid_utilities.h new file mode 100644 index 00000000..2a37e4ef --- /dev/null +++ b/util/fuchsia/koid_utilities.h @@ -0,0 +1,39 @@ +// Copyright 2018 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_UTIL_FUCHSIA_KOID_UTILITIES_H_ +#define CRASHPAD_UTIL_FUCHSIA_KOID_UTILITIES_H_ + +#include + +#include "base/fuchsia/scoped_zx_handle.h" + +namespace crashpad { + +//! \brief Gets a process handle given the process' koid. +//! +//! \param[in] koid The process id. +//! \return A zx_handle_t (owned by a base::ScopedZxHandle) for the process. If +//! the handle is invalid, an error will have been logged. +base::ScopedZxHandle GetProcessFromKoid(zx_koid_t koid); + +//! \brief Retrieves the koid for a given object handle. +//! +//! \param[in] object The handle for which the koid is to be retrieved. +//! \return The koid of \a handle, or `ZX_HANDLE_INVALID` with an error logged. +zx_koid_t GetKoidForHandle(zx_handle_t object); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_FUCHSIA_KOID_UTILITIES_H_ From cf55a7ef550662c4cb7334073be3d934dc7bcda5 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 10 Apr 2018 14:27:54 -0700 Subject: [PATCH 240/326] fuchsia: Implement SnapshotTime() Bug: crashpad:196 Change-Id: I398a8e933c64fca33e2620543c4c9d52a07f7d8f Reviewed-on: https://chromium-review.googlesource.com/1005835 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- snapshot/fuchsia/process_snapshot_fuchsia.cc | 11 ++++++++--- snapshot/fuchsia/process_snapshot_fuchsia.h | 2 ++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc index 9d0897d8..8211df83 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -18,13 +18,18 @@ namespace crashpad { -ProcessSnapshotFuchsia::ProcessSnapshotFuchsia() {} +ProcessSnapshotFuchsia::ProcessSnapshotFuchsia() = default; -ProcessSnapshotFuchsia::~ProcessSnapshotFuchsia() {} +ProcessSnapshotFuchsia::~ProcessSnapshotFuchsia() = default; bool ProcessSnapshotFuchsia::Initialize(zx_handle_t process) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + if (gettimeofday(&snapshot_time_, nullptr) != 0) { + PLOG(ERROR) << "gettimeofday"; + return false; + } + if (!process_reader_.Initialize(process)) { return false; } @@ -87,7 +92,7 @@ pid_t ProcessSnapshotFuchsia::ParentProcessID() const { void ProcessSnapshotFuchsia::SnapshotTime(timeval* snapshot_time) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + *snapshot_time = snapshot_time_; } void ProcessSnapshotFuchsia::ProcessStartTime(timeval* start_time) const { diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.h b/snapshot/fuchsia/process_snapshot_fuchsia.h index eb077330..f7eda4e3 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.h +++ b/snapshot/fuchsia/process_snapshot_fuchsia.h @@ -15,6 +15,7 @@ #ifndef CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_SNAPSHOT_FUCHSIA_H_ #define CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_SNAPSHOT_FUCHSIA_H_ +#include #include #include @@ -84,6 +85,7 @@ class ProcessSnapshotFuchsia : public ProcessSnapshot { std::vector> modules_; ProcessReaderFuchsia process_reader_; std::map annotations_simple_map_; + timeval snapshot_time_; InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotFuchsia); From f5f0aa4a8e843d08736eded813832f7d14a38ece Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 10 Apr 2018 15:34:33 -0700 Subject: [PATCH 241/326] fuchsia: Implementation of system snapshot Mostly sensible implementation for x64 via cpuid. It's too early for Fuchsia to have a version number, so nothing is reported for those fields. ARM64 isn't implemented at all and would hit a lot of NOTREACHED()s. Bug: crashpad:196 Change-Id: I6ca8b12e16fe0cf773a17c88ca9d407b028a501c Reviewed-on: https://chromium-review.googlesource.com/1005906 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- minidump/minidump_extensions.h | 3 + minidump/minidump_system_info_writer.cc | 3 + snapshot/BUILD.gn | 2 + snapshot/fuchsia/process_snapshot_fuchsia.cc | 5 +- snapshot/fuchsia/process_snapshot_fuchsia.h | 2 + snapshot/fuchsia/system_snapshot_fuchsia.cc | 207 +++++++++++++++++++ snapshot/fuchsia/system_snapshot_fuchsia.h | 87 ++++++++ snapshot/system_snapshot.h | 3 + 8 files changed, 310 insertions(+), 2 deletions(-) create mode 100644 snapshot/fuchsia/system_snapshot_fuchsia.cc create mode 100644 snapshot/fuchsia/system_snapshot_fuchsia.h diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h index fbaa6ee5..f3701dc2 100644 --- a/minidump/minidump_extensions.h +++ b/minidump/minidump_extensions.h @@ -248,6 +248,9 @@ enum MinidumpOS : uint32_t { //! \brief Native Client (NaCl). kMinidumpOSNaCl = 0x8205, + //! \brief Fuchsia. + kMinidumpOSFuchsia = 0x8206, + //! \brief Unknown operating system. kMinidumpOSUnknown = 0xffffffff, }; diff --git a/minidump/minidump_system_info_writer.cc b/minidump/minidump_system_info_writer.cc index 4a22b436..cc87d242 100644 --- a/minidump/minidump_system_info_writer.cc +++ b/minidump/minidump_system_info_writer.cc @@ -172,6 +172,9 @@ void MinidumpSystemInfoWriter::InitializeFromSnapshot( case SystemSnapshot::kOperatingSystemAndroid: operating_system = kMinidumpOSAndroid; break; + case SystemSnapshot::kOperatingSystemFuchsia: + operating_system = kMinidumpOSFuchsia; + break; default: NOTREACHED(); operating_system = kMinidumpOSUnknown; diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 44bb53f6..9f559e8e 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -180,6 +180,8 @@ static_library("snapshot") { "fuchsia/process_reader_fuchsia.h", "fuchsia/process_snapshot_fuchsia.cc", "fuchsia/process_snapshot_fuchsia.h", + "fuchsia/system_snapshot_fuchsia.cc", + "fuchsia/system_snapshot_fuchsia.h", "fuchsia/thread_snapshot_fuchsia.cc", "fuchsia/thread_snapshot_fuchsia.h", ] diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc index 8211df83..64d839c3 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -34,6 +34,8 @@ bool ProcessSnapshotFuchsia::Initialize(zx_handle_t process) { return false; } + system_.Initialize(&snapshot_time_); + InitializeThreads(); InitializeModules(); @@ -125,8 +127,7 @@ ProcessSnapshotFuchsia::AnnotationsSimpleMap() const { const SystemSnapshot* ProcessSnapshotFuchsia::System() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return nullptr; + return &system_; } std::vector ProcessSnapshotFuchsia::Threads() const { diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.h b/snapshot/fuchsia/process_snapshot_fuchsia.h index f7eda4e3..0f14404d 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.h +++ b/snapshot/fuchsia/process_snapshot_fuchsia.h @@ -26,6 +26,7 @@ #include "snapshot/elf/elf_image_reader.h" #include "snapshot/elf/module_snapshot_elf.h" #include "snapshot/fuchsia/process_reader_fuchsia.h" +#include "snapshot/fuchsia/system_snapshot_fuchsia.h" #include "snapshot/fuchsia/thread_snapshot_fuchsia.h" #include "snapshot/process_snapshot.h" #include "snapshot/unloaded_module_snapshot.h" @@ -81,6 +82,7 @@ class ProcessSnapshotFuchsia : public ProcessSnapshot { // Initializes modules_ on behalf of Initialize(). void InitializeModules(); + internal::SystemSnapshotFuchsia system_; std::vector> threads_; std::vector> modules_; ProcessReaderFuchsia process_reader_; diff --git a/snapshot/fuchsia/system_snapshot_fuchsia.cc b/snapshot/fuchsia/system_snapshot_fuchsia.cc new file mode 100644 index 00000000..f42b50fe --- /dev/null +++ b/snapshot/fuchsia/system_snapshot_fuchsia.cc @@ -0,0 +1,207 @@ +// Copyright 2018 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/fuchsia/system_snapshot_fuchsia.h" + +#include +#include + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "snapshot/posix/timezone.h" + +namespace crashpad { +namespace internal { + +SystemSnapshotFuchsia::SystemSnapshotFuchsia() = default; + +SystemSnapshotFuchsia::~SystemSnapshotFuchsia() = default; + +void SystemSnapshotFuchsia::Initialize(const timeval* snapshot_time) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + snapshot_time_ = snapshot_time; + + std::string uname_string; + utsname uts; + if (uname(&uts) != 0) { + PLOG(WARNING) << "uname"; + } else { + uname_string = base::StringPrintf("%s %s", uts.sysname, uts.machine); + } + + // TODO(scottmg): There's no version available to be reported yet. + + os_version_full_ = uname_string; + + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +CPUArchitecture SystemSnapshotFuchsia::GetCPUArchitecture() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_64) + return kCPUArchitectureX86_64; +#elif defined(ARCH_CPU_ARM64) + return kCPUArchitectureARM64; +#else +#error Port +#endif +} + +uint32_t SystemSnapshotFuchsia::CPURevision() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return cpuid_.Revision(); +#else + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196. + return 0; +#endif +} + +uint8_t SystemSnapshotFuchsia::CPUCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return base::saturated_cast(zx_system_get_num_cpus()); +} + +std::string SystemSnapshotFuchsia::CPUVendor() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return cpuid_.Vendor(); +#else + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196. + return std::string(); +#endif +} + +void SystemSnapshotFuchsia::CPUFrequency(uint64_t* current_hz, + uint64_t* max_hz) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(scottmg): https://crashpad.chromium.org/bug/196. + *current_hz = 0; + *max_hz = 0; +} + +uint32_t SystemSnapshotFuchsia::CPUX86Signature() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return cpuid_.Signature(); +#else + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196. + return 0; +#endif +} + +uint64_t SystemSnapshotFuchsia::CPUX86Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return cpuid_.Features(); +#else + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196. + return 0; +#endif +} + +uint64_t SystemSnapshotFuchsia::CPUX86ExtendedFeatures() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return cpuid_.ExtendedFeatures(); +#else + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196. + return 0; +#endif +} + +uint32_t SystemSnapshotFuchsia::CPUX86Leaf7Features() const { +#if defined(ARCH_CPU_X86_64) + return cpuid_.Leaf7Features(); +#else + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196. + return 0; +#endif +} + +bool SystemSnapshotFuchsia::CPUX86SupportsDAZ() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return cpuid_.SupportsDAZ(); +#else + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196. + return false; +#endif +} + +SystemSnapshot::OperatingSystem SystemSnapshotFuchsia::GetOperatingSystem() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kOperatingSystemFuchsia; +} + +bool SystemSnapshotFuchsia::OSServer() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return false; +} + +void SystemSnapshotFuchsia::OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(scottmg): https://crashpad.chromium.org/bug/196. There's no version + // available to be reported yet. + *major = 0; + *minor = 0; + *bugfix = 0; + *build = std::string(); +} + +std::string SystemSnapshotFuchsia::OSVersionFull() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return os_version_full_; +} + +std::string SystemSnapshotFuchsia::MachineDescription() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196. + return std::string(); +} + +bool SystemSnapshotFuchsia::NXEnabled() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return cpuid_.NXEnabled(); +#else + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196. + return false; +#endif +} + +void SystemSnapshotFuchsia::TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + internal::TimeZone(*snapshot_time_, + dst_status, + standard_offset_seconds, + daylight_offset_seconds, + standard_name, + daylight_name); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/fuchsia/system_snapshot_fuchsia.h b/snapshot/fuchsia/system_snapshot_fuchsia.h new file mode 100644 index 00000000..c7cd35dc --- /dev/null +++ b/snapshot/fuchsia/system_snapshot_fuchsia.h @@ -0,0 +1,87 @@ +// Copyright 2018 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_FUCHSIA_SYSTEM_SNAPSHOT_FUCHSIA_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_SYSTEM_SNAPSHOT_FUCHSIA_H_ + +#include + +#include "base/macros.h" +#include "build/build_config.h" +#include "snapshot/system_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +#if defined(ARCH_CPU_X86_FAMILY) +#include "snapshot/x86/cpuid_reader.h" +#endif + +namespace crashpad { +namespace internal { + +//! \brief A SystemSnapshot of the running system, when the system runs Fuchsia. +class SystemSnapshotFuchsia final : public SystemSnapshot { + public: + SystemSnapshotFuchsia(); + ~SystemSnapshotFuchsia() override; + + //! \brief Initializes the object. + //! + //! \param[in] snapshot_time The time of the snapshot being taken. + //! + //! This parameter is necessary for TimeZone() to determine whether daylight + //! saving time was in effect at the time the snapshot was taken. Otherwise, + //! it would need to base its determination on the current time, which may be + //! different than the snapshot time for snapshots generated around the + //! daylight saving transition time. + void Initialize(const timeval* snapshot_time); + + // SystemSnapshot: + + CPUArchitecture GetCPUArchitecture() const override; + uint32_t CPURevision() const override; + uint8_t CPUCount() const override; + std::string CPUVendor() const override; + void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override; + uint32_t CPUX86Signature() const override; + uint64_t CPUX86Features() const override; + uint64_t CPUX86ExtendedFeatures() const override; + uint32_t CPUX86Leaf7Features() const override; + bool CPUX86SupportsDAZ() const override; + OperatingSystem GetOperatingSystem() const override; + bool OSServer() const override; + void OSVersion( + int* major, int* minor, int* bugfix, std::string* build) const override; + std::string OSVersionFull() const override; + bool NXEnabled() const override; + std::string MachineDescription() const override; + void TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const override; + private: + std::string os_version_full_; + const timeval* snapshot_time_; // weak +#if defined(ARCH_CPU_X86_FAMILY) + CpuidReader cpuid_; +#endif // ARCH_CPU_X86_FAMILY + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(SystemSnapshotFuchsia); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_SYSTEM_SNAPSHOT_FUCHSIA_H_ diff --git a/snapshot/system_snapshot.h b/snapshot/system_snapshot.h index 6da3d376..a363c0c8 100644 --- a/snapshot/system_snapshot.h +++ b/snapshot/system_snapshot.h @@ -47,6 +47,9 @@ class SystemSnapshot { //! \brief Android. kOperatingSystemAndroid, + + //! \brief Fuchsia. + kOperatingSystemFuchsia, }; //! \brief A system’s daylight saving time status. From a4f4d6a73659263c6d4b5654aac584521f6788a0 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 10 Apr 2018 16:02:00 -0700 Subject: [PATCH 242/326] fuchsia: Small fixes in system and process snapshot hit on generate_dump - Implement ProcessID(). - Return empty ProcessStartTime() and ProcessCPUTimes() as there's nothing available. - Return the Threads that were collected in Initialize(). - Return empty MachineDescription() plus upstream bug link. Bug: crashpad:196 Change-Id: I77b33c18ed3844464bb5b9f238406191c221b17e Reviewed-on: https://chromium-review.googlesource.com/1005889 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- snapshot/fuchsia/process_snapshot_fuchsia.cc | 20 ++++++++++++++------ snapshot/fuchsia/system_snapshot_fuchsia.cc | 3 ++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc index 64d839c3..f16baec2 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -14,7 +14,10 @@ #include "snapshot/fuchsia/process_snapshot_fuchsia.h" +#include + #include "base/logging.h" +#include "util/fuchsia/koid_utilities.h" namespace crashpad { @@ -82,8 +85,7 @@ void ProcessSnapshotFuchsia::GetCrashpadOptions( pid_t ProcessSnapshotFuchsia::ProcessID() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return 0; + return GetKoidForHandle(zx_process_self()); } pid_t ProcessSnapshotFuchsia::ParentProcessID() const { @@ -99,13 +101,16 @@ void ProcessSnapshotFuchsia::SnapshotTime(timeval* snapshot_time) const { void ProcessSnapshotFuchsia::ProcessStartTime(timeval* start_time) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + // TODO(scottmg): https://crashpad.chromium.org/bug/196. Nothing available. + *start_time = timeval{}; } void ProcessSnapshotFuchsia::ProcessCPUTimes(timeval* user_time, timeval* system_time) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + // TODO(scottmg): https://crashpad.chromium.org/bug/196. Nothing available. + *user_time = timeval{}; + *system_time = timeval{}; } void ProcessSnapshotFuchsia::ReportID(UUID* report_id) const { @@ -132,8 +137,11 @@ const SystemSnapshot* ProcessSnapshotFuchsia::System() const { std::vector ProcessSnapshotFuchsia::Threads() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return std::vector(); + std::vector threads; + for (const auto& thread : threads_) { + threads.push_back(thread.get()); + } + return threads; } std::vector ProcessSnapshotFuchsia::Modules() const { diff --git a/snapshot/fuchsia/system_snapshot_fuchsia.cc b/snapshot/fuchsia/system_snapshot_fuchsia.cc index f42b50fe..7705f78b 100644 --- a/snapshot/fuchsia/system_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/system_snapshot_fuchsia.cc @@ -174,7 +174,8 @@ std::string SystemSnapshotFuchsia::OSVersionFull() const { std::string SystemSnapshotFuchsia::MachineDescription() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196. + // TODO(scottmg): https://crashpad.chromium.org/bug/196. Not yet available, + // upstream ZX-1775. return std::string(); } From d9bf38f39c5ac9f4cedff86d75bd45ef556ab9b2 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 10 Apr 2018 16:03:05 -0700 Subject: [PATCH 243/326] fuchsia: More fixes to get a basic minidump written With this `generate_dump ` generates a valid and somewhat plausible (but still quite incomplete) minidump. As an example, on a running Fuchsia system, `ps` reported the pid of "netstack" as 6062, followed by `generate_dump 6062`, copy minidump.6062 to host, and run Breakpad's minidump_dump on the generated dump file, resulting in: https://gist.github.com/sgraham/24e4ba1af968219d7c154bb0fba43925 This looks roughly correct in that it has a bunch of threads (without much data) and a reasonable looking module list. Bug: crashpad:196 Change-Id: I3f68cc015f74374624a5ce497d46ac90df17a22c Reviewed-on: https://chromium-review.googlesource.com/1005978 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- snapshot/fuchsia/process_snapshot_fuchsia.cc | 10 +++----- snapshot/fuchsia/process_snapshot_fuchsia.h | 27 ++++++++++++++++++++ snapshot/fuchsia/thread_snapshot_fuchsia.cc | 1 + 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc index f16baec2..c2d5ab3e 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -115,18 +115,17 @@ void ProcessSnapshotFuchsia::ProcessCPUTimes(timeval* user_time, void ProcessSnapshotFuchsia::ReportID(UUID* report_id) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + *report_id = report_id_; } void ProcessSnapshotFuchsia::ClientID(UUID* client_id) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + *client_id = client_id_; } const std::map& ProcessSnapshotFuchsia::AnnotationsSimpleMap() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return annotations_simple_map_; } @@ -162,26 +161,23 @@ std::vector ProcessSnapshotFuchsia::UnloadedModules() const ExceptionSnapshot* ProcessSnapshotFuchsia::Exception() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + // TODO(scottmg): https://crashpad.chromium.org/bug/196 return nullptr; } std::vector ProcessSnapshotFuchsia::MemoryMap() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return std::vector(); } std::vector ProcessSnapshotFuchsia::Handles() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return std::vector(); } std::vector ProcessSnapshotFuchsia::ExtraMemory() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 return std::vector(); } diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.h b/snapshot/fuchsia/process_snapshot_fuchsia.h index 0f14404d..72078a78 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.h +++ b/snapshot/fuchsia/process_snapshot_fuchsia.h @@ -56,6 +56,31 @@ class ProcessSnapshotFuchsia : public ProcessSnapshot { //! the process. void GetCrashpadOptions(CrashpadInfoClientOptions* options); + //! \brief Sets the value to be returned by ReportID(). + //! + //! On Fuchsia, the crash report ID is under the control of the snapshot + //! producer, which may call this method to set the report ID. If this is not + //! done, ReportID() will return an identifier consisting entirely of zeroes. + void SetReportID(const UUID& report_id) { report_id_ = report_id; } + + //! \brief Sets the value to be returned by ClientID(). + //! + //! On Fuchsia, the client ID is under the control of the snapshot producer, + //! which may call this method to set the client ID. If this is not done, + //! ClientID() will return an identifier consisting entirely of zeroes. + void SetClientID(const UUID& client_id) { client_id_ = client_id; } + + //! \brief Sets the value to be returned by AnnotationsSimpleMap(). + //! + //! On Fuchsia, all process annotations are under the control of the snapshot + //! producer, which may call this method to establish these annotations. + //! Contrast this with module annotations, which are under the control of the + //! process being snapshotted. + void SetAnnotationsSimpleMap( + const std::map& annotations_simple_map) { + annotations_simple_map_ = annotations_simple_map; + } + // ProcessSnapshot: pid_t ProcessID() const override; pid_t ParentProcessID() const override; @@ -87,6 +112,8 @@ class ProcessSnapshotFuchsia : public ProcessSnapshot { std::vector> modules_; ProcessReaderFuchsia process_reader_; std::map annotations_simple_map_; + UUID report_id_; + UUID client_id_; timeval snapshot_time_; InitializationStateDcheck initialized_; diff --git a/snapshot/fuchsia/thread_snapshot_fuchsia.cc b/snapshot/fuchsia/thread_snapshot_fuchsia.cc index ce1aeb33..2963ca3e 100644 --- a/snapshot/fuchsia/thread_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/thread_snapshot_fuchsia.cc @@ -49,6 +49,7 @@ bool ThreadSnapshotFuchsia::Initialize( // TODO(scottmg): https://crashpad.chromium.org/bug/196. Initialize stack_ and // TLS address here. API request for stack range filed upstream at ZX-1748. + stack_.Initialize(process_reader, 0, 0); thread_id_ = thread.id; From 856339b2d2110cce85f99468d30a0b3553530d4d Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 11 Apr 2018 13:41:11 -0700 Subject: [PATCH 244/326] fuchsia: Avoid uname() in system snapshot uname() seems to hang, sometimes, perhaps when then network is in a bad state. Additionally, this way allows getting a minimal amount of version information via zx_system_get_version(). Bug: crashpad:196 Change-Id: I2c040ee38ae017a6e8e060de10039bae6d159058 Reviewed-on: https://chromium-review.googlesource.com/1007979 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- snapshot/fuchsia/system_snapshot_fuchsia.cc | 29 +++++++++++++-------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/snapshot/fuchsia/system_snapshot_fuchsia.cc b/snapshot/fuchsia/system_snapshot_fuchsia.cc index 7705f78b..889817ad 100644 --- a/snapshot/fuchsia/system_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/system_snapshot_fuchsia.cc @@ -14,9 +14,9 @@ #include "snapshot/fuchsia/system_snapshot_fuchsia.h" -#include #include +#include "base/fuchsia/fuchsia_logging.h" #include "base/logging.h" #include "base/numerics/safe_conversions.h" #include "base/strings/stringprintf.h" @@ -34,17 +34,24 @@ void SystemSnapshotFuchsia::Initialize(const timeval* snapshot_time) { snapshot_time_ = snapshot_time; - std::string uname_string; - utsname uts; - if (uname(&uts) != 0) { - PLOG(WARNING) << "uname"; - } else { - uname_string = base::StringPrintf("%s %s", uts.sysname, uts.machine); - } + // This version string mirrors `uname -a` as written by + // garnet/bin/uname/uname.c, however, this information isn't provided by + // uname(). Additionally, uname() seems to hang if the network is in a bad + // state when attempting to retrieve the nodename, so avoid it for now. + char kernel_version[256] = {}; + zx_status_t status = + zx_system_get_version(kernel_version, sizeof(kernel_version)); + ZX_LOG_IF(ERROR, status != ZX_OK, status) << "zx_system_get_version"; - // TODO(scottmg): There's no version available to be reported yet. - - os_version_full_ = uname_string; +#if defined(ARCH_CPU_X86_64) + static constexpr const char kArch[] = "x86_64"; +#elif defined(ARCH_CPU_ARM64) + static constexpr const char kArch[] = "aarch64"; +#else + static constexpr const char kArch[] = "unknown"; +#endif + os_version_full_ = + base::StringPrintf("Zircon prerelease %s %s", kernel_version, kArch); INITIALIZATION_STATE_SET_VALID(initialized_); } From 091308be6aeb506fdc305669fdd6d88661846e54 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 12 Apr 2018 12:33:22 -0700 Subject: [PATCH 245/326] gyp: build client_argv_handling client_argv_handling.{cc,h} were added to BUILD.gn, but omitted from the corresponding gyp files. Change-Id: I52ebf61234cfa22c3f08e2edd824c298e4879e6a Reviewed-on: https://chromium-review.googlesource.com/1010921 Reviewed-by: Scott Graham Commit-Queue: Joshua Peraza --- client/client.gyp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/client.gyp b/client/client.gyp index e149a2c1..a23d0c86 100644 --- a/client/client.gyp +++ b/client/client.gyp @@ -65,6 +65,8 @@ }], ['OS=="linux" or OS=="android"', { 'sources': [ + 'client_argv_handling.cc', + 'client_argv_handling.h', 'crashpad_info_note.S', 'crash_report_database_generic.cc', ], From dd4ba4c8a1684d3d0068fbf77638c65f84e1a46b Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 12 Apr 2018 16:45:35 -0700 Subject: [PATCH 246/326] linux, x86/x64: set fpregs to nullptr in CaptureContext() uc_mcontext.fpregs is a pointer to the floating point context, but CaptureContext() doesn't yet capture floating point context. This error manages to slip by unit tests when run all together, but fails when CrashpadClient.SimulateCrash is run by itself. Bug: crashpad:30 Change-Id: I7adc30648642912d66a7ba8cf9973c9bc0fbd8bc Reviewed-on: https://chromium-review.googlesource.com/1011504 Reviewed-by: Scott Graham Commit-Queue: Joshua Peraza --- client/crashpad_client_linux.cc | 9 +-------- util/misc/capture_context_linux.S | 7 +++++-- util/misc/capture_context_test_util_linux.cc | 5 ++++- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc index 4d0d111b..e7fa162c 100644 --- a/client/crashpad_client_linux.cc +++ b/client/crashpad_client_linux.cc @@ -210,19 +210,12 @@ bool CrashpadClient::StartHandlerForClient( void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) { DCHECK(g_crash_handler); -#if defined(ARCH_CPU_X86) - memset(&context->__fpregs_mem, 0, sizeof(context->__fpregs_mem)); - context->__fpregs_mem.status = 0xffff0000; -#elif defined(ARCH_CPU_X86_64) - memset(&context->__fpregs_mem, 0, sizeof(context->__fpregs_mem)); -#elif defined(ARCH_CPU_ARMEL) +#if defined(ARCH_CPU_ARMEL) memset(context->uc_regspace, 0, sizeof(context->uc_regspace)); #elif defined(ARCH_CPU_ARM64) memset(context->uc_mcontext.__reserved, 0, sizeof(context->uc_mcontext.__reserved)); -#else -#error Port. #endif siginfo_t siginfo; diff --git a/util/misc/capture_context_linux.S b/util/misc/capture_context_linux.S index 5541643f..6ec72454 100644 --- a/util/misc/capture_context_linux.S +++ b/util/misc/capture_context_linux.S @@ -128,6 +128,8 @@ CAPTURECONTEXT_SYMBOL2: movl %ecx, 0x5c(%eax) // context->uc_mcontext.xss // TODO(jperaza): save floating-point registers. + xorl %ecx, %ecx + movl %ecx, 0x60(%eax) // context->uc_mcontext.fpregs // Clean up by restoring clobbered registers, even those considered volatile // by the ABI, so that the captured context represents the state at this @@ -224,14 +226,15 @@ CAPTURECONTEXT_SYMBOL2: movq %r8, 0xd0(%rdi) // context->uc_mcontext.oldmask movq %r8, 0xd8(%rdi) // context->uc_mcontext.cr2 + // TODO(jperaza): save floating-point registers. + movq %r8, 0xe0(%rdi) // context->uc_mcontext.fpregs + // Clean up by restoring clobbered registers, even those considered volatile // by the ABI, so that the captured context represents the state at this // function’s exit. movq 0x90(%rdi), %rax movq 0x28(%rdi), %r8 - // TODO(jperaza): save floating-point registers. - popfq popq %rbp diff --git a/util/misc/capture_context_test_util_linux.cc b/util/misc/capture_context_test_util_linux.cc index fb64e5d4..3ba13d85 100644 --- a/util/misc/capture_context_test_util_linux.cc +++ b/util/misc/capture_context_test_util_linux.cc @@ -23,10 +23,13 @@ namespace test { void SanityCheckContext(const NativeCPUContext& context) { #if defined(ARCH_CPU_X86) - // Nothing to do here yet. + // TODO(jperaza): fpregs is nullptr until CaptureContext() supports capturing + // floating point context. + EXPECT_EQ(context.uc_mcontext.fpregs, nullptr); #elif defined(ARCH_CPU_X86_64) EXPECT_EQ(context.uc_mcontext.gregs[REG_RDI], FromPointerCast(&context)); + EXPECT_EQ(context.uc_mcontext.fpregs, nullptr); #elif defined(ARCH_CPU_ARMEL) EXPECT_EQ(context.uc_mcontext.arm_r0, FromPointerCast(&context)); #elif defined(ARCH_CPU_ARM64) From c80bf96001ddaa69ffa910e61537a967eb1c3116 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 12 Apr 2018 19:37:06 -0700 Subject: [PATCH 247/326] Don't record reports as complete if there is no upload thread This allows clients to use the database to handle uploads themselves, e.g. on Android, where Crashpad does not yet provide an uploader. The handler does not launch an upload thread when no url is supplied. Previously, the handler would move these reports to completed and record the upload as skipped with kUploadsDisabled. With this change, these reports would remain pending until pruned, with no metrics recorded for them in regard to their upload. Bug: crashpad:30 Change-Id: I4167ab1531634b10e91d03229018ae6aab4103aa Reviewed-on: https://chromium-review.googlesource.com/1010970 Reviewed-by: Robert Sesek Commit-Queue: Joshua Peraza --- client/crashpad_client_linux_test.cc | 14 ++++++++------ handler/crashpad_handler_test.cc | 2 +- handler/linux/crash_report_exception_handler.cc | 3 --- handler/mac/crash_report_exception_handler.cc | 3 --- handler/win/crash_report_exception_handler.cc | 3 --- snapshot/win/end_to_end_test.py | 2 +- 6 files changed, 10 insertions(+), 17 deletions(-) diff --git a/client/crashpad_client_linux_test.cc b/client/crashpad_client_linux_test.cc index 9610cddf..edfb5112 100644 --- a/client/crashpad_client_linux_test.cc +++ b/client/crashpad_client_linux_test.cc @@ -85,12 +85,12 @@ TEST(CrashpadClient, SimulateCrash) { std::vector reports; ASSERT_EQ(database->GetPendingReports(&reports), CrashReportDatabase::kNoError); - EXPECT_EQ(reports.size(), 0u); + EXPECT_EQ(reports.size(), 1u); reports.clear(); ASSERT_EQ(database->GetCompletedReports(&reports), CrashReportDatabase::kNoError); - EXPECT_EQ(reports.size(), 1u); + EXPECT_EQ(reports.size(), 0u); } } @@ -147,11 +147,12 @@ class StartHandlerAtCrashTest : public MultiprocessExec { std::vector reports; ASSERT_EQ(database->GetPendingReports(&reports), CrashReportDatabase::kNoError); - EXPECT_EQ(reports.size(), 0u); + EXPECT_EQ(reports.size(), 1u); + reports.clear(); ASSERT_EQ(database->GetCompletedReports(&reports), CrashReportDatabase::kNoError); - EXPECT_EQ(reports.size(), 1u); + EXPECT_EQ(reports.size(), 0u); } DISALLOW_COPY_AND_ASSIGN(StartHandlerAtCrashTest); @@ -213,11 +214,12 @@ class StartHandlerForClientTest { std::vector reports; ASSERT_EQ(database->GetPendingReports(&reports), CrashReportDatabase::kNoError); - EXPECT_EQ(reports.size(), 0u); + EXPECT_EQ(reports.size(), 1u); + reports.clear(); ASSERT_EQ(database->GetCompletedReports(&reports), CrashReportDatabase::kNoError); - EXPECT_EQ(reports.size(), 1u); + EXPECT_EQ(reports.size(), 0u); } bool InstallHandler() { diff --git a/handler/crashpad_handler_test.cc b/handler/crashpad_handler_test.cc index 79076b86..9c468ddf 100644 --- a/handler/crashpad_handler_test.cc +++ b/handler/crashpad_handler_test.cc @@ -93,7 +93,7 @@ void CrashWithExtendedHandler::ValidateGeneratedDump() { ASSERT_TRUE(database); std::vector reports; - ASSERT_EQ(database->GetCompletedReports(&reports), + ASSERT_EQ(database->GetPendingReports(&reports), CrashReportDatabase::kNoError); ASSERT_EQ(reports.size(), 1u); diff --git a/handler/linux/crash_report_exception_handler.cc b/handler/linux/crash_report_exception_handler.cc index e72819cd..2f38f2c0 100644 --- a/handler/linux/crash_report_exception_handler.cc +++ b/handler/linux/crash_report_exception_handler.cc @@ -140,9 +140,6 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection( if (upload_thread_) { upload_thread_->ReportPending(uuid); - } else { - database_->SkipReportUpload( - uuid, Metrics::CrashSkippedReason::kUploadsDisabled); } } diff --git a/handler/mac/crash_report_exception_handler.cc b/handler/mac/crash_report_exception_handler.cc index edebf87a..9919e955 100644 --- a/handler/mac/crash_report_exception_handler.cc +++ b/handler/mac/crash_report_exception_handler.cc @@ -189,9 +189,6 @@ kern_return_t CrashReportExceptionHandler::CatchMachException( if (upload_thread_) { upload_thread_->ReportPending(uuid); - } else { - database_->SkipReportUpload( - uuid, Metrics::CrashSkippedReason::kUploadsDisabled); } } diff --git a/handler/win/crash_report_exception_handler.cc b/handler/win/crash_report_exception_handler.cc index 6d53d810..d845c446 100644 --- a/handler/win/crash_report_exception_handler.cc +++ b/handler/win/crash_report_exception_handler.cc @@ -126,9 +126,6 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException( if (upload_thread_) { upload_thread_->ReportPending(uuid); - } else { - database_->SkipReportUpload( - uuid, Metrics::CrashSkippedReason::kUploadsDisabled); } } diff --git a/snapshot/win/end_to_end_test.py b/snapshot/win/end_to_end_test.py index a08cfd1a..fd602fbc 100755 --- a/snapshot/win/end_to_end_test.py +++ b/snapshot/win/end_to_end_test.py @@ -155,7 +155,7 @@ def GetDumpFromProgram( out = subprocess.check_output([ os.path.join(out_dir, 'crashpad_database_util.exe'), '--database=' + test_database, - '--show-completed-reports', + '--show-pending-reports', '--show-all-report-info', ]) for line in out.splitlines(): From f5d5a41317bed624e3c9226fdc585a0407af3bae Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 13 Apr 2018 09:42:19 -0700 Subject: [PATCH 248/326] fuchsia: Add implementation of ScopedTaskSuspend This implementation has some limitations as documented in the header, however, threads must be suspended in order to use the register capture debug API so this is somewhat useful for now in the context of generate_dump. Also, refactor some child-object retrieval helpers used in a few places. Bug: crashpad:196 Change-Id: I1fdae5fc3d4b43841e535724eac10c1e58af04c5 Reviewed-on: https://chromium-review.googlesource.com/1007966 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- snapshot/fuchsia/process_reader_fuchsia.cc | 93 +++++----------- tools/generate_dump.cc | 6 ++ util/BUILD.gn | 2 + util/fuchsia/koid_utilities.cc | 118 ++++++++++++--------- util/fuchsia/koid_utilities.h | 47 ++++++++ util/fuchsia/scoped_task_suspend.cc | 91 ++++++++++++++++ util/fuchsia/scoped_task_suspend.h | 53 +++++++++ 7 files changed, 295 insertions(+), 115 deletions(-) create mode 100644 util/fuchsia/scoped_task_suspend.cc create mode 100644 util/fuchsia/scoped_task_suspend.h diff --git a/snapshot/fuchsia/process_reader_fuchsia.cc b/snapshot/fuchsia/process_reader_fuchsia.cc index 1ace0d13..ffff96d3 100644 --- a/snapshot/fuchsia/process_reader_fuchsia.cc +++ b/snapshot/fuchsia/process_reader_fuchsia.cc @@ -20,6 +20,7 @@ #include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/scoped_zx_handle.h" #include "base/logging.h" +#include "util/fuchsia/koid_utilities.h" namespace crashpad { @@ -189,74 +190,38 @@ void ProcessReaderFuchsia::InitializeThreads() { initialized_threads_ = true; - // Retrieve the thread koids. This is racy; better if the process is suspended - // itself, but threads could still be externally created. As there's no - // maximum, this needs to be retried in a loop until the actual threads - // retrieved is equal to the available threads. - - std::vector threads(100); - size_t actual_num_threads, available_num_threads; - for (;;) { - zx_status_t status = zx_object_get_info(process_, - ZX_INFO_PROCESS_THREADS, - &threads[0], - sizeof(threads[0]) * threads.size(), - &actual_num_threads, - &available_num_threads); - // If the buffer is too small (even zero), the result is still ZX_OK, not - // ZX_ERR_BUFFER_TOO_SMALL. - if (status != ZX_OK) { - ZX_LOG(ERROR, status) << "zx_object_get_info ZX_INFO_PROCESS_THREADS"; - break; - } - if (actual_num_threads == available_num_threads) { - threads.resize(actual_num_threads); - break; - } - - // Resize to the expected number next time with a bit extra to attempt to - // handle the race between here and the next request. - threads.resize(available_num_threads + 10); - } - - for (const zx_koid_t thread_koid : threads) { - zx_handle_t raw_handle; - zx_status_t status = zx_object_get_child( - process_, thread_koid, ZX_RIGHT_SAME_RIGHTS, &raw_handle); - if (status != ZX_OK) { - ZX_LOG(ERROR, status) << "zx_object_get_child"; - // TODO(scottmg): Decide if it's worthwhile adding a mostly-empty Thread - // here, consisting only of the koid, but no other information. The only - // time this is expected to happen is when there's a race between getting - // the koid above, and requesting the handle here. - continue; - } - - base::ScopedZxHandle thread_handle(raw_handle); + std::vector thread_koids = + GetChildKoids(process_, ZX_INFO_PROCESS_THREADS); + std::vector thread_handles = + GetHandlesForChildKoids(process_, thread_koids); + DCHECK_EQ(thread_koids.size(), thread_handles.size()); + for (size_t i = 0; i < thread_handles.size(); ++i) { Thread thread; - thread.id = thread_koid; + thread.id = thread_koids[i]; - char name[ZX_MAX_NAME_LEN] = {0}; - status = zx_object_get_property( - thread_handle.get(), ZX_PROP_NAME, &name, sizeof(name)); - if (status != ZX_OK) { - ZX_LOG(WARNING, status) << "zx_object_get_property ZX_PROP_NAME"; - } else { - thread.name.assign(name); - } + if (thread_handles[i].is_valid()) { + char name[ZX_MAX_NAME_LEN] = {0}; + zx_status_t status = zx_object_get_property( + thread_handles[i].get(), ZX_PROP_NAME, &name, sizeof(name)); + if (status != ZX_OK) { + ZX_LOG(WARNING, status) << "zx_object_get_property ZX_PROP_NAME"; + } else { + thread.name.assign(name); + } - zx_info_thread_t thread_info; - status = zx_object_get_info(thread_handle.get(), - ZX_INFO_THREAD, - &thread_info, - sizeof(thread_info), - nullptr, - nullptr); - if (status != ZX_OK) { - ZX_LOG(WARNING, status) << "zx_object_get_info ZX_INFO_THREAD"; - } else { - thread.state = thread_info.state; + zx_info_thread_t thread_info; + status = zx_object_get_info(thread_handles[i].get(), + ZX_INFO_THREAD, + &thread_info, + sizeof(thread_info), + nullptr, + nullptr); + if (status != ZX_OK) { + ZX_LOG(WARNING, status) << "zx_object_get_info ZX_INFO_THREAD"; + } else { + thread.state = thread_info.state; + } } threads_.push_back(thread); diff --git a/tools/generate_dump.cc b/tools/generate_dump.cc index 52c189c6..2cdaed27 100644 --- a/tools/generate_dump.cc +++ b/tools/generate_dump.cc @@ -49,6 +49,7 @@ #include "base/fuchsia/scoped_zx_handle.h" #include "snapshot/fuchsia/process_snapshot_fuchsia.h" #include "util/fuchsia/koid_utilities.h" +#include "util/fuchsia/scoped_task_suspend.h" #elif defined(OS_LINUX) || defined(OS_ANDROID) #include "snapshot/linux/process_snapshot_linux.h" #endif // OS_MACOSX @@ -186,6 +187,11 @@ int GenerateDumpMain(int argc, char* argv[]) { if (options.suspend) { suspend.reset(new ScopedProcessSuspend(process.get())); } +#elif defined(OS_FUCHSIA) + std::unique_ptr suspend; + if (options.suspend) { + suspend.reset(new ScopedTaskSuspend(task.get())); + } #endif // OS_MACOSX #if defined(OS_MACOSX) diff --git a/util/BUILD.gn b/util/BUILD.gn index 8cfbee37..bb0fd6fa 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -390,6 +390,8 @@ static_library("util") { sources += [ "fuchsia/koid_utilities.cc", "fuchsia/koid_utilities.h", + "fuchsia/scoped_task_suspend.cc", + "fuchsia/scoped_task_suspend.h", "misc/capture_context_fuchsia.S", "misc/paths_fuchsia.cc", "net/http_transport_none.cc", diff --git a/util/fuchsia/koid_utilities.cc b/util/fuchsia/koid_utilities.cc index b9978524..b280f524 100644 --- a/util/fuchsia/koid_utilities.cc +++ b/util/fuchsia/koid_utilities.cc @@ -41,66 +41,22 @@ base::ScopedZxHandle GetRootJob() { return base::ScopedZxHandle(root_job); } -std::vector GetChildKoids(zx_handle_t parent, uint32_t child_kind) { - constexpr size_t kNumExtraKoids = 10u; - - size_t actual = 0; - size_t available = 0; - std::vector result; - - // This is inherently racy, but we retry with a bit of slop to try to get a - // complete list. - for (int pass = 0; pass < 5; pass++) { - if (actual <= available) - result.resize(available + kNumExtraKoids); - zx_status_t status = zx_object_get_info(parent, - child_kind, - result.data(), - result.size() * sizeof(zx_koid_t), - &actual, - &available); - if (actual == available) { - break; - } - if (status != ZX_OK) { - ZX_LOG(ERROR, status) << "zx_object_get_info"; - break; - } - } - - result.resize(actual); - return result; -} - -// type can be ZX_INFO_JOB_CHILDREN or ZX_INFO_JOB_PROCESSES. -std::vector GetChildObjects( - const base::ScopedZxHandle& job, - zx_object_info_topic_t type) { - auto koids = GetChildKoids(job.get(), type); - - std::vector result; - result.reserve(koids.size()); - - for (zx_koid_t koid : koids) { - zx_handle_t handle; - if (zx_object_get_child(job.get(), koid, ZX_RIGHT_SAME_RIGHTS, &handle) == - ZX_OK) - result.push_back(base::ScopedZxHandle(handle)); - } - return result; -} - bool FindProcess(const base::ScopedZxHandle& job, zx_koid_t koid, base::ScopedZxHandle* out) { - for (auto& proc : GetChildObjects(job, ZX_INFO_JOB_PROCESSES)) { + for (auto& proc : GetChildHandles(job.get(), ZX_INFO_JOB_PROCESSES)) { if (GetKoidForHandle(proc.get()) == koid) { *out = std::move(proc); return true; } } - for (const auto& child_job : GetChildObjects(job, ZX_INFO_JOB_CHILDREN)) { + // TODO(scottmg): As this is recursing down the job tree all the handles are + // kept open, so this could be very expensive in terms of number of open + // handles. This function should be replaced by a syscall in the + // not-too-distant future, so hopefully OK for now. + for (const auto& child_job : + GetChildHandles(job.get(), ZX_INFO_JOB_CHILDREN)) { if (FindProcess(child_job, koid, out)) return true; } @@ -110,6 +66,66 @@ bool FindProcess(const base::ScopedZxHandle& job, } // namespace +std::vector GetChildKoids(zx_handle_t parent, + zx_object_info_topic_t child_kind) { + size_t actual = 0; + size_t available = 0; + std::vector result(100); + + // This is inherently racy. Better if the process is suspended, but there's + // still no guarantee that a thread isn't externally created. As a result, + // must be in a retry loop. + for (;;) { + zx_status_t status = zx_object_get_info(parent, + child_kind, + result.data(), + result.size() * sizeof(zx_koid_t), + &actual, + &available); + // If the buffer is too small (even zero), the result is still ZX_OK, not + // ZX_ERR_BUFFER_TOO_SMALL. + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_object_get_info"; + break; + } + + if (actual == available) { + break; + } + + // Resize to the expected number next time, with a bit of slop to handle the + // race between here and the next request. + result.resize(available + 10); + } + + result.resize(actual); + return result; +} + +std::vector GetChildHandles(zx_handle_t parent, + zx_object_info_topic_t type) { + auto koids = GetChildKoids(parent, type); + return GetHandlesForChildKoids(parent, koids); +} + +std::vector GetHandlesForChildKoids( + zx_handle_t parent, + const std::vector& koids) { + std::vector result; + result.reserve(koids.size()); + + for (zx_koid_t koid : koids) { + zx_handle_t handle; + if (zx_object_get_child(parent, koid, ZX_RIGHT_SAME_RIGHTS, &handle) == + ZX_OK) { + result.emplace_back(base::ScopedZxHandle(handle)); + } else { + result.push_back(base::ScopedZxHandle()); + } + } + return result; +} + zx_koid_t GetKoidForHandle(zx_handle_t object) { zx_info_handle_basic_t info; zx_status_t status = zx_object_get_info( diff --git a/util/fuchsia/koid_utilities.h b/util/fuchsia/koid_utilities.h index 2a37e4ef..326bcac2 100644 --- a/util/fuchsia/koid_utilities.h +++ b/util/fuchsia/koid_utilities.h @@ -15,12 +15,59 @@ #ifndef CRASHPAD_UTIL_FUCHSIA_KOID_UTILITIES_H_ #define CRASHPAD_UTIL_FUCHSIA_KOID_UTILITIES_H_ +#include #include +#include + #include "base/fuchsia/scoped_zx_handle.h" namespace crashpad { +//! \brief Get a list of child koids for a parent handle. +//! +//! For example, the list of processes in jobs, or the list of threads in a +//! process. +//! +//! \param[in] parent The handle to the parent object. +//! \param[in] child_kind The type of children to retrieve from \a parent. Valid +//! values depend on the type of \a parent, but include +//! `ZX_INFO_JOB_CHILDREN` (child jobs of a job), `ZX_INFO_JOB_PROCESSES` +//! (child processes of a job), and `ZX_INFO_PROCESS_THREADS` (child threads +//! of a process). +//! \return A vector of the koids representing the child objects. +//! +//! \sa GetChildHandles +std::vector GetChildKoids(zx_handle_t parent, + zx_object_info_topic_t child_kind); + +//! \brief Get handles representing a list of child objects of a given parent. +//! +//! \param[in] parent The handle to the parent object. +//! \param[in] child_kind The type of children to retrieve from \a parent. Valid +//! values depend on the type of \a parent, but include +//! `ZX_INFO_JOB_CHILDREN` (child jobs of a job), `ZX_INFO_JOB_PROCESSES` +//! (child processes of a job), and `ZX_INFO_PROCESS_THREADS` (child threads +//! of a process). +//! \return The resulting list of handles corresponding to the child objects. +//! +//! \sa GetChildKoids +std::vector GetChildHandles( + zx_handle_t parent, + zx_object_info_topic_t child_kind); + +//! \brief Convert a list of koids that are all children of a particular object +//! into handles. +//! +//! \param[in] parent The parent object to which the koids belong. +//! \param[in] koids The list of koids. +//! \return The resulting list of handles corresponding to the koids. If an +//! element of \a koids is invalid or can't be retrieved, there will be a +//! corresponding `ZX_HANDLE_INVALID` entry in the return. +std::vector GetHandlesForChildKoids( + zx_handle_t parent, + const std::vector& koids); + //! \brief Gets a process handle given the process' koid. //! //! \param[in] koid The process id. diff --git a/util/fuchsia/scoped_task_suspend.cc b/util/fuchsia/scoped_task_suspend.cc new file mode 100644 index 00000000..594edf5c --- /dev/null +++ b/util/fuchsia/scoped_task_suspend.cc @@ -0,0 +1,91 @@ +// Copyright 2018 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 "util/fuchsia/scoped_task_suspend.h" + +#include +#include + +#include + +#include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/scoped_zx_handle.h" +#include "base/logging.h" +#include "util/fuchsia/koid_utilities.h" + +namespace crashpad { + +namespace { + +zx_obj_type_t GetHandleType(zx_handle_t handle) { + zx_info_handle_basic_t basic; + zx_status_t status = zx_object_get_info( + handle, ZX_INFO_HANDLE_BASIC, &basic, sizeof(basic), nullptr, nullptr); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_object_get_info"; + return ZX_OBJ_TYPE_NONE; + } + return basic.type; +} + +bool SuspendThread(zx_handle_t thread) { + zx_status_t status = zx_task_suspend(thread); + ZX_LOG_IF(ERROR, status != ZX_OK, status) << "zx_task_suspend"; + return status == ZX_OK; +} + +bool ResumeThread(zx_handle_t thread) { + zx_status_t status = zx_task_resume(thread, 0); + ZX_LOG_IF(ERROR, status != ZX_OK, status) << "zx_task_resume"; + return status == ZX_OK; +} + +} // namespace + +ScopedTaskSuspend::ScopedTaskSuspend(zx_handle_t task) : task_(task) { + DCHECK_NE(task_, zx_process_self()); + DCHECK_NE(task_, zx_thread_self()); + + zx_obj_type_t type = GetHandleType(task_); + if (type == ZX_OBJ_TYPE_THREAD) { + if (!SuspendThread(task_)) { + task_ = ZX_HANDLE_INVALID; + } + } else if (type == ZX_OBJ_TYPE_PROCESS) { + for (const auto& thread : GetChildHandles(task_, ZX_INFO_PROCESS_THREADS)) { + SuspendThread(thread.get()); + } + } else { + LOG(ERROR) << "unexpected handle type"; + task_ = ZX_HANDLE_INVALID; + } +} + +ScopedTaskSuspend::~ScopedTaskSuspend() { + if (task_ != ZX_HANDLE_INVALID) { + zx_obj_type_t type = GetHandleType(task_); + if (type == ZX_OBJ_TYPE_THREAD) { + ResumeThread(task_); + } else if (type == ZX_OBJ_TYPE_PROCESS) { + for (const auto& thread : + GetChildHandles(task_, ZX_INFO_PROCESS_THREADS)) { + ResumeThread(thread.get()); + } + } else { + LOG(ERROR) << "unexpected handle type"; + } + } +} + +} // namespace crashpad diff --git a/util/fuchsia/scoped_task_suspend.h b/util/fuchsia/scoped_task_suspend.h new file mode 100644 index 00000000..d3790c8c --- /dev/null +++ b/util/fuchsia/scoped_task_suspend.h @@ -0,0 +1,53 @@ +// Copyright 2018 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_UTIL_FUCHSIA_SCOPED_TASK_SUSPEND_H_ +#define CRASHPAD_UTIL_FUCHSIA_SCOPED_TASK_SUSPEND_H_ + +#include + +#include "base/macros.h" + +namespace crashpad { + +//! \brief Manages the suspension of another task. +//! +//! Currently, suspends and resumes are not counted on Fuchsia, so while this +//! class attempts to manage suspension of a task, if another caller or process +//! is simultaneously suspending or resuming this task, the results may not be +//! as expected. +//! +//! Additionally, the underlying API only supports suspending threads (despite +//! its name) not entire tasks. As a result, it's possible some threads may not +//! be correctly suspended/resumed as their creation might race enumeration. +//! +//! Because of these limitations, this class is limited to being a best-effort, +//! and correct suspension/resumption cannot be relied upon. +//! +//! Callers should not attempt to suspend the current task as obtained via +//! `zx_process_self()`. +class ScopedTaskSuspend { + public: + explicit ScopedTaskSuspend(zx_handle_t task); + ~ScopedTaskSuspend(); + + private: + zx_handle_t task_; // weak + + DISALLOW_COPY_AND_ASSIGN(ScopedTaskSuspend); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_FUCHSIA_SCOPED_TASK_SUSPEND_H_ From c2583364a36e4d50f78795e99ee3512a09ef5922 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 12 Apr 2018 14:16:10 -0700 Subject: [PATCH 249/326] fuchsia: Capture general purpose registers in thread snapshot Conversion to CPUContext is currently only implemented for x64. Bug: crashpad:196 Change-Id: I3fb8541f70a6f8d6f12c02e6b17c78e07e195056 Reviewed-on: https://chromium-review.googlesource.com/1007967 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- snapshot/BUILD.gn | 2 + snapshot/fuchsia/cpu_context_fuchsia.cc | 51 +++++++++++++++++++++ snapshot/fuchsia/cpu_context_fuchsia.h | 46 +++++++++++++++++++ snapshot/fuchsia/process_reader_fuchsia.cc | 9 ++++ snapshot/fuchsia/process_reader_fuchsia.h | 6 +++ snapshot/fuchsia/thread_snapshot_fuchsia.cc | 7 ++- snapshot/linux/cpu_context_linux.h | 6 +-- snapshot/mac/cpu_context_mac.h | 6 +-- 8 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 snapshot/fuchsia/cpu_context_fuchsia.cc create mode 100644 snapshot/fuchsia/cpu_context_fuchsia.h diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 9f559e8e..de0b8611 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -176,6 +176,8 @@ static_library("snapshot") { if (crashpad_is_fuchsia) { sources += [ + "fuchsia/cpu_context_fuchsia.cc", + "fuchsia/cpu_context_fuchsia.h", "fuchsia/process_reader_fuchsia.cc", "fuchsia/process_reader_fuchsia.h", "fuchsia/process_snapshot_fuchsia.cc", diff --git a/snapshot/fuchsia/cpu_context_fuchsia.cc b/snapshot/fuchsia/cpu_context_fuchsia.cc new file mode 100644 index 00000000..5a02f62f --- /dev/null +++ b/snapshot/fuchsia/cpu_context_fuchsia.cc @@ -0,0 +1,51 @@ +// Copyright 2018 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/fuchsia/cpu_context_fuchsia.h" + +#include + +namespace crashpad { +namespace internal { + +#if defined(ARCH_CPU_X86_64) + +void InitializeCPUContextX86_64( + const zx_thread_state_general_regs_t& thread_context, + CPUContextX86_64* context) { + memset(context, 0, sizeof(*context)); + context->rax = thread_context.rax; + context->rbx = thread_context.rbx; + context->rcx = thread_context.rcx; + context->rdx = thread_context.rdx; + context->rdi = thread_context.rdi; + context->rsi = thread_context.rsi; + context->rbp = thread_context.rbp; + context->rsp = thread_context.rsp; + context->r8 = thread_context.r8; + context->r9 = thread_context.r9; + context->r10 = thread_context.r10; + context->r11 = thread_context.r11; + context->r12 = thread_context.r12; + context->r13 = thread_context.r13; + context->r14 = thread_context.r14; + context->r15 = thread_context.r15; + context->rip = thread_context.rip; + context->rflags = thread_context.rflags; +} + +#endif // ARCH_CPU_X86_64 + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/fuchsia/cpu_context_fuchsia.h b/snapshot/fuchsia/cpu_context_fuchsia.h new file mode 100644 index 00000000..fb654bee --- /dev/null +++ b/snapshot/fuchsia/cpu_context_fuchsia.h @@ -0,0 +1,46 @@ +// Copyright 2018 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_FUCHSIA_CPU_CONTEXT_FUCHSIA_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_CPU_CONTEXT_FUCHSIA_H_ + +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/fuchsia/process_reader_fuchsia.h" + +namespace crashpad { +namespace internal { + +#if defined(ARCH_CPU_X86_64) || DOXYGEN + +//! \brief Initializes a CPUContextX86_64 structure from native context +//! structures on Fuchsia. +//! +//! Floating point registers are currently initialized to zero. +//! Segment registers are currently initialized to zero. +//! +//! \param[in] thread_context The native thread context. +//! \param[out] context The CPUContextX86_64 structure to initialize. +void InitializeCPUContextX86_64( + const zx_thread_state_general_regs_t& thread_context, + CPUContextX86_64* context); + +#endif // ARCH_CPU_X86_64 || DOXYGEN + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_CPU_CONTEXT_FUCHSIA_H_ diff --git a/snapshot/fuchsia/process_reader_fuchsia.cc b/snapshot/fuchsia/process_reader_fuchsia.cc index ffff96d3..7bad6a6c 100644 --- a/snapshot/fuchsia/process_reader_fuchsia.cc +++ b/snapshot/fuchsia/process_reader_fuchsia.cc @@ -224,6 +224,15 @@ void ProcessReaderFuchsia::InitializeThreads() { } } + zx_thread_state_general_regs_t regs; + status = zx_thread_read_state( + thread_handle.get(), ZX_THREAD_STATE_GENERAL_REGS, ®s, sizeof(regs)); + if (status != ZX_OK) { + ZX_LOG(WARNING, status) << "zx_thread_read_state"; + } else { + thread.general_registers = regs; + } + threads_.push_back(thread); } } diff --git a/snapshot/fuchsia/process_reader_fuchsia.h b/snapshot/fuchsia/process_reader_fuchsia.h index d0811cfe..5efecb12 100644 --- a/snapshot/fuchsia/process_reader_fuchsia.h +++ b/snapshot/fuchsia/process_reader_fuchsia.h @@ -15,6 +15,8 @@ #ifndef CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_READER_H_ #define CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_READER_H_ +#include + #include #include @@ -68,6 +70,10 @@ class ProcessReaderFuchsia { //! \brief The `ZX_PROP_NAME` property of the thread. This may be empty. std::string name; + + //! \brief The raw architecture-specific `zx_thread_state_general_regs_t` as + //! returned by `zx_thread_read_state()`. + zx_thread_state_general_regs_t general_registers = {}; }; ProcessReaderFuchsia(); diff --git a/snapshot/fuchsia/thread_snapshot_fuchsia.cc b/snapshot/fuchsia/thread_snapshot_fuchsia.cc index 2963ca3e..a4e0c1a9 100644 --- a/snapshot/fuchsia/thread_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/thread_snapshot_fuchsia.cc @@ -15,6 +15,7 @@ #include "snapshot/fuchsia/thread_snapshot_fuchsia.h" #include "base/logging.h" +#include "snapshot/fuchsia/cpu_context_fuchsia.h" namespace crashpad { namespace internal { @@ -38,11 +39,13 @@ bool ThreadSnapshotFuchsia::Initialize( #if defined(ARCH_CPU_X86_64) context_.architecture = kCPUArchitectureX86_64; context_.x86_64 = &context_arch_; -// TODO(scottmg): Implement context capture for x64. + // TODO(scottmg): Float context, once Fuchsia has a debug API to capture + // floating point registers. ZX-1750 upstream. + InitializeCPUContextX86_64(thread.general_registers, context_.x86_64); #elif defined(ARCH_CPU_ARM64) context_.architecture = kCPUArchitectureARM64; context_.arm64 = &context_arch_; -// TODO(scottmg): Implement context capture for arm64. + // TODO(scottmg): Implement context capture for arm64. #else #error Port. #endif diff --git a/snapshot/linux/cpu_context_linux.h b/snapshot/linux/cpu_context_linux.h index 22803763..4ca679a9 100644 --- a/snapshot/linux/cpu_context_linux.h +++ b/snapshot/linux/cpu_context_linux.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_CPU_CONTEXT_LINUX_H_ -#define CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_CPU_CONTEXT_LINUX_H_ +#ifndef CRASHPAD_SNAPSHOT_LINUX_CPU_CONTEXT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_CPU_CONTEXT_LINUX_H_ #include "build/build_config.h" #include "snapshot/cpu_context.h" @@ -141,4 +141,4 @@ void InitializeCPUContextARM64_OnlyFPSIMD( } // namespace internal } // namespace crashpad -#endif // CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_CPU_CONTEXT_LINUX_H_ +#endif // CRASHPAD_SNAPSHOT_LINUX_CPU_CONTEXT_LINUX_H_ diff --git a/snapshot/mac/cpu_context_mac.h b/snapshot/mac/cpu_context_mac.h index c96ad3c4..30281c16 100644 --- a/snapshot/mac/cpu_context_mac.h +++ b/snapshot/mac/cpu_context_mac.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CRASHPAD_SNAPSHOT_MAC_SNAPSHOT_CPU_CONTEXT_MAC_H_ -#define CRASHPAD_SNAPSHOT_MAC_SNAPSHOT_CPU_CONTEXT_MAC_H_ +#ifndef CRASHPAD_SNAPSHOT_MAC_CPU_CONTEXT_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_CPU_CONTEXT_MAC_H_ #include @@ -113,4 +113,4 @@ void InitializeCPUContextX86_64(CPUContextX86_64* context, } // namespace internal } // namespace crashpad -#endif // CRASHPAD_SNAPSHOT_MAC_SNAPSHOT_CPU_CONTEXT_MAC_H_ +#endif // CRASHPAD_SNAPSHOT_MAC_CPU_CONTEXT_MAC_H_ From eca0ea8427c2897088b61fb266960288363367eb Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 12 Apr 2018 14:42:20 -0700 Subject: [PATCH 250/326] Add limited version of URL cracking This is a very basic form of URL cracking to break a HTTPTransport::SetURL() argument up into component parts. This is split out of the (upcoming) https://chromium-review.googlesource.com/c/crashpad/crashpad/+/1008407 for Linux and Fuchsia. Bug: crashpad:196 Change-Id: Iba075d9c8720c14550ce53e23d684362da84740c Reviewed-on: https://chromium-review.googlesource.com/1010972 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- util/net/url.cc | 48 +++++++++++++++++++++++++ util/net/url.h | 19 ++++++++++ util/net/url_test.cc | 85 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+) diff --git a/util/net/url.cc b/util/net/url.cc index 6309a8f7..9ce88e93 100644 --- a/util/net/url.cc +++ b/util/net/url.cc @@ -16,6 +16,7 @@ #include +#include "base/logging.h" #include "base/strings/stringprintf.h" namespace crashpad { @@ -41,4 +42,51 @@ std::string URLEncode(const std::string& url) { return encoded; } +bool CrackURL(const std::string& url, + std::string* scheme, + std::string* host, + std::string* port, + std::string* rest) { + std::string result_scheme; + std::string result_port; + + size_t host_start; + static constexpr const char kHttp[] = "http://"; + static constexpr const char kHttps[] = "https://"; + if (url.compare(0, strlen(kHttp), kHttp) == 0) { + result_scheme = "http"; + result_port = "80"; + host_start = strlen(kHttp); + } else if (url.compare(0, strlen(kHttps), kHttps) == 0) { + result_scheme = "https"; + result_port = "443"; + host_start = strlen(kHttps); + } else { + LOG(ERROR) << "expecting http or https"; + return false; + } + + // Find the start of the resource. + size_t resource_start = url.find('/', host_start); + if (resource_start == std::string::npos) { + LOG(ERROR) << "no resource component"; + return false; + } + + scheme->swap(result_scheme); + port->swap(result_port); + std::string host_and_possible_port = + url.substr(host_start, resource_start - host_start); + size_t colon = host_and_possible_port.find(':'); + if (colon == std::string::npos) { + *host = host_and_possible_port; + } else { + *host = host_and_possible_port.substr(0, colon); + *port = host_and_possible_port.substr(colon + 1); + } + + *rest = url.substr(resource_start); + return true; +} + } // namespace crashpad diff --git a/util/net/url.h b/util/net/url.h index 8817571f..f83d436c 100644 --- a/util/net/url.h +++ b/util/net/url.h @@ -26,6 +26,25 @@ namespace crashpad { //! \return The encoded string. std::string URLEncode(const std::string& url); +//! \brief Crack a URL into component parts. +//! +//! This is not a general function, and works only on the limited style of URLs +//! that are expected to be used by HTTPTransport::SetURL(). +//! +//! \param[in] url The URL to crack. +//! \param[out] scheme The request scheme, either http or https. +//! \param[out] host The hostname. +//! \param[out] port The port. +//! \param[out] rest The remainder of the URL (both resource and URL params). +//! \return `true` on success in which case all output parameters will be filled +//! out, or `false` on failure, in which case the output parameters will be +//! unmodified and an error will be logged. +bool CrackURL(const std::string& url, + std::string* scheme, + std::string* host, + std::string* port, + std::string* rest); + } // namespace crashpad #endif // CRASHPAD_UTIL_NET_URL_H_ diff --git a/util/net/url_test.cc b/util/net/url_test.cc index d9376671..4f076413 100644 --- a/util/net/url_test.cc +++ b/util/net/url_test.cc @@ -42,6 +42,91 @@ TEST(URLEncode, SimpleAddress) { "3Dvalue"); } +TEST(CrackURL, Unsupported) { + std::string scheme, host, port, rest; + + // Not HTTP. + EXPECT_FALSE(CrackURL("file://stuff/things", &scheme, &host, &port, &rest)); + + // No resource. + EXPECT_FALSE(CrackURL("file://stuff", &scheme, &host, &port, &rest)); + EXPECT_FALSE(CrackURL("http://stuff", &scheme, &host, &port, &rest)); + EXPECT_FALSE(CrackURL("https://stuff", &scheme, &host, &port, &rest)); +} + +TEST(CrackURL, UnsupportedDoesNotModifiedOutArgs) { + std::string scheme, host, port, rest; + + scheme = "scheme"; + host = "host"; + port = "port"; + rest = "rest"; + + // Bad scheme. + EXPECT_FALSE(CrackURL("file://stuff/things", &scheme, &host, &port, &rest)); + EXPECT_EQ(scheme, "scheme"); + EXPECT_EQ(host, "host"); + EXPECT_EQ(port, "port"); + EXPECT_EQ(rest, "rest"); + + scheme = "scheme"; + host = "host"; + port = "port"; + rest = "rest"; + + // No resource. + EXPECT_FALSE(CrackURL("http://stuff", &scheme, &host, &port, &rest)); + EXPECT_EQ(scheme, "scheme"); + EXPECT_EQ(host, "host"); + EXPECT_EQ(port, "port"); + EXPECT_EQ(rest, "rest"); +} + +TEST(CrackURL, BasicWithDefaultPort) { + std::string scheme, host, port, rest; + + ASSERT_TRUE(CrackURL("http://stuff/things", &scheme, &host, &port, &rest)); + EXPECT_EQ(scheme, "http"); + EXPECT_EQ(host, "stuff"); + EXPECT_EQ(port, "80"); + EXPECT_EQ(rest, "/things"); + + ASSERT_TRUE(CrackURL("https://stuff/things", &scheme, &host, &port, &rest)); + EXPECT_EQ(scheme, "https"); + EXPECT_EQ(host, "stuff"); + EXPECT_EQ(port, "443"); + EXPECT_EQ(rest, "/things"); +} + +TEST(CrackURL, BasicWithExplicitPort) { + std::string scheme, host, port, rest; + + ASSERT_TRUE( + CrackURL("http://stuff:999/things", &scheme, &host, &port, &rest)); + EXPECT_EQ(scheme, "http"); + EXPECT_EQ(host, "stuff"); + EXPECT_EQ(port, "999"); + EXPECT_EQ(rest, "/things"); + + ASSERT_TRUE( + CrackURL("https://stuff:1010/things", &scheme, &host, &port, &rest)); + EXPECT_EQ(scheme, "https"); + EXPECT_EQ(host, "stuff"); + EXPECT_EQ(port, "1010"); + EXPECT_EQ(rest, "/things"); +} + +TEST(CrackURL, WithURLParams) { + std::string scheme, host, port, rest; + + ASSERT_TRUE(CrackURL( + "http://stuff:999/things?blah=stuff:3", &scheme, &host, &port, &rest)); + EXPECT_EQ(scheme, "http"); + EXPECT_EQ(host, "stuff"); + EXPECT_EQ(port, "999"); + EXPECT_EQ(rest, "/things?blah=stuff:3"); +} + } // namespace } // namespace test } // namespace crashpad From ca2d7e776a0a276827cfe3144a81c9e1f1500ef0 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 13 Apr 2018 10:20:47 -0700 Subject: [PATCH 251/326] Pull go toolchain for all host types To be used to build replacement http test server. Bug: crashpad:227, crashpad:196, crashpad:30 Change-Id: I8bdd33a629d98af8b149bd83a11edb5965ad6e76 Reviewed-on: https://chromium-review.googlesource.com/1011653 Reviewed-by: Robert Sesek Commit-Queue: Scott Graham --- .gitignore | 1 + DEPS | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/.gitignore b/.gitignore index d225542e..56041446 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ /third_party/linux/.cipd /third_party/linux/clang /third_party/linux/sysroot +/third_party/go /third_party/gyp/gyp /third_party/mini_chromium/mini_chromium /third_party/zlib/zlib diff --git a/DEPS b/DEPS index e6719e2a..ae5cc201 100644 --- a/DEPS +++ b/DEPS @@ -114,6 +114,45 @@ hooks = [ 'buildtools/win/gn.exe.sha1', ], }, + { + 'name': 'go_toolchain_mac', + 'condition': 'host_os == "mac"', + 'pattern': '.', + 'action': [ + 'cipd', + 'install', + 'infra/go/mac-amd64', + 'latest', + '-root', 'crashpad/third_party/go/mac-amd64', + '-log-level', 'info', + ], + }, + { + 'name': 'go_toolchain_linux', + 'condition': 'host_os == "linux"', + 'pattern': '.', + 'action': [ + 'cipd', + 'install', + 'infra/go/linux-amd64', + 'latest', + '-root', 'crashpad/third_party/go/linux-amd64', + '-log-level', 'info', + ], + }, + { + 'name': 'go_toolchain_win', + 'condition': 'host_os == "win"', + 'pattern': '.', + 'action': [ + 'cipd', + 'install', + 'infra/go/windows-amd64', + 'latest', + '-root', 'crashpad/third_party/go/windows-amd64', + '-log-level', 'info', + ], + }, { # This uses “cipd install” so that mac-amd64 and linux-amd64 can coexist # peacefully. “cipd ensure” would remove the macOS package when running on a From 799dcef4c90fe28d67703cad8fec2a98a24aa86f Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 13 Apr 2018 11:47:26 -0700 Subject: [PATCH 252/326] fuchsia: Fix compilation after hasty review changes I'm a dope. I really need to get the trybots enabled again, but unfortunately I haven't made any useful progress on https://bugs.chromium.org/p/crashpad/issues/detail?id=219. Bug: crashpad:196 Change-Id: Iba2a2460d36c17d8261f82deb6cabaab5995111b Reviewed-on: https://chromium-review.googlesource.com/1012464 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- snapshot/fuchsia/cpu_context_fuchsia.h | 2 +- snapshot/fuchsia/process_reader_fuchsia.cc | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/snapshot/fuchsia/cpu_context_fuchsia.h b/snapshot/fuchsia/cpu_context_fuchsia.h index fb654bee..f5336fdd 100644 --- a/snapshot/fuchsia/cpu_context_fuchsia.h +++ b/snapshot/fuchsia/cpu_context_fuchsia.h @@ -15,7 +15,7 @@ #ifndef CRASHPAD_SNAPSHOT_FUCHSIA_CPU_CONTEXT_FUCHSIA_H_ #define CRASHPAD_SNAPSHOT_FUCHSIA_CPU_CONTEXT_FUCHSIA_H_ -#include +#include #include "build/build_config.h" #include "snapshot/cpu_context.h" diff --git a/snapshot/fuchsia/process_reader_fuchsia.cc b/snapshot/fuchsia/process_reader_fuchsia.cc index 7bad6a6c..e8f0fdc6 100644 --- a/snapshot/fuchsia/process_reader_fuchsia.cc +++ b/snapshot/fuchsia/process_reader_fuchsia.cc @@ -222,15 +222,17 @@ void ProcessReaderFuchsia::InitializeThreads() { } else { thread.state = thread_info.state; } - } - zx_thread_state_general_regs_t regs; - status = zx_thread_read_state( - thread_handle.get(), ZX_THREAD_STATE_GENERAL_REGS, ®s, sizeof(regs)); - if (status != ZX_OK) { - ZX_LOG(WARNING, status) << "zx_thread_read_state"; - } else { - thread.general_registers = regs; + zx_thread_state_general_regs_t regs; + status = zx_thread_read_state(thread_handles[i].get(), + ZX_THREAD_STATE_GENERAL_REGS, + ®s, + sizeof(regs)); + if (status != ZX_OK) { + ZX_LOG(WARNING, status) << "zx_thread_read_state"; + } else { + thread.general_registers = regs; + } } threads_.push_back(thread); From a7c30f05016bcbc53ed30387ac1919bbff683859 Mon Sep 17 00:00:00 2001 From: Leonard Mosescu Date: Mon, 16 Apr 2018 10:23:21 -0700 Subject: [PATCH 253/326] Fix a few small issues found by GCC Building Crashpad with GCC flagged a few potential issues. The issues don't seem particularly severe, but they are easy enough to fix. Note that even with these changes, Crashpad will not cleanly build with GCC (additional patches would be needed to third_party/mini_chromium). Bug: crashpad: Change-Id: I9289d6c918da9a111aa3c2a078ad0dc1ba84749f Reviewed-on: https://chromium-review.googlesource.com/1014280 Reviewed-by: Joshua Peraza Commit-Queue: Leonard Mosescu --- handler/linux/exception_handler_server.cc | 1 + util/process/process_memory_test.cc | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/handler/linux/exception_handler_server.cc b/handler/linux/exception_handler_server.cc index 17fc18c8..447093dc 100644 --- a/handler/linux/exception_handler_server.cc +++ b/handler/linux/exception_handler_server.cc @@ -164,6 +164,7 @@ class PtraceStrategyDeciderImpl : public PtraceStrategyDecider { } DCHECK(false); + return Strategy::kError; } private: diff --git a/util/process/process_memory_test.cc b/util/process/process_memory_test.cc index 5827638f..741e58d6 100644 --- a/util/process/process_memory_test.cc +++ b/util/process/process_memory_test.cc @@ -350,7 +350,7 @@ void DoReadUnmappedChildMainSetup(ScopedMmap* pages, CRASHPAD_CHILD_TEST_MAIN(ReadUnmappedChildMain) { ScopedMmap pages; - VMAddress address; + VMAddress address = 0; size_t page_size, region_size; DoReadUnmappedChildMainSetup(&pages, &address, &page_size, ®ion_size); FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); @@ -369,7 +369,7 @@ class ReadUnmappedTest : public MultiprocessExec { void RunAgainstSelf() { ScopedMmap pages; - VMAddress address; + VMAddress address = 0; size_t page_size, region_size; DoReadUnmappedChildMainSetup(&pages, &address, &page_size, ®ion_size); DoTest(GetSelfProcess(), address, page_size, region_size); @@ -379,7 +379,7 @@ class ReadUnmappedTest : public MultiprocessExec { private: void MultiprocessParent() override { - VMAddress address; + VMAddress address = 0; size_t page_size, region_size; ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &address, sizeof(address))); ASSERT_TRUE( From 439ba730c5ae031195ae927b5f8d077d2d733e77 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 18 Apr 2018 12:49:04 -0700 Subject: [PATCH 254/326] Implementation in C++ of HTTPTransport test server - Pulls in cpp-httplib for test-only usage in third_party/. - Replaces http_transport_test_server.py with .cc server. - Remove unnecessary Go toolchain pull. This was planned to be used for the test server, but the toolchain integration was too messy when covering all target platforms/configs. Bug: crashpad:196, crashpad:227, crashpad:30 Change-Id: I5990781473dcadfcc036fbe711c02928638ff851 Reviewed-on: https://chromium-review.googlesource.com/1013293 Reviewed-by: Robert Sesek Commit-Queue: Scott Graham --- .gitignore | 1 - DEPS | 39 - third_party/cpp-httplib/BUILD.gn | 21 + third_party/cpp-httplib/README.crashpad | 15 + third_party/cpp-httplib/cpp-httplib/LICENSE | 22 + third_party/cpp-httplib/cpp-httplib/README.md | 188 ++ third_party/cpp-httplib/cpp-httplib/httplib.h | 2207 +++++++++++++++++ util/BUILD.gn | 30 +- util/net/http_transport_test.cc | 18 +- util/net/http_transport_test_server.cc | 114 + util/net/http_transport_test_server.py | 202 -- 11 files changed, 2601 insertions(+), 256 deletions(-) create mode 100644 third_party/cpp-httplib/BUILD.gn create mode 100644 third_party/cpp-httplib/README.crashpad create mode 100644 third_party/cpp-httplib/cpp-httplib/LICENSE create mode 100644 third_party/cpp-httplib/cpp-httplib/README.md create mode 100644 third_party/cpp-httplib/cpp-httplib/httplib.h create mode 100644 util/net/http_transport_test_server.cc delete mode 100755 util/net/http_transport_test_server.py diff --git a/.gitignore b/.gitignore index 56041446..d225542e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,6 @@ /third_party/linux/.cipd /third_party/linux/clang /third_party/linux/sysroot -/third_party/go /third_party/gyp/gyp /third_party/mini_chromium/mini_chromium /third_party/zlib/zlib diff --git a/DEPS b/DEPS index ae5cc201..e6719e2a 100644 --- a/DEPS +++ b/DEPS @@ -114,45 +114,6 @@ hooks = [ 'buildtools/win/gn.exe.sha1', ], }, - { - 'name': 'go_toolchain_mac', - 'condition': 'host_os == "mac"', - 'pattern': '.', - 'action': [ - 'cipd', - 'install', - 'infra/go/mac-amd64', - 'latest', - '-root', 'crashpad/third_party/go/mac-amd64', - '-log-level', 'info', - ], - }, - { - 'name': 'go_toolchain_linux', - 'condition': 'host_os == "linux"', - 'pattern': '.', - 'action': [ - 'cipd', - 'install', - 'infra/go/linux-amd64', - 'latest', - '-root', 'crashpad/third_party/go/linux-amd64', - '-log-level', 'info', - ], - }, - { - 'name': 'go_toolchain_win', - 'condition': 'host_os == "win"', - 'pattern': '.', - 'action': [ - 'cipd', - 'install', - 'infra/go/windows-amd64', - 'latest', - '-root', 'crashpad/third_party/go/windows-amd64', - '-log-level', 'info', - ], - }, { # This uses “cipd install” so that mac-amd64 and linux-amd64 can coexist # peacefully. “cipd ensure” would remove the macOS package when running on a diff --git a/third_party/cpp-httplib/BUILD.gn b/third_party/cpp-httplib/BUILD.gn new file mode 100644 index 00000000..be2a6d74 --- /dev/null +++ b/third_party/cpp-httplib/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright 2018 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. + +source_set("cpp-httplib") { + testonly = true + include_dirs = [ "cpp-httplib" ] + sources = [ + "cpp-httplib/httplib.h", + ] +} diff --git a/third_party/cpp-httplib/README.crashpad b/third_party/cpp-httplib/README.crashpad new file mode 100644 index 00000000..e9a0bf1c --- /dev/null +++ b/third_party/cpp-httplib/README.crashpad @@ -0,0 +1,15 @@ +Name: cpp-httplib +Short Name: cpp-httplib +URL: https://github.com/yhirose/cpp-httplib +Revision: 4320d7ba3e8b7388e1443eb54c039a1304cf7a6b +License: MIT +License File: cpp-httplib/LICENSE +Security Critical: no (test only) + +Description: +A C++11 header-only HTTP library. + +Local Modifications: +- Exclude test/ and example/ subdirs. +- Patch httplib.h to use #include "third_party/zlib/zlib_crashpad.h" instead of + . diff --git a/third_party/cpp-httplib/cpp-httplib/LICENSE b/third_party/cpp-httplib/cpp-httplib/LICENSE new file mode 100644 index 00000000..3e5ed359 --- /dev/null +++ b/third_party/cpp-httplib/cpp-httplib/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2017 yhirose + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/third_party/cpp-httplib/cpp-httplib/README.md b/third_party/cpp-httplib/cpp-httplib/README.md new file mode 100644 index 00000000..291e4bd7 --- /dev/null +++ b/third_party/cpp-httplib/cpp-httplib/README.md @@ -0,0 +1,188 @@ +cpp-httplib +=========== + +A C++11 header-only HTTP library. + +It's extremely easy to setup. Just include **httplib.h** file in your code! + +Inspired by [Sinatra](http://www.sinatrarb.com/) and [express](https://github.com/visionmedia/express). + +Server Example +-------------- + +```c++ +#include + +int main(void) +{ + using namespace httplib; + + Server svr; + + svr.get("/hi", [](const Request& req, Response& res) { + res.set_content("Hello World!", "text/plain"); + }); + + svr.get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) { + auto numbers = req.matches[1]; + res.set_content(numbers, "text/plain"); + }); + + svr.listen("localhost", 1234); +} +``` + +### Method Chain + +```cpp +svr.get("/get", [](const auto& req, auto& res) { + res.set_content("get", "text/plain"); + }) + .post("/post", [](const auto& req, auto& res) { + res.set_content(req.body(), "text/plain"); + }) + .listen("localhost", 1234); +``` + +### Static File Server + +```cpp +svr.set_base_dir("./www"); +``` + +### Logging + +```cpp +svr.set_logger([](const auto& req, const auto& res) { + your_logger(req, res); +}); +``` + +### Error Handler + +```cpp +svr.set_error_handler([](const auto& req, auto& res) { + const char* fmt = "

Error Status: %d

"; + char buf[BUFSIZ]; + snprintf(buf, sizeof(buf), fmt, res.status); + res.set_content(buf, "text/html"); +}); +``` + +### 'multipart/form-data' POST data + +```cpp +svr.post("/multipart", [&](const auto& req, auto& res) { + auto size = req.files.size(); + auto ret = req.has_file("name1")); + const auto& file = req.get_file_value("name1"); + // file.filename; + // file.content_type; + auto body = req.body.substr(file.offset, file.length)); +}) +``` + +Client Example +-------------- + +### GET + +```c++ +#include +#include + +int main(void) +{ + httplib::Client cli("localhost", 1234); + + auto res = cli.get("/hi"); + if (res && res->status == 200) { + std::cout << res->body << std::endl; + } +} +``` + +### POST + +```c++ +res = cli.post("/post", "text", "text/plain"); +res = cli.post("/person", "name=john1¬e=coder", "application/x-www-form-urlencoded"); +``` + +### POST with parameters + +```c++ +httplib::Map params; +params["name"] = "john"; +params["note"] = "coder"; +auto res = cli.post("/post", params); +``` + +### Connection Timeout + +```c++ +httplib::Client cli("localhost", 8080, 5); // timeouts in 5 seconds +``` +### With Progress Callback + +```cpp +httplib::Client client(url, port); + +// prints: 0 / 000 bytes => 50% complete +std::shared_ptr res = + cli.get("/", [](uint64_t len, uint64_t total) { + printf("%lld / %lld bytes => %d%% complete\n", + len, total, + (int)((len/total)*100)); + } +); +``` + +![progress](https://user-images.githubusercontent.com/236374/33138910-495c4ecc-cf86-11e7-8693-2fc6d09615c4.gif) + +This feature was contributed by [underscorediscovery](https://github.com/yhirose/cpp-httplib/pull/23). + +### Range + +```cpp +httplib::Client cli("httpbin.org", 80); + +// 'Range: bytes=1-10' +httplib::Headers headers = { httplib::make_range_header(1, 10) }; + +auto res = cli.get("/range/32", headers); +// res->status should be 206. +// res->body should be "bcdefghijk". +``` + +OpenSSL Support +--------------- + +SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked. + +```c++ +#define CPPHTTPLIB_OPENSSL_SUPPORT + +SSLServer svr("./cert.pem", "./key.pem"); + +SSLClient cli("localhost", 8080); +``` + +Zlib Support +------------ + +'gzip' compression is available with `CPPHTTPLIB_ZLIB_SUPPORT`. + +The server applies gzip compression to the following MIME type contents: + + * all text types + * image/svg+xml + * application/javascript + * application/json + * application/xml + * application/xhtml+xml + +License +------- + +MIT license (© 2017 Yuji Hirose) diff --git a/third_party/cpp-httplib/cpp-httplib/httplib.h b/third_party/cpp-httplib/cpp-httplib/httplib.h new file mode 100644 index 00000000..693f430a --- /dev/null +++ b/third_party/cpp-httplib/cpp-httplib/httplib.h @@ -0,0 +1,2207 @@ +// +// httplib.h +// +// Copyright (c) 2017 Yuji Hirose. All rights reserved. +// MIT License +// + +#ifndef _CPPHTTPLIB_HTTPLIB_H_ +#define _CPPHTTPLIB_HTTPLIB_H_ + +#ifdef _WIN32 +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf_s +#endif + +#ifndef S_ISREG +#define S_ISREG(m) (((m)&S_IFREG)==S_IFREG) +#endif +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&S_IFDIR)==S_IFDIR) +#endif + +#include +#include +#include + +#undef min +#undef max + +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif + +typedef SOCKET socket_t; +#else +#include +#include +#include +#include +#include +#include +#include +#include + +typedef int socket_t; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +#include +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +#include "third_party/zlib/zlib_crashpad.h" +#endif + +/* + * Configuration + */ +#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0 + +namespace httplib +{ + +namespace detail { + +struct ci { + bool operator() (const std::string & s1, const std::string & s2) const { + return std::lexicographical_compare( + s1.begin(), s1.end(), + s2.begin(), s2.end(), + [](char c1, char c2) { + return ::tolower(c1) < ::tolower(c2); + }); + } +}; + +} // namespace detail + +enum class HttpVersion { v1_0 = 0, v1_1 }; + +typedef std::multimap Headers; + +template +std::pair make_range_header(uint64_t value, Args... args); + +typedef std::multimap Params; +typedef std::smatch Match; +typedef std::function Progress; + +struct MultipartFile { + std::string filename; + std::string content_type; + size_t offset = 0; + size_t length = 0; +}; +typedef std::multimap MultipartFiles; + +struct Request { + std::string version; + std::string method; + std::string path; + Headers headers; + std::string body; + Params params; + MultipartFiles files; + Match matches; + + Progress progress; + + bool has_header(const char* key) const; + std::string get_header_value(const char* key) const; + void set_header(const char* key, const char* val); + + bool has_param(const char* key) const; + std::string get_param_value(const char* key) const; + + bool has_file(const char* key) const; + MultipartFile get_file_value(const char* key) const; +}; + +struct Response { + std::string version; + int status; + Headers headers; + std::string body; + + bool has_header(const char* key) const; + std::string get_header_value(const char* key) const; + void set_header(const char* key, const char* val); + + void set_redirect(const char* url); + void set_content(const char* s, size_t n, const char* content_type); + void set_content(const std::string& s, const char* content_type); + + Response() : status(-1) {} +}; + +class Stream { +public: + virtual ~Stream() {} + virtual int read(char* ptr, size_t size) = 0; + virtual int write(const char* ptr, size_t size1) = 0; + virtual int write(const char* ptr) = 0; + virtual std::string get_remote_addr() = 0; + + template + void write_format(const char* fmt, const Args& ...args); +}; + +class SocketStream : public Stream { +public: + SocketStream(socket_t sock); + virtual ~SocketStream(); + + virtual int read(char* ptr, size_t size); + virtual int write(const char* ptr, size_t size); + virtual int write(const char* ptr); + virtual std::string get_remote_addr(); + +private: + socket_t sock_; +}; + +class Server { +public: + typedef std::function Handler; + typedef std::function Logger; + + Server(HttpVersion http_version = HttpVersion::v1_0); + + virtual ~Server(); + + virtual bool is_valid() const; + + Server& get(const char* pattern, Handler handler); + Server& post(const char* pattern, Handler handler); + + bool set_base_dir(const char* path); + + void set_error_handler(Handler handler); + void set_logger(Logger logger); + + int bind_to_any_port(const char* host, int socket_flags = 0); + bool listen_after_bind(); + + bool listen(const char* host, int port, int socket_flags = 0); + + bool is_running() const; + void stop(); + +protected: + bool process_request(Stream& strm, bool last_connection); + + const HttpVersion http_version_; + +private: + typedef std::vector> Handlers; + + socket_t create_server_socket(const char* host, int port, int socket_flags) const; + int bind_internal(const char* host, int port, int socket_flags); + bool listen_internal(); + + bool routing(Request& req, Response& res); + bool handle_file_request(Request& req, Response& res); + bool dispatch_request(Request& req, Response& res, Handlers& handlers); + + bool parse_request_line(const char* s, Request& req); + void write_response(Stream& strm, bool last_connection, const Request& req, Response& res); + + virtual bool read_and_close_socket(socket_t sock); + + bool is_running_; + socket_t svr_sock_; + std::string base_dir_; + Handlers get_handlers_; + Handlers post_handlers_; + Handler error_handler_; + Logger logger_; + + // TODO: Use thread pool... + std::mutex running_threads_mutex_; + int running_threads_; +}; + +class Client { +public: + Client( + const char* host, + int port = 80, + size_t timeout_sec = 300, + HttpVersion http_version = HttpVersion::v1_0); + + virtual ~Client(); + + virtual bool is_valid() const; + + std::shared_ptr get(const char* path, Progress progress = nullptr); + std::shared_ptr get(const char* path, const Headers& headers, Progress progress = nullptr); + + std::shared_ptr head(const char* path); + std::shared_ptr head(const char* path, const Headers& headers); + + std::shared_ptr post(const char* path, const std::string& body, const char* content_type); + std::shared_ptr post(const char* path, const Headers& headers, const std::string& body, const char* content_type); + + std::shared_ptr post(const char* path, const Params& params); + std::shared_ptr post(const char* path, const Headers& headers, const Params& params); + + bool send(Request& req, Response& res); + +protected: + bool process_request(Stream& strm, Request& req, Response& res); + + const std::string host_; + const int port_; + size_t timeout_sec_; + const HttpVersion http_version_; + const std::string host_and_port_; + +private: + socket_t create_client_socket() const; + bool read_response_line(Stream& strm, Response& res); + void write_request(Stream& strm, Request& req); + + virtual bool read_and_close_socket(socket_t sock, Request& req, Response& res); +}; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +class SSLSocketStream : public Stream { +public: + SSLSocketStream(socket_t sock, SSL* ssl); + virtual ~SSLSocketStream(); + + virtual int read(char* ptr, size_t size); + virtual int write(const char* ptr, size_t size); + virtual int write(const char* ptr); + virtual std::string get_remote_addr(); + +private: + socket_t sock_; + SSL* ssl_; +}; + +class SSLServer : public Server { +public: + SSLServer( + const char* cert_path, const char* private_key_path, + HttpVersion http_version = HttpVersion::v1_0); + + virtual ~SSLServer(); + + virtual bool is_valid() const; + +private: + virtual bool read_and_close_socket(socket_t sock); + + SSL_CTX* ctx_; +}; + +class SSLClient : public Client { +public: + SSLClient( + const char* host, + int port = 80, + size_t timeout_sec = 300, + HttpVersion http_version = HttpVersion::v1_0); + + virtual ~SSLClient(); + + virtual bool is_valid() const; + +private: + virtual bool read_and_close_socket(socket_t sock, Request& req, Response& res); + + SSL_CTX* ctx_; +}; +#endif + +/* + * Implementation + */ +namespace detail { + +static std::vector http_version_strings = { "HTTP/1.0", "HTTP/1.1" }; + +template +void split(const char* b, const char* e, char d, Fn fn) +{ + int i = 0; + int beg = 0; + + while (e ? (b + i != e) : (b[i] != '\0')) { + if (b[i] == d) { + fn(&b[beg], &b[i]); + beg = i + 1; + } + i++; + } + + if (i) { + fn(&b[beg], &b[i]); + } +} + +// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` +// to store data. The call can set memory on stack for performance. +class stream_line_reader { +public: + stream_line_reader(Stream& strm, char* fixed_buffer, size_t fixed_buffer_size) + : strm_(strm) + , fixed_buffer_(fixed_buffer) + , fixed_buffer_size_(fixed_buffer_size) { + } + + const char* ptr() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_; + } else { + return glowable_buffer_.data(); + } + } + + bool getline() { + fixed_buffer_used_size_ = 0; + glowable_buffer_.clear(); + + for (size_t i = 0; ; i++) { + char byte; + auto n = strm_.read(&byte, 1); + + if (n < 0) { + return false; + } else if (n == 0) { + if (i == 0) { + return false; + } else { + break; + } + } + + append(byte); + + if (byte == '\n') { + break; + } + } + + return true; + } + +private: + void append(char c) { + if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { + fixed_buffer_[fixed_buffer_used_size_++] = c; + fixed_buffer_[fixed_buffer_used_size_] = '\0'; + } else { + if (glowable_buffer_.empty()) { + assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); + glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); + } + glowable_buffer_ += c; + } + } + + Stream& strm_; + char* fixed_buffer_; + const size_t fixed_buffer_size_; + size_t fixed_buffer_used_size_; + std::string glowable_buffer_; +}; + +inline int close_socket(socket_t sock) +{ +#ifdef _WIN32 + return closesocket(sock); +#else + return close(sock); +#endif +} + +inline int select_read(socket_t sock, size_t sec, size_t usec) +{ + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = sec; + tv.tv_usec = usec; + + return select(sock + 1, &fds, NULL, NULL, &tv); +} + +inline bool wait_until_socket_is_ready(socket_t sock, size_t sec, size_t usec) +{ + fd_set fdsr; + FD_ZERO(&fdsr); + FD_SET(sock, &fdsr); + + auto fdsw = fdsr; + auto fdse = fdsr; + + timeval tv; + tv.tv_sec = sec; + tv.tv_usec = usec; + + if (select(sock + 1, &fdsr, &fdsw, &fdse, &tv) < 0) { + return false; + } else if (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw)) { + int error = 0; + socklen_t len = sizeof(error); + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0 || error) { + return false; + } + } else { + return false; + } + + return true; +} + +template +inline bool read_and_close_socket(socket_t sock, bool keep_alive, T callback) +{ + bool ret = false; + + if (keep_alive) { + auto count = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; + while (count > 0 && + detail::select_read(sock, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { + auto last_connection = count == 1; + SocketStream strm(sock); + ret = callback(strm, last_connection); + if (!ret) { + break; + } + count--; + } + } else { + SocketStream strm(sock); + ret = callback(strm, true); + } + + close_socket(sock); + return ret; +} + +inline int shutdown_socket(socket_t sock) +{ +#ifdef _WIN32 + return shutdown(sock, SD_BOTH); +#else + return shutdown(sock, SHUT_RDWR); +#endif +} + +template +socket_t create_socket(const char* host, int port, Fn fn, int socket_flags = 0) +{ +#ifdef _WIN32 +#define SO_SYNCHRONOUS_NONALERT 0x20 +#define SO_OPENTYPE 0x7008 + + int opt = SO_SYNCHRONOUS_NONALERT; + setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char*)&opt, sizeof(opt)); +#endif + + // Get address info + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = socket_flags; + hints.ai_protocol = 0; + + auto service = std::to_string(port); + + if (getaddrinfo(host, service.c_str(), &hints, &result)) { + return -1; + } + + for (auto rp = result; rp; rp = rp->ai_next) { + // Create a socket + auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sock == -1) { + continue; + } + + // Make 'reuse address' option available + int yes = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)); + + // bind or connect + if (fn(sock, *rp)) { + freeaddrinfo(result); + return sock; + } + + close_socket(sock); + } + + freeaddrinfo(result); + return -1; +} + +inline void set_nonblocking(socket_t sock, bool nonblocking) +{ +#ifdef _WIN32 + auto flags = nonblocking ? 1UL : 0UL; + ioctlsocket(sock, FIONBIO, &flags); +#else + auto flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); +#endif +} + +inline bool is_connection_error() +{ +#ifdef _WIN32 + return WSAGetLastError() != WSAEWOULDBLOCK; +#else + return errno != EINPROGRESS; +#endif +} + +inline std::string get_remote_addr(socket_t sock) { + struct sockaddr_storage addr; + char ipstr[INET6_ADDRSTRLEN]; + socklen_t len = sizeof(addr); + getpeername(sock, (struct sockaddr*)&addr, &len); + + // deal with both IPv4 and IPv6: + if (addr.ss_family == AF_INET) { + auto s = (struct sockaddr_in *)&addr; + inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof(ipstr)); + } else { // AF_INET6 + auto s = (struct sockaddr_in6 *)&addr; + inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof(ipstr)); + } + + return ipstr; +} + +inline bool is_file(const std::string& path) +{ + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode); +} + +inline bool is_dir(const std::string& path) +{ + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode); +} + +inline bool is_valid_path(const std::string& path) { + size_t level = 0; + size_t i = 0; + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + + while (i < path.size()) { + // Read component + auto beg = i; + while (i < path.size() && path[i] != '/') { + i++; + } + + auto len = i - beg; + assert(len > 0); + + if (!path.compare(beg, len, ".")) { + ; + } else if (!path.compare(beg, len, "..")) { + if (level == 0) { + return false; + } + level--; + } else { + level++; + } + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + } + + return true; +} + +inline void read_file(const std::string& path, std::string& out) +{ + std::ifstream fs(path, std::ios_base::binary); + fs.seekg(0, std::ios_base::end); + auto size = fs.tellg(); + fs.seekg(0); + out.resize(static_cast(size)); + fs.read(&out[0], size); +} + +inline std::string file_extension(const std::string& path) +{ + std::smatch m; + auto pat = std::regex("\\.([a-zA-Z0-9]+)$"); + if (std::regex_search(path, m, pat)) { + return m[1].str(); + } + return std::string(); +} + +inline const char* find_content_type(const std::string& path) +{ + auto ext = file_extension(path); + if (ext == "txt") { + return "text/plain"; + } else if (ext == "html") { + return "text/html"; + } else if (ext == "css") { + return "text/css"; + } else if (ext == "jpeg" || ext == "jpg") { + return "image/jpg"; + } else if (ext == "png") { + return "image/png"; + } else if (ext == "gif") { + return "image/gif"; + } else if (ext == "svg") { + return "image/svg+xml"; + } else if (ext == "ico") { + return "image/x-icon"; + } else if (ext == "json") { + return "application/json"; + } else if (ext == "pdf") { + return "application/pdf"; + } else if (ext == "js") { + return "application/javascript"; + } else if (ext == "xml") { + return "application/xml"; + } else if (ext == "xhtml") { + return "application/xhtml+xml"; + } + return nullptr; +} + +inline const char* status_message(int status) +{ + switch (status) { + case 200: return "OK"; + case 400: return "Bad Request"; + case 404: return "Not Found"; + case 415: return "Unsupported Media Type"; + default: + case 500: return "Internal Server Error"; + } +} + +inline const char* get_header_value(const Headers& headers, const char* key, const char* def) +{ + auto it = headers.find(key); + if (it != headers.end()) { + return it->second.c_str(); + } + return def; +} + +inline int get_header_value_int(const Headers& headers, const char* key, int def) +{ + auto it = headers.find(key); + if (it != headers.end()) { + return std::stoi(it->second); + } + return def; +} + +inline bool read_headers(Stream& strm, Headers& headers) +{ + static std::regex re(R"((.+?):\s*(.+?)\s*\r\n)"); + + const auto bufsiz = 2048; + char buf[bufsiz]; + + stream_line_reader reader(strm, buf, bufsiz); + + for (;;) { + if (!reader.getline()) { + return false; + } + if (!strcmp(reader.ptr(), "\r\n")) { + break; + } + std::cmatch m; + if (std::regex_match(reader.ptr(), m, re)) { + auto key = std::string(m[1]); + auto val = std::string(m[2]); + headers.emplace(key, val); + } + } + + return true; +} + +bool read_content_with_length(Stream& strm, std::string& out, size_t len, Progress progress) +{ + out.assign(len, 0); + size_t r = 0; + while (r < len){ + auto n = strm.read(&out[r], len - r); + if (n <= 0) { + return false; + } + + r += n; + + if (progress) { + progress(r, len); + } + } + + return true; +} + +bool read_content_without_length(Stream& strm, std::string& out) +{ + for (;;) { + char byte; + auto n = strm.read(&byte, 1); + if (n < 0) { + return false; + } else if (n == 0) { + return true; + } + out += byte; + } + + return true; +} + +bool read_content_chunked(Stream& strm, std::string& out) +{ + const auto bufsiz = 16; + char buf[bufsiz]; + + stream_line_reader reader(strm, buf, bufsiz); + + if (!reader.getline()) { + return false; + } + + auto chunk_len = std::stoi(reader.ptr(), 0, 16); + + while (chunk_len > 0){ + std::string chunk; + if (!read_content_with_length(strm, chunk, chunk_len, nullptr)) { + return false; + } + + if (!reader.getline()) { + return false; + } + + if (strcmp(reader.ptr(), "\r\n")) { + break; + } + + out += chunk; + + if (!reader.getline()) { + return false; + } + + chunk_len = std::stoi(reader.ptr(), 0, 16); + } + + if (chunk_len == 0) { + // Reader terminator after chunks + if (!reader.getline() || strcmp(reader.ptr(), "\r\n")) + return false; + } + + return true; +} + +template +bool read_content(Stream& strm, T& x, Progress progress = Progress()) +{ + auto len = get_header_value_int(x.headers, "Content-Length", 0); + + if (len) { + return read_content_with_length(strm, x.body, len, progress); + } else { + const auto& encoding = get_header_value(x.headers, "Transfer-Encoding", ""); + + if (!strcasecmp(encoding, "chunked")) { + return read_content_chunked(strm, x.body); + } else { + return read_content_without_length(strm, x.body); + } + } + + return true; +} + +template +inline void write_headers(Stream& strm, const T& info) +{ + for (const auto& x: info.headers) { + strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + } + strm.write("\r\n"); +} + +inline std::string encode_url(const std::string& s) +{ + std::string result; + + for (auto i = 0; s[i]; i++) { + switch (s[i]) { + case ' ': result += "+"; break; + case '\'': result += "%27"; break; + case ',': result += "%2C"; break; + case ':': result += "%3A"; break; + case ';': result += "%3B"; break; + default: + if (s[i] < 0) { + result += '%'; + char hex[4]; + size_t len = snprintf(hex, sizeof(hex) - 1, "%02X", (unsigned char)s[i]); + assert(len == 2); + result.append(hex, len); + } else { + result += s[i]; + } + break; + } + } + + return result; +} + +inline bool is_hex(char c, int& v) +{ + if (0x20 <= c && isdigit(c)) { + v = c - '0'; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; + return true; + } + return false; +} + +inline bool from_hex_to_i(const std::string& s, int i, int cnt, int& val) +{ + val = 0; + for (; cnt; i++, cnt--) { + if (!s[i]) { + return false; + } + int v = 0; + if (is_hex(s[i], v)) { + val = val * 16 + v; + } else { + return false; + } + } + return true; +} + +inline size_t to_utf8(int code, char* buff) +{ + if (code < 0x0080) { + buff[0] = (code & 0x7F); + return 1; + } else if (code < 0x0800) { + buff[0] = (0xC0 | ((code >> 6) & 0x1F)); + buff[1] = (0x80 | (code & 0x3F)); + return 2; + } else if (code < 0xD800) { + buff[0] = (0xE0 | ((code >> 12) & 0xF)); + buff[1] = (0x80 | ((code >> 6) & 0x3F)); + buff[2] = (0x80 | (code & 0x3F)); + return 3; + } else if (code < 0xE000) { // D800 - DFFF is invalid... + return 0; + } else if (code < 0x10000) { + buff[0] = (0xE0 | ((code >> 12) & 0xF)); + buff[1] = (0x80 | ((code >> 6) & 0x3F)); + buff[2] = (0x80 | (code & 0x3F)); + return 3; + } else if (code < 0x110000) { + buff[0] = (0xF0 | ((code >> 18) & 0x7)); + buff[1] = (0x80 | ((code >> 12) & 0x3F)); + buff[2] = (0x80 | ((code >> 6) & 0x3F)); + buff[3] = (0x80 | (code & 0x3F)); + return 4; + } + + // NOTREACHED + return 0; +} + +inline std::string decode_url(const std::string& s) +{ + std::string result; + + for (int i = 0; s[i]; i++) { + if (s[i] == '%') { + if (s[i + 1] && s[i + 1] == 'u') { + int val = 0; + if (from_hex_to_i(s, i + 2, 4, val)) { + // 4 digits Unicode codes + char buff[4]; + size_t len = to_utf8(val, buff); + if (len > 0) { + result.append(buff, len); + } + i += 5; // 'u0000' + } else { + result += s[i]; + } + } else { + int val = 0; + if (from_hex_to_i(s, i + 1, 2, val)) { + // 2 digits hex codes + result += val; + i += 2; // '00' + } else { + result += s[i]; + } + } + } else if (s[i] == '+') { + result += ' '; + } else { + result += s[i]; + } + } + + return result; +} + +inline void parse_query_text(const std::string& s, Params& params) +{ + split(&s[0], &s[s.size()], '&', [&](const char* b, const char* e) { + std::string key; + std::string val; + split(b, e, '=', [&](const char* b, const char* e) { + if (key.empty()) { + key.assign(b, e); + } else { + val.assign(b, e); + } + }); + params.emplace(key, decode_url(val)); + }); +} + +inline bool parse_multipart_boundary(const std::string& content_type, std::string& boundary) +{ + auto pos = content_type.find("boundary="); + if (pos == std::string::npos) { + return false; + } + + boundary = content_type.substr(pos + 9); + return true; +} + +inline bool parse_multipart_formdata( + const std::string& boundary, const std::string& body, MultipartFiles& files) +{ + static std::string dash = "--"; + static std::string crlf = "\r\n"; + + static std::regex re_content_type( + "Content-Type: (.*?)", std::regex_constants::icase); + + static std::regex re_content_disposition( + "Content-Disposition: form-data; name=\"(.*?)\"(?:; filename=\"(.*?)\")?", + std::regex_constants::icase); + + auto dash_boundary = dash + boundary; + + auto pos = body.find(dash_boundary); + if (pos != 0) { + return false; + } + + pos += dash_boundary.size(); + + auto next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { + return false; + } + + pos = next_pos + crlf.size(); + + while (pos < body.size()) { + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { + return false; + } + + std::string name; + MultipartFile file; + + auto header = body.substr(pos, (next_pos - pos)); + + while (pos != next_pos) { + std::smatch m; + if (std::regex_match(header, m, re_content_type)) { + file.content_type = m[1]; + } else if (std::regex_match(header, m, re_content_disposition)) { + name = m[1]; + file.filename = m[2]; + } + + pos = next_pos + crlf.size(); + + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { + return false; + } + + header = body.substr(pos, (next_pos - pos)); + } + + pos = next_pos + crlf.size(); + + next_pos = body.find(crlf + dash_boundary, pos); + + if (next_pos == std::string::npos) { + return false; + } + + file.offset = pos; + file.length = next_pos - pos; + + pos = next_pos + crlf.size() + dash_boundary.size(); + + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { + return false; + } + + files.emplace(name, file); + + pos = next_pos + crlf.size(); + } + + return true; +} + +inline std::string to_lower(const char* beg, const char* end) +{ + std::string out; + auto it = beg; + while (it != end) { + out += ::tolower(*it); + it++; + } + return out; +} + +inline void make_range_header_core(std::string&) {} + +template +inline void make_range_header_core(std::string& field, uint64_t value) +{ + if (!field.empty()) { + field += ", "; + } + field += std::to_string(value) + "-"; +} + +template +inline void make_range_header_core(std::string& field, uint64_t value1, uint64_t value2, Args... args) +{ + if (!field.empty()) { + field += ", "; + } + field += std::to_string(value1) + "-" + std::to_string(value2); + make_range_header_core(field, args...); +} + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +inline bool can_compress(const std::string& content_type) { + return !content_type.find("text/") || + content_type == "image/svg+xml" || + content_type == "application/javascript" || + content_type == "application/json" || + content_type == "application/xml" || + content_type == "application/xhtml+xml"; +} + +inline void compress(const Request& req, Response& res) +{ + // TODO: Server version is HTTP/1.1 and 'Accpet-Encoding' has gzip, not gzip;q=0 + const auto& encodings = req.get_header_value("Accept-Encoding"); + if (encodings.find("gzip") == std::string::npos) { + return; + } + + if (!can_compress(res.get_header_value("Content-Type"))) { + return; + } + + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY); + if (ret != Z_OK) { + return; + } + + strm.avail_in = res.body.size(); + strm.next_in = (Bytef *)res.body.data(); + + std::string compressed; + + const auto bufsiz = 16384; + char buff[bufsiz]; + do { + strm.avail_out = bufsiz; + strm.next_out = (Bytef *)buff; + deflate(&strm, Z_FINISH); + compressed.append(buff, bufsiz - strm.avail_out); + } while (strm.avail_out == 0); + + res.set_header("Content-Encoding", "gzip"); + res.body.swap(compressed); + + deflateEnd(&strm); +} + +inline void decompress_request_body(Request& req) +{ + if (req.get_header_value("Content-Encoding") != "gzip") + return; + + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + // 15 is the value of wbits, which should be at the maximum possible value to ensure + // that any gzip stream can be decoded. The offset of 16 specifies that the stream + // to decompress will be formatted with a gzip wrapper. + auto ret = inflateInit2(&strm, 16 + 15); + if (ret != Z_OK) { + return; + } + + strm.avail_in = req.body.size(); + strm.next_in = (Bytef *)req.body.data(); + + std::string decompressed; + + const auto bufsiz = 16384; + char buff[bufsiz]; + do { + strm.avail_out = bufsiz; + strm.next_out = (Bytef *)buff; + inflate(&strm, Z_NO_FLUSH); + decompressed.append(buff, bufsiz - strm.avail_out); + } while (strm.avail_out == 0); + + req.body.swap(decompressed); + + inflateEnd(&strm); +} +#endif + +#ifdef _WIN32 +class WSInit { +public: + WSInit() { + WSADATA wsaData; + WSAStartup(0x0002, &wsaData); + } + + ~WSInit() { + WSACleanup(); + } +}; + +static WSInit wsinit_; +#endif + +} // namespace detail + +// Header utilities +template +inline std::pair make_range_header(uint64_t value, Args... args) +{ + std::string field; + detail::make_range_header_core(field, value, args...); + field.insert(0, "bytes="); + return std::make_pair("Range", field); +} + +// Request implementation +inline bool Request::has_header(const char* key) const +{ + return headers.find(key) != headers.end(); +} + +inline std::string Request::get_header_value(const char* key) const +{ + return detail::get_header_value(headers, key, ""); +} + +inline void Request::set_header(const char* key, const char* val) +{ + headers.emplace(key, val); +} + +inline bool Request::has_param(const char* key) const +{ + return params.find(key) != params.end(); +} + +inline std::string Request::get_param_value(const char* key) const +{ + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return std::string(); +} + +inline bool Request::has_file(const char* key) const +{ + return files.find(key) != files.end(); +} + +inline MultipartFile Request::get_file_value(const char* key) const +{ + auto it = files.find(key); + if (it != files.end()) { + return it->second; + } + return MultipartFile(); +} + +// Response implementation +inline bool Response::has_header(const char* key) const +{ + return headers.find(key) != headers.end(); +} + +inline std::string Response::get_header_value(const char* key) const +{ + return detail::get_header_value(headers, key, ""); +} + +inline void Response::set_header(const char* key, const char* val) +{ + headers.emplace(key, val); +} + +inline void Response::set_redirect(const char* url) +{ + set_header("Location", url); + status = 302; +} + +inline void Response::set_content(const char* s, size_t n, const char* content_type) +{ + body.assign(s, n); + set_header("Content-Type", content_type); +} + +inline void Response::set_content(const std::string& s, const char* content_type) +{ + body = s; + set_header("Content-Type", content_type); +} + +// Rstream implementation +template +inline void Stream::write_format(const char* fmt, const Args& ...args) +{ + const auto bufsiz = 2048; + char buf[bufsiz]; + +#if defined(_MSC_VER) && _MSC_VER < 1900 + auto n = _snprintf_s(buf, bufsiz, bufsiz - 1, fmt, args...); +#else + auto n = snprintf(buf, bufsiz - 1, fmt, args...); +#endif + if (n > 0) { + if (n >= bufsiz - 1) { + std::vector glowable_buf(bufsiz); + + while (n >= static_cast(glowable_buf.size() - 1)) { + glowable_buf.resize(glowable_buf.size() * 2); +#if defined(_MSC_VER) && _MSC_VER < 1900 + n = _snprintf_s(&glowable_buf[0], glowable_buf.size(), glowable_buf.size() - 1, fmt, args...); +#else + n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...); +#endif + } + write(&glowable_buf[0], n); + } else { + write(buf, n); + } + } +} + +// Socket stream implementation +inline SocketStream::SocketStream(socket_t sock): sock_(sock) +{ +} + +inline SocketStream::~SocketStream() +{ +} + +inline int SocketStream::read(char* ptr, size_t size) +{ + return recv(sock_, ptr, size, 0); +} + +inline int SocketStream::write(const char* ptr, size_t size) +{ + return send(sock_, ptr, size, 0); +} + +inline int SocketStream::write(const char* ptr) +{ + return write(ptr, strlen(ptr)); +} + +inline std::string SocketStream::get_remote_addr() { + return detail::get_remote_addr(sock_); +} + +// HTTP server implementation +inline Server::Server(HttpVersion http_version) + : http_version_(http_version) + , is_running_(false) + , svr_sock_(-1) + , running_threads_(0) +{ +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif +} + +inline Server::~Server() +{ +} + +inline Server& Server::get(const char* pattern, Handler handler) +{ + get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server& Server::post(const char* pattern, Handler handler) +{ + post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline bool Server::set_base_dir(const char* path) +{ + if (detail::is_dir(path)) { + base_dir_ = path; + return true; + } + return false; +} + +inline void Server::set_error_handler(Handler handler) +{ + error_handler_ = handler; +} + +inline void Server::set_logger(Logger logger) +{ + logger_ = logger; +} + +inline int Server::bind_to_any_port(const char* host, int socket_flags) +{ + return bind_internal(host, 0, socket_flags); +} + +inline bool Server::listen_after_bind() { + return listen_internal(); +} + +inline bool Server::listen(const char* host, int port, int socket_flags) +{ + if (bind_internal(host, port, socket_flags) < 0) + return false; + return listen_internal(); +} + +inline bool Server::is_running() const +{ + return is_running_; +} + +inline void Server::stop() +{ + if (is_running_) { + assert(svr_sock_ != -1); + detail::shutdown_socket(svr_sock_); + detail::close_socket(svr_sock_); + svr_sock_ = -1; + } +} + +inline bool Server::parse_request_line(const char* s, Request& req) +{ + static std::regex re("(GET|HEAD|POST) ([^?]+)(?:\\?(.+?))? (HTTP/1\\.[01])\r\n"); + + std::cmatch m; + if (std::regex_match(s, m, re)) { + req.version = std::string(m[4]); + req.method = std::string(m[1]); + req.path = detail::decode_url(m[2]); + + // Parse query text + auto len = std::distance(m[3].first, m[3].second); + if (len > 0) { + detail::parse_query_text(m[3], req.params); + } + + return true; + } + + return false; +} + +inline void Server::write_response(Stream& strm, bool last_connection, const Request& req, Response& res) +{ + assert(res.status != -1); + + if (400 <= res.status && error_handler_) { + error_handler_(req, res); + } + + // Response line + strm.write_format("%s %d %s\r\n", + detail::http_version_strings[static_cast(http_version_)], + res.status, + detail::status_message(res.status)); + + // Headers + if (!res.has_header("Connection") && (last_connection || req.version == "HTTP/1.0")) { + res.set_header("Connection", "close"); + } + + if (!res.body.empty()) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + detail::compress(req, res); +#endif + + if (!res.has_header("Content-Type")) { + res.set_header("Content-Type", "text/plain"); + } + + auto length = std::to_string(res.body.size()); + res.set_header("Content-Length", length.c_str()); + } + + detail::write_headers(strm, res); + + // Body + if (!res.body.empty() && req.method != "HEAD") { + strm.write(res.body.c_str(), res.body.size()); + } + + // Log + if (logger_) { + logger_(req, res); + } +} + +inline bool Server::handle_file_request(Request& req, Response& res) +{ + if (!base_dir_.empty() && detail::is_valid_path(req.path)) { + std::string path = base_dir_ + req.path; + + if (!path.empty() && path.back() == '/') { + path += "index.html"; + } + + if (detail::is_file(path)) { + detail::read_file(path, res.body); + auto type = detail::find_content_type(path); + if (type) { + res.set_header("Content-Type", type); + } + res.status = 200; + return true; + } + } + + return false; +} + +inline socket_t Server::create_server_socket(const char* host, int port, int socket_flags) const +{ + return detail::create_socket(host, port, + [](socket_t sock, struct addrinfo& ai) -> bool { + if (::bind(sock, ai.ai_addr, ai.ai_addrlen)) { + return false; + } + if (::listen(sock, 5)) { // Listen through 5 channels + return false; + } + return true; + }, socket_flags); +} + +inline int Server::bind_internal(const char* host, int port, int socket_flags) +{ + if (!is_valid()) { + return -1; + } + + svr_sock_ = create_server_socket(host, port, socket_flags); + if (svr_sock_ == -1) { + return -1; + } + + if (port == 0) { + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + if (getsockname(svr_sock_, reinterpret_cast(&sin), &len) == -1) { + return -1; + } + return ntohs(sin.sin_port); + } else { + return port; + } +} + +inline bool Server::listen_internal() +{ + auto ret = true; + + is_running_ = true; + + for (;;) { + auto val = detail::select_read(svr_sock_, 0, 100000); + + if (val == 0) { // Timeout + if (svr_sock_ == -1) { + // The server socket was closed by 'stop' method. + break; + } + continue; + } + + socket_t sock = accept(svr_sock_, NULL, NULL); + + if (sock == -1) { + if (svr_sock_ != -1) { + detail::close_socket(svr_sock_); + ret = false; + } else { + ; // The server socket was closed by user. + } + break; + } + + // TODO: Use thread pool... + std::thread([=]() { + { + std::lock_guard guard(running_threads_mutex_); + running_threads_++; + } + + read_and_close_socket(sock); + + { + std::lock_guard guard(running_threads_mutex_); + running_threads_--; + } + }).detach(); + } + + // TODO: Use thread pool... + for (;;) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + std::lock_guard guard(running_threads_mutex_); + if (!running_threads_) { + break; + } + } + + is_running_ = false; + + return ret; +} + +inline bool Server::routing(Request& req, Response& res) +{ + if (req.method == "GET" && handle_file_request(req, res)) { + return true; + } + + if (req.method == "GET" || req.method == "HEAD") { + return dispatch_request(req, res, get_handlers_); + } else if (req.method == "POST") { + return dispatch_request(req, res, post_handlers_); + } + return false; +} + +inline bool Server::dispatch_request(Request& req, Response& res, Handlers& handlers) +{ + for (const auto& x: handlers) { + const auto& pattern = x.first; + const auto& handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res); + return true; + } + } + return false; +} + +inline bool Server::process_request(Stream& strm, bool last_connection) +{ + const auto bufsiz = 2048; + char buf[bufsiz]; + + detail::stream_line_reader reader(strm, buf, bufsiz); + + // Connection has been closed on client + if (!reader.getline()) { + return false; + } + + Request req; + Response res; + + res.version = detail::http_version_strings[static_cast(http_version_)]; + + // Request line and headers + if (!parse_request_line(reader.ptr(), req) || !detail::read_headers(strm, req.headers)) { + res.status = 400; + write_response(strm, last_connection, req, res); + return true; + } + + auto ret = true; + if (req.get_header_value("Connection") == "close") { + ret = false; + } + + req.set_header("REMOTE_ADDR", strm.get_remote_addr().c_str()); + + // Body + if (req.method == "POST") { + if (!detail::read_content(strm, req)) { + res.status = 400; + write_response(strm, last_connection, req, res); + return ret; + } + + const auto& content_type = req.get_header_value("Content-Type"); + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + detail::decompress_request_body(req); +#else + if (req.get_header_value("Content-Encoding") == "gzip") { + res.status = 415; + write_response(strm, last_connection, req, res); + return ret; + } +#endif + + if (!content_type.find("application/x-www-form-urlencoded")) { + detail::parse_query_text(req.body, req.params); + } else if(!content_type.find("multipart/form-data")) { + std::string boundary; + if (!detail::parse_multipart_boundary(content_type, boundary) || + !detail::parse_multipart_formdata(boundary, req.body, req.files)) { + res.status = 400; + write_response(strm, last_connection, req, res); + return ret; + } + } + } + + if (routing(req, res)) { + if (res.status == -1) { + res.status = 200; + } + } else { + res.status = 404; + } + + write_response(strm, last_connection, req, res); + return ret; +} + +inline bool Server::is_valid() const +{ + return true; +} + +inline bool Server::read_and_close_socket(socket_t sock) +{ + auto keep_alive = http_version_ == HttpVersion::v1_1; + + return detail::read_and_close_socket( + sock, + keep_alive, + [this](Stream& strm, bool last_connection) { + return process_request(strm, last_connection); + }); +} + +// HTTP client implementation +inline Client::Client( + const char* host, int port, size_t timeout_sec, HttpVersion http_version) + : host_(host) + , port_(port) + , timeout_sec_(timeout_sec) + , http_version_(http_version) + , host_and_port_(host_ + ":" + std::to_string(port_)) +{ +} + +inline Client::~Client() +{ +} + +inline bool Client::is_valid() const +{ + return true; +} + +inline socket_t Client::create_client_socket() const +{ + return detail::create_socket(host_.c_str(), port_, + [=](socket_t sock, struct addrinfo& ai) -> bool { + detail::set_nonblocking(sock, true); + + auto ret = connect(sock, ai.ai_addr, ai.ai_addrlen); + if (ret < 0) { + if (detail::is_connection_error() || + !detail::wait_until_socket_is_ready(sock, timeout_sec_, 0)) { + detail::close_socket(sock); + return false; + } + } + + detail::set_nonblocking(sock, false); + return true; + }); +} + +inline bool Client::read_response_line(Stream& strm, Response& res) +{ + const auto bufsiz = 2048; + char buf[bufsiz]; + + detail::stream_line_reader reader(strm, buf, bufsiz); + + if (!reader.getline()) { + return false; + } + + const static std::regex re("HTTP/1\\.[01] (\\d+?) .+\r\n"); + + std::cmatch m; + if (std::regex_match(reader.ptr(), m, re)) { + res.status = std::stoi(std::string(m[1])); + } + + return true; +} + +inline bool Client::send(Request& req, Response& res) +{ + if (req.path.empty()) { + return false; + } + + auto sock = create_client_socket(); + if (sock == -1) { + return false; + } + + return read_and_close_socket(sock, req, res); +} + +inline void Client::write_request(Stream& strm, Request& req) +{ + auto path = detail::encode_url(req.path); + + // Request line + strm.write_format("%s %s %s\r\n", + req.method.c_str(), + path.c_str(), + detail::http_version_strings[static_cast(http_version_)]); + + // Headers + req.set_header("Host", host_and_port_.c_str()); + + if (!req.has_header("Accept")) { + req.set_header("Accept", "*/*"); + } + + if (!req.has_header("User-Agent")) { + req.set_header("User-Agent", "cpp-httplib/0.2"); + } + + // TODO: if (!req.has_header("Connection") && + // (last_connection || http_version_ == detail::HttpVersion::v1_0)) { + if (!req.has_header("Connection")) { + req.set_header("Connection", "close"); + } + + if (!req.body.empty()) { + if (!req.has_header("Content-Type")) { + req.set_header("Content-Type", "text/plain"); + } + + auto length = std::to_string(req.body.size()); + req.set_header("Content-Length", length.c_str()); + } + + detail::write_headers(strm, req); + + // Body + if (!req.body.empty()) { + if (req.get_header_value("Content-Type") == "application/x-www-form-urlencoded") { + auto str = detail::encode_url(req.body); + strm.write(str.c_str(), str.size()); + } else { + strm.write(req.body.c_str(), req.body.size()); + } + } +} + +inline bool Client::process_request(Stream& strm, Request& req, Response& res) +{ + // Send request + write_request(strm, req); + + // Receive response and headers + if (!read_response_line(strm, res) || !detail::read_headers(strm, res.headers)) { + return false; + } + + // TODO: Check if 'Connection' header is 'close' or HTTP version is 1.0, then close socket... + + // Body + if (req.method != "HEAD") { + if (!detail::read_content(strm, res, req.progress)) { + return false; + } + } + + return true; +} + +inline bool Client::read_and_close_socket(socket_t sock, Request& req, Response& res) +{ + return detail::read_and_close_socket(sock, false, [&](Stream& strm, bool /*last_connection*/) { + return process_request(strm, req, res); + }); +} + +inline std::shared_ptr Client::get(const char* path, Progress progress) +{ + return get(path, Headers(), progress); +} + +inline std::shared_ptr Client::get(const char* path, const Headers& headers, Progress progress) +{ + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.progress = progress; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +inline std::shared_ptr Client::head(const char* path) +{ + return head(path, Headers()); +} + +inline std::shared_ptr Client::head(const char* path, const Headers& headers) +{ + Request req; + req.method = "HEAD"; + req.headers = headers; + req.path = path; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +inline std::shared_ptr Client::post( + const char* path, const std::string& body, const char* content_type) +{ + return post(path, Headers(), body, content_type); +} + +inline std::shared_ptr Client::post( + const char* path, const Headers& headers, const std::string& body, const char* content_type) +{ + Request req; + req.method = "POST"; + req.headers = headers; + req.path = path; + + req.headers.emplace("Content-Type", content_type); + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +inline std::shared_ptr Client::post(const char* path, const Params& params) +{ + return post(path, Headers(), params); +} + +inline std::shared_ptr Client::post(const char* path, const Headers& headers, const Params& params) +{ + std::string query; + for (auto it = params.begin(); it != params.end(); ++it) { + if (it != params.begin()) { + query += "&"; + } + query += it->first; + query += "="; + query += it->second; + } + + return post(path, headers, query, "application/x-www-form-urlencoded"); +} + +/* + * SSL Implementation + */ +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +namespace detail { + +template +inline bool read_and_close_socket_ssl( + socket_t sock, bool keep_alive, + SSL_CTX* ctx, U SSL_connect_or_accept, V setup, + T callback) +{ + auto ssl = SSL_new(ctx); + if (!ssl) { + return false; + } + + auto bio = BIO_new_socket(sock, BIO_NOCLOSE); + SSL_set_bio(ssl, bio, bio); + + setup(ssl); + + SSL_connect_or_accept(ssl); + + bool ret = false; + + if (keep_alive) { + auto count = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; + while (count > 0 && + detail::select_read(sock, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { + auto last_connection = count == 1; + SSLSocketStream strm(sock, ssl); + ret = callback(strm, last_connection); + if (!ret) { + break; + } + count--; + } + } else { + SSLSocketStream strm(sock, ssl); + ret = callback(strm, true); + } + + SSL_shutdown(ssl); + SSL_free(ssl); + close_socket(sock); + return ret; +} + +class SSLInit { +public: + SSLInit() { + SSL_load_error_strings(); + SSL_library_init(); + } +}; + +static SSLInit sslinit_; + +} // namespace detail + +// SSL socket stream implementation +inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL* ssl) + : sock_(sock), ssl_(ssl) +{ +} + +inline SSLSocketStream::~SSLSocketStream() +{ +} + +inline int SSLSocketStream::read(char* ptr, size_t size) +{ + return SSL_read(ssl_, ptr, size); +} + +inline int SSLSocketStream::write(const char* ptr, size_t size) +{ + return SSL_write(ssl_, ptr, size); +} + +inline int SSLSocketStream::write(const char* ptr) +{ + return write(ptr, strlen(ptr)); +} + +inline std::string SSLSocketStream::get_remote_addr() { + return detail::get_remote_addr(sock_); +} + +// SSL HTTP server implementation +inline SSLServer::SSLServer(const char* cert_path, const char* private_key_path, HttpVersion http_version) + : Server(http_version) +{ + ctx_ = SSL_CTX_new(SSLv23_server_method()); + + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + // auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + // SSL_CTX_set_tmp_ecdh(ctx_, ecdh); + // EC_KEY_free(ecdh); + + if (SSL_CTX_use_certificate_file(ctx_, cert_path, SSL_FILETYPE_PEM) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLServer::~SSLServer() +{ + if (ctx_) { + SSL_CTX_free(ctx_); + } +} + +inline bool SSLServer::is_valid() const +{ + return ctx_; +} + +inline bool SSLServer::read_and_close_socket(socket_t sock) +{ + auto keep_alive = http_version_ == HttpVersion::v1_1; + + return detail::read_and_close_socket_ssl( + sock, + keep_alive, + ctx_, + SSL_accept, + [](SSL* /*ssl*/) {}, + [this](Stream& strm, bool last_connection) { + return process_request(strm, last_connection); + }); +} + +// SSL HTTP client implementation +inline SSLClient::SSLClient( + const char* host, int port, size_t timeout_sec, HttpVersion http_version) + : Client(host, port, timeout_sec, http_version) +{ + ctx_ = SSL_CTX_new(SSLv23_client_method()); +} + +inline SSLClient::~SSLClient() +{ + if (ctx_) { + SSL_CTX_free(ctx_); + } +} + +inline bool SSLClient::is_valid() const +{ + return ctx_; +} + +inline bool SSLClient::read_and_close_socket(socket_t sock, Request& req, Response& res) +{ + return is_valid() && detail::read_and_close_socket_ssl( + sock, false, + ctx_, SSL_connect, + [&](SSL* ssl) { + SSL_set_tlsext_host_name(ssl, host_.c_str()); + }, + [&](Stream& strm, bool /*last_connection*/) { + return process_request(strm, req, res); + }); +} +#endif + +} // namespace httplib + +#endif + +// vim: et ts=4 sw=4 cin cino={1s ff=unix diff --git a/util/BUILD.gn b/util/BUILD.gn index bb0fd6fa..5f886f20 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -448,6 +448,29 @@ static_library("util") { } } +crashpad_executable("http_transport_test_server") { + testonly = true + sources = [ + "net/http_transport_test_server.cc", + ] + + deps = [ + ":util", + "../third_party/cpp-httplib", + "../third_party/mini_chromium:base", + "../third_party/zlib", + "../tools:tool_support", + ] + + remove_configs = [ + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", + ] + + if (crashpad_is_win) { + libs = [ "ws2_32.lib" ] + } +} + source_set("util_test") { testonly = true @@ -589,7 +612,6 @@ source_set("util_test") { } data = [ - "net/http_transport_test_server.py", "net/testdata/", ] @@ -604,6 +626,10 @@ source_set("util_test") { "../third_party/zlib", ] + data_deps = [ + ":http_transport_test_server", + ] + if (crashpad_is_mac) { libs = [ "Foundation.framework" ] } @@ -613,7 +639,7 @@ source_set("util_test") { "rpcrt4.lib", "dbghelp.lib", ] - data_deps = [ + data_deps += [ ":crashpad_util_test_process_info_test_child", ":crashpad_util_test_safe_terminate_process_test_child", ] diff --git a/util/net/http_transport_test.cc b/util/net/http_transport_test.cc index fc02ba19..abaa1727 100644 --- a/util/net/http_transport_test.cc +++ b/util/net/http_transport_test.cc @@ -56,19 +56,13 @@ class HTTPTransportTestFixture : public MultiprocessExec { body_stream_(std::move(body_stream)), response_code_(http_response_code), request_validator_(request_validator) { - base::FilePath server_path = TestPaths::TestDataRoot().Append( - FILE_PATH_LITERAL("util/net/http_transport_test_server.py")); -#if defined(OS_POSIX) + base::FilePath server_path = TestPaths::Executable().DirName().Append( + FILE_PATH_LITERAL("http_transport_test_server") +#if defined(OS_WIN) + FILE_PATH_LITERAL(".exe") +#endif + ); SetChildCommand(server_path, nullptr); -#elif defined(OS_WIN) - // Explicitly invoke a shell and python so that python can be found in the - // path, and run the test script. - std::vector args; - args.push_back("/c"); - args.push_back("python"); - args.push_back(base::UTF16ToUTF8(server_path.value())); - SetChildCommand(base::FilePath(_wgetenv(L"COMSPEC")), &args); -#endif // OS_POSIX } const HTTPHeaders& headers() { return headers_; } diff --git a/util/net/http_transport_test_server.cc b/util/net/http_transport_test_server.cc new file mode 100644 index 00000000..2ac5ad60 --- /dev/null +++ b/util/net/http_transport_test_server.cc @@ -0,0 +1,114 @@ +// Copyright 2018 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. + +// A one-shot testing webserver. +// +// When invoked, this server will write a short integer to stdout, indicating on +// which port the server is listening. It will then read one integer from stdin, +// indicating the response code to be sent in response to a request. It also +// reads 16 characters from stdin, which, after having "\r\n" appended, will +// form the response body in a successful response (one with code 200). The +// server will process one HTTP request, deliver the prearranged response to the +// client, and write the entire request to stdout. It will then terminate. + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "tools/tool_support.h" +#include "util/file/file_io.h" + +#if COMPILER_MSVC +#pragma warning(push) +#pragma warning(disable: 4244 4245 4267 4702) +#endif + +#define CPPHTTPLIB_ZLIB_SUPPORT +#include "third_party/cpp-httplib/cpp-httplib/httplib.h" + +#if COMPILER_MSVC +#pragma warning(pop) +#endif + +namespace crashpad { +namespace { + +int HttpTransportTestServerMain(int argc, char* argv[]) { + httplib::Server svr(httplib::HttpVersion::v1_0); + + if (!svr.is_valid()) { + LOG(ERROR) << "server creation failed"; + return 1; + } + + uint16_t response_code; + char response[16]; + + std::string to_stdout; + + svr.post("/upload", + [&response, &response_code, &svr, &to_stdout]( + const httplib::Request& req, httplib::Response& res) { + res.status = response_code; + if (response_code == 200) { + res.set_content(std::string(response, 16) + "\r\n", + "text/plain"); + } else { + res.set_content("error", "text/plain"); + } + + for (const auto& h : req.headers) { + to_stdout += base::StringPrintf( + "%s: %s\r\n", h.first.c_str(), h.second.c_str()); + } + to_stdout += "\r\n"; + to_stdout += req.body; + + svr.stop(); + }); + + int port = svr.bind_to_any_port("127.0.0.1"); + + CheckedWriteFile( + StdioFileHandle(StdioStream::kStandardOutput), &port, sizeof(port)); + + CheckedReadFileExactly(StdioFileHandle(StdioStream::kStandardInput), + &response_code, + sizeof(response_code)); + + CheckedReadFileExactly(StdioFileHandle(StdioStream::kStandardInput), + &response, + sizeof(response)); + + svr.listen_after_bind(); + + LoggingWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + to_stdout.data(), + to_stdout.size()); + + return 0; +} + +} // namespace +} // namespace crashpad + +#if defined(OS_POSIX) || defined(OS_FUCHSIA) +int main(int argc, char* argv[]) { + return crashpad::HttpTransportTestServerMain(argc, argv); +} +#elif defined(OS_WIN) +int wmain(int argc, wchar_t* argv[]) { + return crashpad::ToolSupport::Wmain( + argc, argv, crashpad::HttpTransportTestServerMain); +} +#endif // OS_POSIX diff --git a/util/net/http_transport_test_server.py b/util/net/http_transport_test_server.py deleted file mode 100755 index d9777cbc..00000000 --- a/util/net/http_transport_test_server.py +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -# Copyright 2014 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. - -"""A one-shot testing webserver. - -When invoked, this server will write a short integer to stdout, indiciating on -which port the server is listening. It will then read one integer from stdin, -indiciating the response code to be sent in response to a request. It also reads -16 characters from stdin, which, after having "\r\n" appended, will form the -response body in a successful response (one with code 200). The server will -process one HTTP request, deliver the prearranged response to the client, and -write the entire request to stdout. It will then terminate. - -This server is written in Python since it provides a simple HTTP stack, and -because parsing chunked encoding is safer and easier in a memory-safe language. -This could easily have been written in C++ instead. -""" - -import os -import struct -import sys -import zlib - -if sys.platform == 'win32': - import msvcrt - -if sys.version_info[0] < 3: - import BaseHTTPServer as http_server -else: - import http.server as http_server - - -class BufferedReadFile(object): - """A File-like object that stores all read contents into a buffer.""" - - def __init__(self, real_file): - self.file = real_file - self.buffer = b'' - - def read(self, size=-1): - buf = self.file.read(size) - self.buffer += buf - return buf - - def readline(self, size=-1): - buf = self.file.readline(size) - self.buffer += buf - return buf - - def flush(self): - self.file.flush() - - def close(self): - self.file.close() - - -class RequestHandler(http_server.BaseHTTPRequestHandler): - # Everything to be written to stdout is collected into this string. It can’t - # be written to stdout until after the HTTP transaction is complete, because - # stdout is a pipe being read by a test program that’s also the HTTP client. - # The test program expects to complete the entire HTTP transaction before it - # even starts reading this script’s stdout. If the stdout pipe buffer fills up - # during an HTTP transaction, deadlock would result. - raw_request = b'' - - response_code = 500 - response_body = b'' - - def handle_one_request(self): - # Wrap the rfile in the buffering file object so that the raw header block - # can be written to stdout after it is parsed. - self.rfile = BufferedReadFile(self.rfile) - http_server.BaseHTTPRequestHandler.handle_one_request(self) - - def do_POST(self): - RequestHandler.raw_request = self.rfile.buffer - self.rfile.buffer = b'' - - if self.headers.get('Transfer-Encoding', '').lower() == 'chunked': - if 'Content-Length' in self.headers: - raise AssertionError - body = self.handle_chunked_encoding() - else: - length = int(self.headers.get('Content-Length', -1)) - body = self.rfile.read(length) - - if self.headers.get('Content-Encoding', '').lower() == 'gzip': - # 15 is the value of |wbits|, which should be at the maximum possible - # value to ensure that any gzip stream can be decoded. The offset of 16 - # specifies that the stream to decompress will be formatted with a gzip - # wrapper. - body = zlib.decompress(body, 16 + 15) - - RequestHandler.raw_request += body - - self.send_response(self.response_code) - self.end_headers() - if self.response_code == 200: - self.wfile.write(self.response_body) - self.wfile.write(b'\r\n') - - def handle_chunked_encoding(self): - """This parses a "Transfer-Encoding: Chunked" body in accordance with RFC - 7230 §4.1. This returns the result as a string. - """ - body = b'' - chunk_size = self.read_chunk_size() - while chunk_size > 0: - # Read the body. - data = self.rfile.read(chunk_size) - chunk_size -= len(data) - body += data - - # Finished reading this chunk. - if chunk_size == 0: - # Read through any trailer fields. - trailer_line = self.rfile.readline() - while trailer_line.strip() != b'': - trailer_line = self.rfile.readline() - - # Read the chunk size. - chunk_size = self.read_chunk_size() - return body - - def read_chunk_size(self): - # Read the whole line, including the \r\n. - chunk_size_and_ext_line = self.rfile.readline() - # Look for a chunk extension. - chunk_size_end = chunk_size_and_ext_line.find(b';') - if chunk_size_end == -1: - # No chunk extensions; just encounter the end of line. - chunk_size_end = chunk_size_and_ext_line.find(b'\r') - if chunk_size_end == -1: - self.send_response(400) # Bad request. - return -1 - return int(chunk_size_and_ext_line[:chunk_size_end], base=16) - - def log_request(self, code='-', size='-'): - # The default implementation logs these to sys.stderr, which is just noise. - pass - - -def StdioBinaryEquivalent(file): - """Return a file object equivalent to sys.stdin or sys.stdout capable of - reading or writing binary “bytes”. - - struct.unpack consumes “bytes”, and struct.pack produces “bytes”. These are - distinct from “str” in Python 3 (but not 2). In order to read and write these - from stdin and stdout, the underlying binary buffer must be used in place of - the upper-layer text wrapper. This function returns a suitable file. - - There is no underlying buffer in Python 2, but on Windows, the file mode must - still be set to binary in order to cleanly pass binary data. Note that in this - case, the mode of |file| itself is changed, as it’s not distinct from the - returned file. - """ - if hasattr(file, 'buffer'): - file = file.buffer - elif sys.platform == 'win32': - msvcrt.setmode(file.fileno(), os.O_BINARY) - return file - - -def Main(): - in_file = StdioBinaryEquivalent(sys.stdin) - out_file = StdioBinaryEquivalent(sys.stdout) - - # Start the server. - server = http_server.HTTPServer(('127.0.0.1', 0), RequestHandler) - - # Write the port as an unsigned short to the parent process. - out_file.write(struct.pack('=H', server.server_address[1])) - out_file.flush() - - # Read the desired test response code as an unsigned short and the desired - # response body as a 16-byte string from the parent process. - RequestHandler.response_code, RequestHandler.response_body = \ - struct.unpack('=H16s', in_file.read(struct.calcsize('=H16s'))) - - # Handle the request. - server.handle_request() - - # Share the entire request with the test program, which will validate it. - out_file.write(RequestHandler.raw_request) - out_file.flush() - -if __name__ == '__main__': - Main() From 0e144fb9aeffc0287c2e4670192938853478833a Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 20 Apr 2018 14:40:51 -0700 Subject: [PATCH 255/326] Add missing http_transport_test_server to gyp build Follow up to https://chromium-review.googlesource.com/c/crashpad/crashpad/+/1013293. Bug: crashpad:196, crashpad:227, crashpad:30 Change-Id: I87f18dce6a49f537cff5c9f3af2fe78b8d87c2ef Reviewed-on: https://chromium-review.googlesource.com/1022738 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- util/util_test.gyp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/util/util_test.gyp b/util/util_test.gyp index d07dc82a..e26b69aa 100644 --- a/util/util_test.gyp +++ b/util/util_test.gyp @@ -165,6 +165,30 @@ }], ], }, + { + 'target_name': 'http_transport_test_server', + 'type': 'executable', + 'dependencies': [ + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../third_party/zlib/zlib.gyp:zlib', + '../tools/tools.gyp:crashpad_tool_support', + '../util/util.gyp:crashpad_util', + ], + 'sources': [ + 'net/http_transport_test_server.cc', + ], + 'include_dirs': [ + '..', + ], + 'xcode_settings': { + 'WARNING_CFLAGS!': [ + '-Wexit-time-destructors', + ], + }, + 'cflags!': [ + '-Wexit-time-destructors', + ], + }, ], 'conditions': [ ['OS=="win"', { From 7274c9823f9b4d3b2e9620f7846b529d0453bae2 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 24 Apr 2018 13:02:29 -0700 Subject: [PATCH 256/326] fuchsia: Various build fixes for building in Fuchsia tree - Use "deprecated_system_image" (merging from downstream) - Add package for crashpad_handler - Depend on launchpad target instead of a lib when in tree, as launchpad is no longer in the sysroot. - Don't try to remove the -Wexit_time_destructors unless building standalone, when it's added by mini_chromiums BUILDCONFIG.gn Bug: crashpad:196 Change-Id: I08e0faaa989346b078a41896eb4ace69e7b1bcdc Reviewed-on: https://chromium-review.googlesource.com/1026514 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- BUILD.gn | 10 +++++++++- test/BUILD.gn | 8 +++++++- util/BUILD.gn | 8 +++++--- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index ffd1c576..709ce495 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -36,7 +36,7 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { import("//build/package.gni") package("crashpad_test") { testonly = true - system_image = true + deprecated_system_image = true deps = [ ":crashpad_tests", @@ -48,6 +48,14 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { }, ] } + + package("crashpad_handler") { + deprecated_system_image = true + + deps = [ + "handler:crashpad_handler", + ] + } } } else if (crashpad_is_standalone) { test("crashpad_client_test") { diff --git a/test/BUILD.gn b/test/BUILD.gn index a8d65954..ab37dc16 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -125,7 +125,13 @@ static_library("test") { } if (crashpad_is_fuchsia) { - libs = [ "launchpad" ] + if (crashpad_is_in_fuchsia) { + deps += [ + "//zircon/public/lib/launchpad", + ] + } else { + libs = [ "launchpad" ] + } } } diff --git a/util/BUILD.gn b/util/BUILD.gn index 5f886f20..1723ddcd 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -462,9 +462,11 @@ crashpad_executable("http_transport_test_server") { "../tools:tool_support", ] - remove_configs = [ - "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", - ] + if (crashpad_is_standalone) { + remove_configs = [ + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", + ] + } if (crashpad_is_win) { libs = [ "ws2_32.lib" ] From d051e00cd85a42a0da5de2e44f3b5ac62950a1a6 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 24 Apr 2018 15:48:14 -0700 Subject: [PATCH 257/326] android: name crashpad_handler like a loadable module There is not any normal way to package native executables in an Android APK (that I've found). It is normal to package native code as loadable modules, but Android's APK installer will ignore files not named like shared objects. Bug: crashpad:30 Change-Id: I45ea3e4b6dbfaf92d3d174e96aafe377928b9294 Reviewed-on: https://chromium-review.googlesource.com/1026157 Commit-Queue: Joshua Peraza Reviewed-by: Robert Sesek --- handler/BUILD.gn | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/handler/BUILD.gn b/handler/BUILD.gn index 78a4b092..f94e4e74 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -159,6 +159,26 @@ crashpad_executable("crashpad_handler") { } } +# There is not any normal way to package native executables in an Android APK. +# It is normal to package native code as a loadable module but Android's APK +# installer will ignore files not named like a shared object, so give the +# handler executable an acceptable name. +if (crashpad_is_android) { + copy("crashpad_handler_module") { + deps = [ + ":crashpad_handler", + ] + + sources = [ + "$root_out_dir/crashpad_handler", + ] + + outputs = [ + "$root_out_dir/libcrashpad_handler.so", + ] + } +} + crashpad_executable("crashpad_handler_test_extended_handler") { testonly = true From 78689cc810210f803702c79ee9b76bdc5cdb6094 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 25 Apr 2018 14:02:54 -0700 Subject: [PATCH 258/326] fuchsia: Depend on fdio_config when in-tree Fixes .so link errors from https://logs.chromium.org/v/?s=fuchsia%2Fbuildbucket%2Fcr-buildbucket.appspot.com%2F8948324805139372352%2F%2B%2Fsteps%2Fbuild%2F0%2Fsteps%2Fbuild_fuchsia%2F0%2Fsteps%2Fninja%2F0%2Fstdout [7302/11459] SOLINK x64-shared/crashpad_snapshot_test_module_small.so FAILED: x64-shared/crashpad_snapshot_test_module_small.so x64-shared/lib.unstripped/crashpad_snapshot_test_module_small.so /b/s/w/ir/kitchen-workdir/cipd/goma/gomacc ../../buildtools/linux-x64/clang/bin/clang++ -shared -Wl,--threads --sysroot=/b/s/w/ir/kitchen-workdir/out/debug-x64/zircon_toolchain/obj/zircon/public/sysroot/sysroot --target=x86_64-fuchsia -march=x86-64 -mcx16 -g -Og -Wl,--icf=all -Wl,--no-undefined -o "x64-shared/lib.unstripped/crashpad_snapshot_test_module_small.so" -Wl,-soname="crashpad_snapshot_test_module_small.so" @"x64-shared/crashpad_snapshot_test_module_small.so.rsp" && ../../buildtools/linux-x64/clang/bin/llvm-objcopy --strip-all "x64-shared/lib.unstripped/crashpad_snapshot_test_module_small.so" "x64-shared/crashpad_snapshot_test_module_small.so" /b/s/w/ir/kitchen-workdir/out/debug-x64/../../buildtools/linux-x64/clang/bin/ld.lld: error: undefined symbol: dirfd >>> referenced by directory_reader_posix.cc:72 (../../third_party/crashpad/util/file/directory_reader_posix.cc:72) >>> libutil.directory_reader_posix.o:(crashpad::DirectoryReader::DirectoryFD()) in archive x64-shared/obj/third_party/crashpad/util/libutil.a /b/s/w/ir/kitchen-workdir/out/debug-x64/../../buildtools/linux-x64/clang/bin/ld.lld: error: undefined symbol: fdio_ioctl >>> referenced by sysinfo.h:32 (../../out/debug-x64/zircon_toolchain/obj/zircon/public/sysroot/sysroot/include/zircon/device/sysinfo.h:32) >>> libutil.koid_utilities.o:(ioctl_sysinfo_get_root_job(int, unsigned int*)) in archive x64-shared/obj/third_party/crashpad/util/libutil.a clang-7.0: error: ld.lld command failed with exit code 1 (use -v to see invocation) Bug: crashpad:196 Change-Id: I50b50a8ba668d75d40e86d863976c386bb382f65 Reviewed-on: https://chromium-review.googlesource.com/1028840 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- build/crashpad_buildconfig.gni | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/crashpad_buildconfig.gni b/build/crashpad_buildconfig.gni index c10ad30e..f767027f 100644 --- a/build/crashpad_buildconfig.gni +++ b/build/crashpad_buildconfig.gni @@ -78,6 +78,8 @@ template("crashpad_executable") { if (crashpad_is_in_chromium) { deps += [ "//build/config:exe_and_shlib_deps" ] + } else if (crashpad_is_in_fuchsia) { + configs += [ "//build/config/fuchsia:fdio_config" ] } } } @@ -99,6 +101,8 @@ template("crashpad_loadable_module") { if (crashpad_is_in_chromium) { deps += [ "//build/config:exe_and_shlib_deps" ] + } else if (crashpad_is_in_fuchsia) { + configs += [ "//build/config/fuchsia:fdio_config" ] } } } From 2ddfb4cd3c79a94aee1f3e4ab0f3ad63a1a05fc4 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 26 Apr 2018 14:13:26 -0700 Subject: [PATCH 259/326] fuchsia: CaptureContext for arm64 Copied from the _linux implementation, which looks close to what ucontext on Fuchsia is (though it will probably need to change). In arm64 debug, CaptureContext.CaptureContext requires slightly longer slop distance. Bug: crashpad:196 Change-Id: I2a6f90095e06fe8b468fbfd8add66a73c8a1d92f Reviewed-on: https://chromium-review.googlesource.com/1031091 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- util/misc/capture_context_fuchsia.S | 42 ++++++++++++++++++++++++++++- util/misc/capture_context_test.cc | 2 +- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/util/misc/capture_context_fuchsia.S b/util/misc/capture_context_fuchsia.S index c8436ffc..8e5c0cbe 100644 --- a/util/misc/capture_context_fuchsia.S +++ b/util/misc/capture_context_fuchsia.S @@ -128,6 +128,46 @@ CAPTURECONTEXT_SYMBOL: #elif defined(__aarch64__) - #error TODO implement + // Zero out fault_address, which is unused. + str x31, [x0, #0xb0] // context->uc_mcontext.fault_address + + // Save general purpose registers in context->uc_mcontext.regs[i]. + // The original x0 can't be recovered. + stp x0, x1, [x0, #0xb8] + stp x2, x3, [x0, #0xc8] + stp x4, x5, [x0, #0xd8] + stp x6, x7, [x0, #0xe8] + stp x8, x9, [x0, #0xf8] + stp x10, x11, [x0, #0x108] + stp x12, x13, [x0, #0x118] + stp x14, x15, [x0, #0x128] + stp x16, x17, [x0, #0x138] + stp x18, x19, [x0, #0x148] + stp x20, x21, [x0, #0x158] + stp x22, x23, [x0, #0x168] + stp x24, x25, [x0, #0x178] + stp x26, x27, [x0, #0x188] + stp x28, x29, [x0, #0x198] + + // The original LR can't be recovered. + str LR, [x0, #0x1a8] + + // Use x1 as a scratch register. + mov x1, SP + str x1, [x0, #0x1b0] // context->uc_mcontext.sp + + // The link register holds the return address for this function. + str LR, [x0, #0x1b8] // context->uc_mcontext.pc + + // NZCV, pstate, and CPSR are synonyms. + mrs x1, NZCV + str x1, [x0, #0x1c0] // context->uc_mcontext.pstate + + // Restore x1 from the saved context. + ldr x1, [x0, #0xc0] + + // TODO(scottmg): save floating-point registers. + + ret #endif // __x86_64__ diff --git a/util/misc/capture_context_test.cc b/util/misc/capture_context_test.cc index 6ce81002..deeea9e3 100644 --- a/util/misc/capture_context_test.cc +++ b/util/misc/capture_context_test.cc @@ -76,7 +76,7 @@ void TestCaptureContext() { reinterpret_cast(&sp))); sp = StackPointerFromContext(context_1); EXPECT_PRED2([](uintptr_t actual, - uintptr_t reference) { return reference - actual < 512u; }, + uintptr_t reference) { return reference - actual < 768u; }, sp, kReferenceSP); From 60ae9eeadb4a3a923219ba7b50e663f32dd97d40 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 26 Apr 2018 17:21:45 -0700 Subject: [PATCH 260/326] Implementation of HTTPTransport via raw socket Partial implementation: Currently only handles http (i.e. no TLS), only POST, and only certain response types (only when Content-Length is specified, and not chunked). Used for Linux and Fuchsia lacking anything better (that's shippable). Removes libcurl HTTPTransport, since it isn't available in the Chromium sysroot anyway. This is an intermediate step until BoringSSL is available in the Fuchsia SDK. Once that's available, it should be "relatively straightfoward" to make http_transport_socket.cc secure its socket using BoringSSL or OpenSSL depending on the platform. Bug: crashpad:196, crashpad:227, crashpad:30 Change-Id: If33a0d3f11b9000cbc3f52f96cd024ef274a922f Reviewed-on: https://chromium-review.googlesource.com/1022717 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- build/run_tests.py | 2 +- util/BUILD.gn | 37 +-- util/net/http_transport_libcurl.cc | 402 ------------------------ util/net/http_transport_socket.cc | 409 +++++++++++++++++++++++++ util/net/http_transport_test_server.cc | 5 +- util/util.gyp | 10 +- 6 files changed, 423 insertions(+), 442 deletions(-) delete mode 100644 util/net/http_transport_libcurl.cc create mode 100644 util/net/http_transport_socket.cc diff --git a/build/run_tests.py b/build/run_tests.py index efd0aa46..df7d8af1 100755 --- a/build/run_tests.py +++ b/build/run_tests.py @@ -422,7 +422,7 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line): done_message = 'TERMINATED: ' + unique_id namespace_command = [ - 'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root, + 'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root, '/svc=/svc', '--replace-child-argv0=/pkg/bin/' + test, '--', staging_root + '/bin/' + test] + extra_command_line netruncmd(namespace_command, ['echo', done_message]) diff --git a/util/BUILD.gn b/util/BUILD.gn index 1723ddcd..d3578dd4 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -14,13 +14,6 @@ import("../build/crashpad_buildconfig.gni") -declare_args() { - if (crashpad_is_linux) { - # Whether the libcurl-based HTTPTransport implementation should be built. - crashpad_enable_http_transport_libcurl = !crashpad_is_in_chromium - } -} - if (crashpad_is_mac) { if (crashpad_is_in_chromium) { import("//build/config/sysroot.gni") @@ -251,12 +244,10 @@ static_library("util") { sources += get_target_outputs(":mig") } - if (crashpad_is_linux || crashpad_is_android) { - if (crashpad_is_linux && crashpad_enable_http_transport_libcurl) { - sources += [ "net/http_transport_libcurl.cc" ] - } else { - sources += [ "net/http_transport_none.cc" ] - } + if (crashpad_is_linux || crashpad_is_fuchsia) { + sources += [ "net/http_transport_socket.cc" ] + } else if (crashpad_is_android) { + sources += [ "net/http_transport_none.cc" ] } if (crashpad_is_linux || crashpad_is_android) { @@ -394,7 +385,6 @@ static_library("util") { "fuchsia/scoped_task_suspend.h", "misc/capture_context_fuchsia.S", "misc/paths_fuchsia.cc", - "net/http_transport_none.cc", "process/process_memory_fuchsia.cc", "process/process_memory_fuchsia.h", ] @@ -425,10 +415,6 @@ static_library("util") { include_dirs += [ "$root_build_dir/gen" ] } - if (crashpad_is_linux && crashpad_enable_http_transport_libcurl) { - libs = [ "curl" ] - } - if (crashpad_is_win) { libs = [ "user32.lib", @@ -519,19 +505,8 @@ source_set("util_test") { "thread/worker_thread_test.cc", ] - if (!crashpad_is_android && !crashpad_is_fuchsia && - (!crashpad_is_linux || crashpad_enable_http_transport_libcurl)) { - # Android and Fuchsia will each require an HTTPTransport implementation - # (libcurl isn’t in either’s SDK) and a solution to - # http_transport_test_server.py, because Python isn’t available on either. - # The latter could be ported to non-Python, or the test server could run on - # the build host with a method to forward requests from the device to the - # host. - # - # Linux optionally compiles in a libcurl-based HTTPTransport, but since curl - # isn't in a base Debian sysroot (which is what Chromium builds against), - # maintain an option to exclude that, for now. - # https://crashpad.chromium.org/bug/220. + if (!crashpad_is_android) { + # Android requires an HTTPTransport implementation. sources += [ "net/http_transport_test.cc" ] } diff --git a/util/net/http_transport_libcurl.cc b/util/net/http_transport_libcurl.cc deleted file mode 100644 index c16a593d..00000000 --- a/util/net/http_transport_libcurl.cc +++ /dev/null @@ -1,402 +0,0 @@ -// 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 "util/net/http_transport.h" - -#include -#include -#include - -#include -#include - -#include "base/logging.h" -#include "base/numerics/safe_math.h" -#include "base/scoped_generic.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/stringprintf.h" -#include "build/build_config.h" -#include "package.h" -#include "util/net/http_body.h" -#include "util/numeric/safe_assignment.h" - -namespace crashpad { - -namespace { - -std::string UserAgent() { - std::string user_agent = base::StringPrintf( - "%s/%s %s", PACKAGE_NAME, PACKAGE_VERSION, curl_version()); - - utsname os; - if (uname(&os) != 0) { - PLOG(WARNING) << "uname"; - } else { - // Match the architecture name that would be used by the kernel, so that the - // strcmp() below can omit the kernel’s architecture name if it’s the same - // as the user process’ architecture. On Linux, these names are normally - // defined in each architecture’s Makefile as UTS_MACHINE, but can be - // overridden in architecture-specific configuration as COMPAT_UTS_MACHINE. - // See linux-4.9.17/arch/*/Makefile and - // linux-4.9.17/arch/*/include/asm/compat.h. In turn, on some systems, these - // names are further overridden or refined in early kernel startup code by - // modifying the string returned by linux-4.9.17/include/linux/utsname.h - // init_utsname() as noted. -#if defined(ARCH_CPU_X86) - // linux-4.9.17/arch/x86/kernel/cpu/bugs.c check_bugs() sets the first digit - // to 4, 5, or 6, but no higher. -#if defined(__i686__) - static constexpr char arch[] = "i686"; -#elif defined(__i586__) - static constexpr char arch[] = "i586"; -#elif defined(__i486__) - static constexpr char arch[] = "i486"; -#else - static constexpr char arch[] = "i386"; -#endif -#elif defined(ARCH_CPU_X86_64) - static constexpr char arch[] = "x86_64"; -#elif defined(ARCH_CPU_ARMEL) - // linux-4.9.17/arch/arm/kernel/setup.c setup_processor() bases the string - // on the ARM processor name and a character identifying little- or - // big-endian. The processor name comes from a definition in - // arch/arm/mm/proc-*.S. -#if defined(__ARM_ARCH_4T__) - static constexpr char arch[] = "armv4t" -#elif defined(__ARM_ARCH_5TEJ__) - static constexpr char arch[] = "armv5tej" -#elif defined(__ARM_ARCH_5TE__) - static constexpr char arch[] = "armv5te" -#elif defined(__ARM_ARCH_5T__) - static constexpr char arch[] = "armv5t" -#elif defined(__ARM_ARCH_7M__) - static constexpr char arch[] = "armv7m" -#else - // Most ARM architectures fall into here, including all profile variants of - // armv6, armv7, armv8, with one exception, armv7m, handled above. - // xstr(__ARM_ARCH) will be the architecture revision number, such as 6, 7, - // or 8. -#define xstr(s) str(s) -#define str(s) #s - static constexpr char arch[] = "armv" xstr(__ARM_ARCH) -#undef str -#undef xstr -#endif -#if defined(ARCH_CPU_LITTLE_ENDIAN) - "l"; -#elif defined(ARCH_CPU_BIG_ENDIAN) - "b"; -#endif -#elif defined(ARCH_CPU_ARM64) - // ARM64 uses aarch64 or aarch64_be as directed by ELF_PLATFORM. See - // linux-4.9.17/arch/arm64/kernel/setup.c setup_arch(). -#if defined(ARCH_CPU_LITTLE_ENDIAN) - static constexpr char arch[] = "aarch64"; -#elif defined(ARCH_CPU_BIG_ENDIAN) - static constexpr char arch[] = "aarch64_be"; -#endif -#elif defined(ARCH_CPU_MIPSEL) - static constexpr char arch[] = "mips"; -#elif defined(ARCH_CPU_MIPS64EL) - static constexpr char arch[] = "mips64"; -#else -#error Port -#endif - - user_agent.append( - base::StringPrintf(" %s/%s (%s", os.sysname, os.release, arch)); - if (strcmp(arch, os.machine) != 0) { - user_agent.append(base::StringPrintf("; %s", os.machine)); - } - user_agent.append(1, ')'); - } - - return user_agent; -} - -std::string CurlErrorMessage(CURLcode curl_err, const std::string& base) { - return base::StringPrintf( - "%s: %s (%d)", base.c_str(), curl_easy_strerror(curl_err), curl_err); -} - -struct ScopedCURLTraits { - static CURL* InvalidValue() { return nullptr; } - static void Free(CURL* curl) { - if (curl) { - curl_easy_cleanup(curl); - } - } -}; -using ScopedCURL = base::ScopedGeneric; - -class CurlSList { - public: - CurlSList() : list_(nullptr) {} - ~CurlSList() { - if (list_) { - curl_slist_free_all(list_); - } - } - - curl_slist* get() const { return list_; } - - bool Append(const char* data) { - curl_slist* list = curl_slist_append(list_, data); - if (!list_) { - list_ = list; - } - return list != nullptr; - } - - private: - curl_slist* list_; - - DISALLOW_COPY_AND_ASSIGN(CurlSList); -}; - -class ScopedClearString { - public: - explicit ScopedClearString(std::string* string) : string_(string) {} - - ~ScopedClearString() { - if (string_) { - string_->clear(); - } - } - - void Disarm() { string_ = nullptr; } - - private: - std::string* string_; - - DISALLOW_COPY_AND_ASSIGN(ScopedClearString); -}; - -class HTTPTransportLibcurl final : public HTTPTransport { - public: - HTTPTransportLibcurl(); - ~HTTPTransportLibcurl() override; - - // HTTPTransport: - bool ExecuteSynchronously(std::string* response_body) override; - - private: - static size_t ReadRequestBody(char* buffer, - size_t size, - size_t nitems, - void* userdata); - static size_t WriteResponseBody(char* buffer, - size_t size, - size_t nitems, - void* userdata); - - DISALLOW_COPY_AND_ASSIGN(HTTPTransportLibcurl); -}; - -HTTPTransportLibcurl::HTTPTransportLibcurl() : HTTPTransport() {} - -HTTPTransportLibcurl::~HTTPTransportLibcurl() {} - -bool HTTPTransportLibcurl::ExecuteSynchronously(std::string* response_body) { - DCHECK(body_stream()); - - response_body->clear(); - - // curl_easy_init() will do this on the first call if it hasn’t been done yet, - // but not in a thread-safe way as is done here. - static CURLcode curl_global_init_err = []() { - return curl_global_init(CURL_GLOBAL_DEFAULT); - }(); - if (curl_global_init_err != CURLE_OK) { - LOG(ERROR) << CurlErrorMessage(curl_global_init_err, "curl_global_init"); - return false; - } - - CurlSList curl_headers; - ScopedCURL curl(curl_easy_init()); - if (!curl.get()) { - LOG(ERROR) << "curl_easy_init"; - return false; - } - -// These macros wrap the repetitive “try something, log an error and return -// false on failure” pattern. Macros are convenient because the log messages -// will point to the correct line number, which can help pinpoint a problem when -// there are as many calls to these functions as there are here. -#define TRY_CURL_EASY_SETOPT(curl, option, parameter) \ - do { \ - CURLcode curl_err = curl_easy_setopt((curl), (option), (parameter)); \ - if (curl_err != CURLE_OK) { \ - LOG(ERROR) << CurlErrorMessage(curl_err, "curl_easy_setopt"); \ - return false; \ - } \ - } while (false) -#define TRY_CURL_SLIST_APPEND(slist, data) \ - do { \ - if (!(slist).Append(data)) { \ - LOG(ERROR) << "curl_slist_append"; \ - return false; \ - } \ - } while (false) - - TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_USERAGENT, UserAgent().c_str()); - - // Accept and automatically decode any encoding that libcurl understands. - TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_ACCEPT_ENCODING, ""); - - TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_URL, url().c_str()); - - constexpr int kMillisecondsPerSecond = 1E3; - TRY_CURL_EASY_SETOPT(curl.get(), - CURLOPT_TIMEOUT_MS, - static_cast(timeout() * kMillisecondsPerSecond)); - - // If the request body size is known ahead of time, a Content-Length header - // field will be present. Store that to use as CURLOPT_POSTFIELDSIZE_LARGE, - // which will both set the Content-Length field in the request header and - // inform libcurl of the request body size. Otherwise, use Transfer-Encoding: - // chunked, which does not require advance knowledge of the request body size. - bool chunked = true; - size_t content_length; - for (const auto& pair : headers()) { - if (pair.first == kContentLength) { - chunked = !base::StringToSizeT(pair.second, &content_length); - DCHECK(!chunked); - } else { - TRY_CURL_SLIST_APPEND(curl_headers, - (pair.first + ": " + pair.second).c_str()); - } - } - - if (method() == "POST") { - TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_POST, 1l); - - // By default when sending a POST request, libcurl includes an “Expect: - // 100-continue” header field. Althogh this header is specified in HTTP/1.1 - // (RFC 2616 §8.2.3, RFC 7231 §5.1.1), even collection servers that claim to - // speak HTTP/1.1 may not respond to it. When sending this header field, - // libcurl will wait for one second for the server to respond with a “100 - // Continue” status before continuing to transmit the request body. This - // delay is avoided by telling libcurl not to send this header field at all. - // The drawback is that certain HTTP error statuses may not be received - // until after substantial amounts of data have been sent to the server. - TRY_CURL_SLIST_APPEND(curl_headers, "Expect:"); - - if (chunked) { - TRY_CURL_SLIST_APPEND(curl_headers, "Transfer-Encoding: chunked"); - } else { - curl_off_t content_length_curl; - if (!AssignIfInRange(&content_length_curl, content_length)) { - LOG(ERROR) << base::StringPrintf("Content-Length %zu too large", - content_length); - return false; - } - TRY_CURL_EASY_SETOPT( - curl.get(), CURLOPT_POSTFIELDSIZE_LARGE, content_length_curl); - } - } else if (method() != "GET") { - // Untested. - TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_CUSTOMREQUEST, method().c_str()); - } - - TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_HTTPHEADER, curl_headers.get()); - - TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_READFUNCTION, ReadRequestBody); - TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_READDATA, this); - TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_WRITEFUNCTION, WriteResponseBody); - TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_WRITEDATA, response_body); - -#undef TRY_CURL_EASY_SETOPT -#undef TRY_CURL_SLIST_APPEND - - // If a partial response body is received and then a failure occurs, ensure - // that response_body is cleared. - ScopedClearString clear_response_body(response_body); - - // Do it. - CURLcode curl_err = curl_easy_perform(curl.get()); - if (curl_err != CURLE_OK) { - LOG(ERROR) << CurlErrorMessage(curl_err, "curl_easy_perform"); - return false; - } - - long status; - curl_err = curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &status); - if (curl_err != CURLE_OK) { - LOG(ERROR) << CurlErrorMessage(curl_err, "curl_easy_getinfo"); - return false; - } - - if (status != 200) { - LOG(ERROR) << base::StringPrintf("HTTP status %ld", status); - return false; - } - - // The response body is complete. Don’t clear it. - clear_response_body.Disarm(); - - return true; -} - -// static -size_t HTTPTransportLibcurl::ReadRequestBody(char* buffer, - size_t size, - size_t nitems, - void* userdata) { - HTTPTransportLibcurl* self = - reinterpret_cast(userdata); - - // This libcurl callback mimics the silly stdio-style fread() interface: size - // and nitems have been separated and must be multiplied. - base::CheckedNumeric checked_len = base::CheckMul(size, nitems); - size_t len = checked_len.ValueOrDefault(std::numeric_limits::max()); - - // Limit the read to what can be expressed in a FileOperationResult. - len = std::min( - len, - static_cast(std::numeric_limits::max())); - - FileOperationResult bytes_read = self->body_stream()->GetBytesBuffer( - reinterpret_cast(buffer), len); - if (bytes_read < 0) { - return CURL_READFUNC_ABORT; - } - - return bytes_read; -} - -// static -size_t HTTPTransportLibcurl::WriteResponseBody(char* buffer, - size_t size, - size_t nitems, - void* userdata) { - std::string* response_body = reinterpret_cast(userdata); - - // This libcurl callback mimics the silly stdio-style fread() interface: size - // and nitems have been separated and must be multiplied. - base::CheckedNumeric checked_len = base::CheckMul(size, nitems); - size_t len = checked_len.ValueOrDefault(std::numeric_limits::max()); - - response_body->append(buffer, len); - return len; -} - -} // namespace - -// static -std::unique_ptr HTTPTransport::Create() { - return std::unique_ptr(new HTTPTransportLibcurl()); -} - -} // namespace crashpad diff --git a/util/net/http_transport_socket.cc b/util/net/http_transport_socket.cc new file mode 100644 index 00000000..6e2e7aaa --- /dev/null +++ b/util/net/http_transport_socket.cc @@ -0,0 +1,409 @@ +// Copyright 2018 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 "util/net/http_transport.h" + +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/macros.h" +#include "base/numerics/safe_conversions.h" +#include "base/posix/eintr_wrapper.h" +#include "base/scoped_generic.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "util/file/file_io.h" +#include "util/net/http_body.h" +#include "util/net/url.h" +#include "util/stdlib/string_number_conversion.h" +#include "util/string/split_string.h" + +namespace crashpad { + +namespace { + +constexpr const char kCRLFTerminator[] = "\r\n"; + +class HTTPTransportSocket final : public HTTPTransport { + public: + HTTPTransportSocket() = default; + ~HTTPTransportSocket() override = default; + + bool ExecuteSynchronously(std::string* response_body) override; + + private: + DISALLOW_COPY_AND_ASSIGN(HTTPTransportSocket); +}; + +struct ScopedAddrinfoTraits { + static addrinfo* InvalidValue() { return nullptr; } + static void Free(addrinfo* ai) { freeaddrinfo(ai); } +}; +using ScopedAddrinfo = + base::ScopedGeneric; + +bool WaitUntilSocketIsReady(int sock) { + pollfd pollfds; + pollfds.fd = sock; + pollfds.events = POLLIN | POLLPRI | POLLOUT; + constexpr int kTimeoutMS = 1000; + int ret = HANDLE_EINTR(poll(&pollfds, 1, kTimeoutMS)); + if (ret < 0) { + PLOG(ERROR) << "poll"; + return false; + } else if (ret == 1) { + if (pollfds.revents & POLLERR) { + int err; + socklen_t err_len = sizeof(err); + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &err_len) != 0) { + PLOG(ERROR) << "getsockopt"; + } else { + errno = err; + PLOG(ERROR) << "POLLERR"; + } + return false; + } + if (pollfds.revents & POLLHUP) { + return false; + } + return (pollfds.revents & POLLIN) != 0 || (pollfds.revents & POLLOUT) != 0; + } + + // Timeout. + return false; +} + +class ScopedSetNonblocking { + public: + explicit ScopedSetNonblocking(int sock) : sock_(sock) { + int flags = fcntl(sock, F_GETFL, 0); + if (flags < 0) { + PLOG(ERROR) << "fcntl"; + sock_ = -1; + return; + } + + if (fcntl(sock_, F_SETFL, flags | O_NONBLOCK) < 0) { + PLOG(ERROR) << "fcntl"; + sock_ = -1; + } + } + + ~ScopedSetNonblocking() { + if (sock_ >= 0) { + int flags = fcntl(sock_, F_GETFL, 0); + if (flags < 0) { + PLOG(ERROR) << "fcntl"; + return; + } + + if (fcntl(sock_, F_SETFL, flags & (~O_NONBLOCK)) < 0) { + PLOG(ERROR) << "fcntl"; + } + } + } + + private: + int sock_; + + DISALLOW_COPY_AND_ASSIGN(ScopedSetNonblocking); +}; + +base::ScopedFD CreateSocket(const std::string& hostname, + const std::string& port) { + addrinfo hints = {}; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = 0; + + addrinfo* addrinfo_raw; + if (getaddrinfo(hostname.c_str(), port.c_str(), &hints, &addrinfo_raw) < 0) { + PLOG(ERROR) << "getaddrinfo"; + return base::ScopedFD(); + } + ScopedAddrinfo addrinfo(addrinfo_raw); + + for (const auto* ap = addrinfo.get(); ap; ap = ap->ai_next) { + base::ScopedFD result( + socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol)); + if (!result.is_valid()) { + continue; + } + + { + // Set socket to non-blocking to avoid hanging for a long time if the + // network is down. + ScopedSetNonblocking nonblocking(result.get()); + + if (HANDLE_EINTR(connect(result.get(), ap->ai_addr, ap->ai_addrlen)) < + 0) { + if (errno != EINPROGRESS) { + PLOG(ERROR) << "connect"; + } else if (WaitUntilSocketIsReady(result.get())) { + return result; + } + return base::ScopedFD(); + } + + return result; + } + } + + return base::ScopedFD(); +} + +bool WriteRequest(int sock, + const std::string& method, + const std::string& resource, + const HTTPHeaders& headers, + HTTPBodyStream* body_stream) { + std::string request_line = base::StringPrintf( + "%s %s HTTP/1.0\r\n", method.c_str(), resource.c_str()); + if (!LoggingWriteFile(sock, request_line.data(), request_line.size())) + return false; + + // Write headers, and determine if Content-Length has been specified. + bool chunked = true; + size_t content_length = 0; + for (const auto& header : headers) { + std::string header_str = base::StringPrintf( + "%s: %s\r\n", header.first.c_str(), header.second.c_str()); + if (header.first == kContentLength) { + chunked = !base::StringToSizeT(header.second, &content_length); + DCHECK(!chunked); + } + + if (!LoggingWriteFile(sock, header_str.data(), header_str.size())) + return false; + } + + // If no Content-Length, then encode as chunked, so add that header too. + if (chunked) { + static constexpr const char kTransferEncodingChunked[] = + "Transfer-Encoding: chunked\r\n"; + if (!LoggingWriteFile( + sock, kTransferEncodingChunked, strlen(kTransferEncodingChunked))) { + return false; + } + } + + if (!LoggingWriteFile(sock, kCRLFTerminator, strlen(kCRLFTerminator))) { + return false; + } + + FileOperationResult data_bytes; + do { + constexpr size_t kCRLFSize = arraysize(kCRLFTerminator) - 1; + struct __attribute__((packed)) { + char size[8]; + char crlf[2]; + uint8_t data[32 * 1024 + kCRLFSize]; + } buf; + static_assert( + sizeof(buf) == sizeof(buf.size) + sizeof(buf.crlf) + sizeof(buf.data), + "buf should not have padding"); + + // Read a block of data. + data_bytes = + body_stream->GetBytesBuffer(buf.data, sizeof(buf.data) - kCRLFSize); + if (data_bytes == -1) { + return false; + } + DCHECK_GE(data_bytes, 0); + DCHECK_LE(static_cast(data_bytes), sizeof(buf.data) - kCRLFSize); + + void* write_start; + size_t write_size; + + if (chunked) { + // Chunked encoding uses the entirety of buf. buf.size is presented in + // hexadecimal without any leading "0x". The terminating CR and LF will be + // placed immediately following the used portion of buf.data, even if + // buf.data is not full. + + // Not snprintf because non-null terminated is desired. + int rv = sprintf( + buf.size, "%08x", base::checked_cast(data_bytes)); + DCHECK_GE(rv, 0); + DCHECK_EQ(static_cast(rv), sizeof(buf.size)); + DCHECK_NE(buf.size[sizeof(buf.size) - 1], '\0'); + + memcpy(&buf.crlf[0], kCRLFTerminator, kCRLFSize); + memcpy(&buf.data[data_bytes], kCRLFTerminator, kCRLFSize); + + // Skip leading zeroes in the chunk size. + size_t size_len; + for (size_len = sizeof(buf.size); size_len > 1; --size_len) { + if (buf.size[sizeof(buf.size) - size_len] != '0') { + break; + } + } + + write_start = buf.crlf - size_len; + write_size = size_len + sizeof(buf.crlf) + data_bytes + kCRLFSize; + } else { + // When not using chunked encoding, only use buf.data. + write_start = buf.data; + write_size = data_bytes; + } + + // write_size will be 0 at EOF in non-chunked mode. Skip the write in that + // case. In contrast, at EOF in chunked mode, a zero-length chunk must be + // sent to signal EOF. This will happen when processing the EOF indicated by + // a 0 return from body_stream()->GetBytesBuffer() above. + if (write_size != 0) { + if (!LoggingWriteFile(sock, write_start, write_size)) + return false; + } + } while (data_bytes > 0); + + return true; +} + +bool ReadLine(int sock, std::string* line) { + line->clear(); + for (;;) { + char byte; + if (!LoggingReadFileExactly(sock, &byte, 1)) { + return false; + } + + line->append(&byte, 1); + if (byte == '\n') + return true; + } +} + +bool StartsWith(const std::string& str, const char* with, size_t len) { + return str.compare(0, len, with) == 0; +} + +bool ReadResponseLine(int sock) { + std::string response_line; + if (!ReadLine(sock, &response_line)) { + LOG(ERROR) << "ReadLine"; + return false; + } + static constexpr const char kHttp10[] = "HTTP/1.0 200 "; + static constexpr const char kHttp11[] = "HTTP/1.1 200 "; + return StartsWith(response_line, kHttp10, strlen(kHttp10)) || + StartsWith(response_line, kHttp11, strlen(kHttp11)); +} + +bool ReadResponseHeaders(int sock, HTTPHeaders* headers) { + for (;;) { + std::string line; + if (!ReadLine(sock, &line)) { + return false; + } + + if (line == kCRLFTerminator) { + return true; + } + + std::string left, right; + if (!SplitStringFirst(line, ':', &left, &right)) { + LOG(ERROR) << "SplitStringFirst"; + return false; + } + DCHECK_EQ(right[right.size() - 1], '\n'); + DCHECK_EQ(right[right.size() - 2], '\r'); + DCHECK_EQ(right[0], ' '); + DCHECK_NE(right[1], ' '); + right = right.substr(1, right.size() - 3); + (*headers)[left] = right; + } +} + +bool ReadContentChunked(int sock, std::string* body) { + // TODO(scottmg): https://crashpad.chromium.org/bug/196. + LOG(ERROR) << "TODO(scottmg): chunked response read"; + return false; +} + +bool ReadResponse(int sock, std::string* response_body) { + response_body->clear(); + + if (!ReadResponseLine(sock)) { + return false; + } + + HTTPHeaders response_headers; + if (!ReadResponseHeaders(sock, &response_headers)) { + return false; + } + + auto it = response_headers.find("Content-Length"); + size_t len = 0; + if (it != response_headers.end()) { + if (!base::StringToSizeT(it->second, &len)) { + LOG(ERROR) << "invalid Content-Length"; + return false; + } + } + + if (len) { + response_body->resize(len, 0); + return ReadFileExactly(sock, &(*response_body)[0], len); + } + + it = response_headers.find("Transfer-Encoding"); + bool chunked = false; + if (it != response_headers.end() && it->second == "chunked") { + chunked = true; + } + if (!chunked) { + // TODO(scottmg): https://crashpad.chromium.org/bug/196. Doesn't happen + // in practice, but is possible. + LOG(ERROR) << "unimplemented non-chunked without Content-Length"; + return false; + } + + return ReadContentChunked(sock, response_body); +} + +bool HTTPTransportSocket::ExecuteSynchronously(std::string* response_body) { + std::string scheme, hostname, port, resource; + if (!CrackURL(url(), &scheme, &hostname, &port, &resource)) { + return false; + } + + base::ScopedFD sock(CreateSocket(hostname, port)); + if (!sock.is_valid()) { + return false; + } + + if (!WriteRequest(sock.get(), method(), resource, headers(), body_stream())) { + return false; + } + + if (!ReadResponse(sock.get(), response_body)) { + return false; + } + + return true; +} + +} // namespace + +// static +std::unique_ptr HTTPTransport::Create() { + return std::unique_ptr(new HTTPTransportSocket); +} + +} // namespace crashpad diff --git a/util/net/http_transport_test_server.cc b/util/net/http_transport_test_server.cc index 2ac5ad60..228d5c1c 100644 --- a/util/net/http_transport_test_server.cc +++ b/util/net/http_transport_test_server.cc @@ -23,6 +23,7 @@ // client, and write the entire request to stdout. It will then terminate. #include "base/logging.h" +#include "base/numerics/safe_conversions.h" #include "base/strings/stringprintf.h" #include "build/build_config.h" #include "tools/tool_support.h" @@ -67,6 +68,7 @@ int HttpTransportTestServerMain(int argc, char* argv[]) { res.set_content("error", "text/plain"); } + to_stdout += "POST /upload HTTP/1.0\r\n"; for (const auto& h : req.headers) { to_stdout += base::StringPrintf( "%s: %s\r\n", h.first.c_str(), h.second.c_str()); @@ -77,7 +79,8 @@ int HttpTransportTestServerMain(int argc, char* argv[]) { svr.stop(); }); - int port = svr.bind_to_any_port("127.0.0.1"); + uint16_t port = + base::checked_cast(svr.bind_to_any_port("127.0.0.1")); CheckedWriteFile( StdioFileHandle(StdioStream::kStandardOutput), &port, sizeof(port)); diff --git a/util/util.gyp b/util/util.gyp index 45de7a62..5590b1f3 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -175,7 +175,6 @@ 'net/http_multipart_builder.h', 'net/http_transport.cc', 'net/http_transport.h', - 'net/http_transport_libcurl.cc', 'net/http_transport_mac.mm', 'net/http_transport_none.cc', 'net/http_transport_win.cc', @@ -383,15 +382,12 @@ ], }], ['OS=="linux"', { - 'link_settings': { - 'libraries': [ - '-lcurl', - ], - }, + 'sources': [ + 'net/http_transport_socket.cc', + ], }, { # else: OS!="linux" 'sources!': [ 'misc/capture_context_linux.S', - 'net/http_transport_libcurl.cc', ], }], ['OS!="android"', { From d4533dc92bffa2690e48ed9870bf1f5fa7fe1026 Mon Sep 17 00:00:00 2001 From: Takuto Ikuta Date: Fri, 27 Apr 2018 12:58:40 +0900 Subject: [PATCH 261/326] Add override for overridden function Bug: 428099 Change-Id: If8818d02fd6315ad46d512357db2b70d011a52b0 Reviewed-on: https://chromium-review.googlesource.com/1031992 Reviewed-by: Scott Graham Commit-Queue: Scott Graham --- snapshot/win/capture_memory_delegate_win.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/snapshot/win/capture_memory_delegate_win.h b/snapshot/win/capture_memory_delegate_win.h index d5615aee..175b4c95 100644 --- a/snapshot/win/capture_memory_delegate_win.h +++ b/snapshot/win/capture_memory_delegate_win.h @@ -54,7 +54,8 @@ class CaptureMemoryDelegateWin : public CaptureMemory::Delegate { bool ReadMemory(uint64_t at, uint64_t num_bytes, void* into) const override; std::vector> GetReadableRanges( const CheckedRange& range) const override; - void AddNewMemorySnapshot(const CheckedRange& range); + void AddNewMemorySnapshot( + const CheckedRange& range) override; private: CheckedRange stack_; From 63d331e57aac138acb26ee1444b6253882ab1204 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 27 Apr 2018 13:11:48 -0700 Subject: [PATCH 262/326] fuchsia: Fix child process reader test to do what it should be doing This "child" test was actually reading itself (whoops!). Instead, pass the address of the string to be read back from the child and read that. Bug: crashpad:196 Change-Id: I27aa4cd06c69cd492cb3387a5a773a56e9cb02a3 Reviewed-on: https://chromium-review.googlesource.com/1033712 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- snapshot/fuchsia/process_reader_fuchsia_test.cc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/snapshot/fuchsia/process_reader_fuchsia_test.cc b/snapshot/fuchsia/process_reader_fuchsia_test.cc index 35af291c..35ac6e68 100644 --- a/snapshot/fuchsia/process_reader_fuchsia_test.cc +++ b/snapshot/fuchsia/process_reader_fuchsia_test.cc @@ -60,6 +60,9 @@ TEST(ProcessReaderFuchsia, SelfBasic) { constexpr char kTestMemory[] = "Read me from another process"; CRASHPAD_CHILD_TEST_MAIN(ProcessReaderBasicChildTestMain) { + zx_vaddr_t addr = reinterpret_cast(kTestMemory); + CheckedWriteFile( + StdioFileHandle(StdioStream::kStandardOutput), &addr, sizeof(addr)); CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); return 0; } @@ -74,11 +77,13 @@ class BasicChildTest : public MultiprocessExec { private: void MultiprocessParent() override { ProcessReaderFuchsia process_reader; - ASSERT_TRUE(process_reader.Initialize(zx_process_self())); + ASSERT_TRUE(process_reader.Initialize(ChildProcess())); + + zx_vaddr_t addr; + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &addr, sizeof(addr))); std::string read_string; - ASSERT_TRUE(process_reader.Memory()->ReadCString( - reinterpret_cast(kTestMemory), &read_string)); + ASSERT_TRUE(process_reader.Memory()->ReadCString(addr, &read_string)); EXPECT_EQ(read_string, kTestMemory); } From 5636102fb418582be671c26c84626ab5aa9e89d4 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 27 Apr 2018 17:00:02 -0700 Subject: [PATCH 263/326] Implement unlengthed response read in HTTPTransport Of course, as soon as I tried it against the real endpoint on Fuchsia, the server just spits out raw crash id as a string without specifying Content-Length. Bug: crashpad:196, crashpad:30 Change-Id: I22af87589a8801cdfece0a7b862e70e0e7097f1f Reviewed-on: https://chromium-review.googlesource.com/1024953 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- client/crash_report_database_generic.cc | 18 +----------------- util/file/file_io.cc | 18 +++++++++++------- util/file/file_io.h | 8 +++++++- util/net/http_transport_socket.cc | 9 ++------- 4 files changed, 21 insertions(+), 32 deletions(-) diff --git a/client/crash_report_database_generic.cc b/client/crash_report_database_generic.cc index fc1d04e0..e10c24fd 100644 --- a/client/crash_report_database_generic.cc +++ b/client/crash_report_database_generic.cc @@ -30,22 +30,6 @@ namespace crashpad { namespace { -// Reads from the current file position to EOF and returns as a string of bytes. -bool ReadRestOfFileAsString(FileHandle handle, std::string* contents) { - char buffer[4096]; - FileOperationResult rv; - std::string local_contents; - while ((rv = ReadFile(handle, buffer, sizeof(buffer))) > 0) { - local_contents.append(buffer, rv); - } - if (rv < 0) { - PLOG(ERROR) << "ReadFile"; - return false; - } - contents->swap(local_contents); - return true; -} - base::FilePath ReplaceFinalExtension( const base::FilePath& path, const base::FilePath::StringType extension) { @@ -768,7 +752,7 @@ bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path, return false; } - if (!ReadRestOfFileAsString(handle.get(), &report->id)) { + if (!LoggingReadToEOF(handle.get(), &report->id)) { return false; } diff --git a/util/file/file_io.cc b/util/file/file_io.cc index f8a66304..55a6c5f1 100644 --- a/util/file/file_io.cc +++ b/util/file/file_io.cc @@ -156,16 +156,11 @@ void CheckedReadFileAtEOF(FileHandle file) { } } -bool LoggingReadEntireFile(const base::FilePath& path, std::string* contents) { - ScopedFileHandle handle(LoggingOpenFileForRead(path)); - if (!handle.is_valid()) { - return false; - } - +bool LoggingReadToEOF(FileHandle file, std::string* contents) { char buffer[4096]; FileOperationResult rv; std::string local_contents; - while ((rv = ReadFile(handle.get(), buffer, sizeof(buffer))) > 0) { + while ((rv = ReadFile(file, buffer, sizeof(buffer))) > 0) { DCHECK_LE(static_cast(rv), sizeof(buffer)); local_contents.append(buffer, rv); } @@ -177,6 +172,15 @@ bool LoggingReadEntireFile(const base::FilePath& path, std::string* contents) { return true; } +bool LoggingReadEntireFile(const base::FilePath& path, std::string* contents) { + ScopedFileHandle handle(LoggingOpenFileForRead(path)); + if (!handle.is_valid()) { + return false; + } + + return LoggingReadToEOF(handle.get(), contents); +} + void CheckedCloseFile(FileHandle file) { CHECK(LoggingCloseFile(file)); } diff --git a/util/file/file_io.h b/util/file/file_io.h index 050c0749..797db682 100644 --- a/util/file/file_io.h +++ b/util/file/file_io.h @@ -318,7 +318,13 @@ void CheckedWriteFile(FileHandle file, const void* buffer, size_t size); //! \sa ReadFile void CheckedReadFileAtEOF(FileHandle file); -//! brief Wraps LoggingOpenFileForRead() and ReadFile() reading the entire file +//! \brief Wraps ReadFile() to read from the current file position to the end of +//! the file into \a contents. +//! +//! \return `true` on success, or `false` with a message logged. +bool LoggingReadToEOF(FileHandle file, std::string* contents); + +//! \brief Wraps LoggingOpenFileForRead() and ReadFile() reading the entire file //! into \a contents. //! //! \return `true` on success, or `false` with a message logged. diff --git a/util/net/http_transport_socket.cc b/util/net/http_transport_socket.cc index 6e2e7aaa..a6bce455 100644 --- a/util/net/http_transport_socket.cc +++ b/util/net/http_transport_socket.cc @@ -367,14 +367,9 @@ bool ReadResponse(int sock, std::string* response_body) { if (it != response_headers.end() && it->second == "chunked") { chunked = true; } - if (!chunked) { - // TODO(scottmg): https://crashpad.chromium.org/bug/196. Doesn't happen - // in practice, but is possible. - LOG(ERROR) << "unimplemented non-chunked without Content-Length"; - return false; - } - return ReadContentChunked(sock, response_body); + return chunked ? ReadContentChunked(sock, response_body) + : LoggingReadToEOF(sock, response_body); } bool HTTPTransportSocket::ExecuteSynchronously(std::string* response_body) { From a107b8b95d044913b7e504c28e8179a5a527cf51 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 30 Apr 2018 12:57:03 -0700 Subject: [PATCH 264/326] fuchsia: Identify and include stack mapping in dump Bug: crashpad:196 Change-Id: I4d71502028ba1d961e53c0450c3ae88c6285f04e Reviewed-on: https://chromium-review.googlesource.com/1033358 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- snapshot/BUILD.gn | 2 + snapshot/fuchsia/memory_map_fuchsia.cc | 88 +++++++++++++++++++ snapshot/fuchsia/memory_map_fuchsia.h | 57 ++++++++++++ snapshot/fuchsia/process_reader_fuchsia.cc | 51 +++++++++++ snapshot/fuchsia/process_reader_fuchsia.h | 8 ++ .../fuchsia/process_reader_fuchsia_test.cc | 68 ++++++++++++++ snapshot/fuchsia/thread_snapshot_fuchsia.cc | 11 ++- 7 files changed, 282 insertions(+), 3 deletions(-) create mode 100644 snapshot/fuchsia/memory_map_fuchsia.cc create mode 100644 snapshot/fuchsia/memory_map_fuchsia.h diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index de0b8611..ed60fbea 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -178,6 +178,8 @@ static_library("snapshot") { sources += [ "fuchsia/cpu_context_fuchsia.cc", "fuchsia/cpu_context_fuchsia.h", + "fuchsia/memory_map_fuchsia.cc", + "fuchsia/memory_map_fuchsia.h", "fuchsia/process_reader_fuchsia.cc", "fuchsia/process_reader_fuchsia.h", "fuchsia/process_snapshot_fuchsia.cc", diff --git a/snapshot/fuchsia/memory_map_fuchsia.cc b/snapshot/fuchsia/memory_map_fuchsia.cc new file mode 100644 index 00000000..b60531d0 --- /dev/null +++ b/snapshot/fuchsia/memory_map_fuchsia.cc @@ -0,0 +1,88 @@ +// Copyright 2018 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/fuchsia/memory_map_fuchsia.h" + +#include + +#include "base/fuchsia/fuchsia_logging.h" +#include "util/numeric/checked_range.h" + +namespace crashpad { + +MemoryMapFuchsia::MemoryMapFuchsia() = default; + +MemoryMapFuchsia::~MemoryMapFuchsia() = default; + +bool MemoryMapFuchsia::Initialize(zx_handle_t process) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + // There's no way to know what an appropriate buffer size is before starting. + // Start at a size that should be more than enough for any reasonable process. + map_entries_.resize(4096); + + // Retrieving the maps is racy with new mappings being created, so retry this + // loop up to |tries| times until the number of actual mappings retrieved + // matches those available. + int tries = 5; + for (;;) { + size_t actual; + size_t available; + zx_status_t status = + zx_object_get_info(process, + ZX_INFO_PROCESS_MAPS, + &map_entries_[0], + map_entries_.size() * sizeof(map_entries_[0]), + &actual, + &available); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_object_get_info ZX_INFO_PROCESS_MAPS"; + map_entries_.clear(); + return false; + } + if (actual < available && tries-- > 0) { + // Make the buffer slightly larger than |available| to attempt to account + // for the race between here and the next retrieval. + map_entries_.resize(available + 20); + continue; + } + + map_entries_.resize(actual); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; + } +} + +bool MemoryMapFuchsia::FindMappingForAddress(zx_vaddr_t address, + zx_info_maps_t* map) const { + bool found = false; + zx_info_maps_t result = {}; + for (const auto& m : map_entries_) { + CheckedRange range(m.base, m.size); + if (range.ContainsValue(address)) { + if (!found || m.depth > result.depth) { + result = m; + found = true; + } + } + } + + if (found) { + *map = result; + } + return found; +} + +} // namespace crashpad diff --git a/snapshot/fuchsia/memory_map_fuchsia.h b/snapshot/fuchsia/memory_map_fuchsia.h new file mode 100644 index 00000000..88df5695 --- /dev/null +++ b/snapshot/fuchsia/memory_map_fuchsia.h @@ -0,0 +1,57 @@ +// Copyright 2018 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_FUCHSIA_MEMORY_MAP_FUCHSIA_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_FUCHSIA_H_ + +#include + +#include + +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +//! \brief A list of mappings in the address space of a Fuchsia process. +class MemoryMapFuchsia { + public: + MemoryMapFuchsia(); + ~MemoryMapFuchsia(); + + //! \brief Initializes this object with information about the mapped memory + //! regions in the given process. + //! + //! \return `true` on success, or `false`, with an error logged. + bool Initialize(zx_handle_t process); + + //! \brief Searches through the previously retrieved memory map for the given + //! address. If found, returns the deepest `zx_info_maps_t` mapping that + //! contains \a address. + //! + //! \param[in] address The address to locate. + //! \param[out] map The `zx_info_maps_t` data corresponding to the address. + //! \return `true` if a mapping for \a address was found, in which case \a map + //! will be filled out, otherwise `false` and \a map will be unchanged. + bool FindMappingForAddress(zx_vaddr_t address, zx_info_maps_t* map) const; + + private: + std::vector map_entries_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(MemoryMapFuchsia); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_FUCHSIA_H_ diff --git a/snapshot/fuchsia/process_reader_fuchsia.cc b/snapshot/fuchsia/process_reader_fuchsia.cc index e8f0fdc6..b9fcf16d 100644 --- a/snapshot/fuchsia/process_reader_fuchsia.cc +++ b/snapshot/fuchsia/process_reader_fuchsia.cc @@ -24,6 +24,53 @@ namespace crashpad { +namespace { + +// Based on the thread's SP and the process's memory map, attempts to figure out +// the stack regions for the thread. Fuchsia's C ABI specifies +// https://fuchsia.googlesource.com/zircon/+/master/docs/safestack.md so the +// callstack and locals-that-have-their-address-taken are in two different +// stacks. +void GetStackRegions( + const zx_thread_state_general_regs_t& regs, + const MemoryMapFuchsia& memory_map, + std::vector>* stack_regions) { + stack_regions->clear(); + + uint64_t sp; +#if defined(ARCH_CPU_X86_64) + sp = regs.rsp; +#elif defined(ARCH_CPU_ARM64) + sp = regs.sp; +#else +#error Port +#endif + + zx_info_maps_t range_with_sp; + if (!memory_map.FindMappingForAddress(sp, &range_with_sp)) { + LOG(ERROR) << "stack pointer not found in mapping"; + return; + } + + if (range_with_sp.type != ZX_INFO_MAPS_TYPE_MAPPING) { + LOG(ERROR) << "stack range has unexpected type, continuing anyway"; + } + + if (range_with_sp.u.mapping.mmu_flags & ZX_VM_FLAG_PERM_EXECUTE) { + LOG(ERROR) + << "stack range is unexpectedly marked executable, continuing anyway"; + } + + stack_regions->push_back( + CheckedRange(range_with_sp.base, range_with_sp.size)); + + // TODO(scottmg): https://crashpad.chromium.org/bug/196, once the retrievable + // registers include FS and similar for ARM, retrieve the region for the + // unsafe part of the stack too. +} + +} // namespace + ProcessReaderFuchsia::Module::Module() = default; ProcessReaderFuchsia::Module::~Module() = default; @@ -44,6 +91,8 @@ bool ProcessReaderFuchsia::Initialize(zx_handle_t process) { process_memory_.reset(new ProcessMemoryFuchsia()); process_memory_->Initialize(process_); + memory_map_.Initialize(process_); + INITIALIZATION_STATE_SET_VALID(initialized_); return true; } @@ -232,6 +281,8 @@ void ProcessReaderFuchsia::InitializeThreads() { ZX_LOG(WARNING, status) << "zx_thread_read_state"; } else { thread.general_registers = regs; + + GetStackRegions(regs, memory_map_, &thread.stack_regions); } } diff --git a/snapshot/fuchsia/process_reader_fuchsia.h b/snapshot/fuchsia/process_reader_fuchsia.h index 5efecb12..39d56895 100644 --- a/snapshot/fuchsia/process_reader_fuchsia.h +++ b/snapshot/fuchsia/process_reader_fuchsia.h @@ -23,8 +23,10 @@ #include "base/macros.h" #include "build/build_config.h" #include "snapshot/elf/elf_image_reader.h" +#include "snapshot/fuchsia/memory_map_fuchsia.h" #include "snapshot/module_snapshot.h" #include "util/misc/initialization_state_dcheck.h" +#include "util/numeric/checked_range.h" #include "util/process/process_memory_fuchsia.h" #include "util/process/process_memory_range.h" @@ -74,6 +76,11 @@ class ProcessReaderFuchsia { //! \brief The raw architecture-specific `zx_thread_state_general_regs_t` as //! returned by `zx_thread_read_state()`. zx_thread_state_general_regs_t general_registers = {}; + + //! \brief The regions representing the stack. The first entry in the vector + //! represents the callstack, and further entries optionally identify + //! other stack data when the thread uses a split stack representation. + std::vector> stack_regions; }; ProcessReaderFuchsia(); @@ -114,6 +121,7 @@ class ProcessReaderFuchsia { std::vector> module_readers_; std::vector> process_memory_ranges_; std::unique_ptr process_memory_; + MemoryMapFuchsia memory_map_; zx_handle_t process_; bool initialized_modules_ = false; bool initialized_threads_ = false; diff --git a/snapshot/fuchsia/process_reader_fuchsia_test.cc b/snapshot/fuchsia/process_reader_fuchsia_test.cc index 35ac6e68..e9d04834 100644 --- a/snapshot/fuchsia/process_reader_fuchsia_test.cc +++ b/snapshot/fuchsia/process_reader_fuchsia_test.cc @@ -14,11 +14,13 @@ #include "snapshot/fuchsia/process_reader_fuchsia.h" +#include #include #include #include "gtest/gtest.h" #include "test/multiprocess_exec.h" +#include "util/fuchsia/scoped_task_suspend.h" namespace crashpad { namespace test { @@ -95,6 +97,72 @@ TEST(ProcessReaderFuchsia, ChildBasic) { test.Run(); } +void* SignalAndSleep(void* arg) { + zx_object_signal(*reinterpret_cast(arg), 0, ZX_EVENT_SIGNALED); + zx_nanosleep(UINT64_MAX); + return nullptr; +} + +CRASHPAD_CHILD_TEST_MAIN(ProcessReaderChildThreadsTestMain) { + // Create 5 threads with stack sizes of 4096, 8192, ... + zx_handle_t events[5]; + zx_wait_item_t items[arraysize(events)]; + for (size_t i = 0; i < arraysize(events); ++i) { + pthread_t thread; + EXPECT_EQ(zx_event_create(0, &events[i]), ZX_OK); + pthread_attr_t attr; + EXPECT_EQ(pthread_attr_init(&attr), 0); + EXPECT_EQ(pthread_attr_setstacksize(&attr, (i + 1) * 4096), 0); + EXPECT_EQ(pthread_create(&thread, &attr, &SignalAndSleep, &events[i]), 0); + items[i].waitfor = ZX_EVENT_SIGNALED; + items[i].handle = events[i]; + } + + EXPECT_EQ(zx_object_wait_many(items, arraysize(items), ZX_TIME_INFINITE), + ZX_OK); + + char c = ' '; + CheckedWriteFile( + StdioFileHandle(StdioStream::kStandardOutput), &c, sizeof(c)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ThreadsChildTest : public MultiprocessExec { + public: + ThreadsChildTest() : MultiprocessExec() { + SetChildTestMainFunction("ProcessReaderChildThreadsTestMain"); + } + ~ThreadsChildTest() {} + + private: + void MultiprocessParent() override { + char c; + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &c, 1)); + ASSERT_EQ(c, ' '); + + ScopedTaskSuspend suspend(ChildProcess()); + + ProcessReaderFuchsia process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildProcess())); + + const auto& threads = process_reader.Threads(); + EXPECT_EQ(threads.size(), 6u); + + for (size_t i = 1; i < 6; ++i) { + ASSERT_GT(threads[i].stack_regions.size(), 0u); + EXPECT_EQ(threads[i].stack_regions[0].size(), i * 4096u); + } + } + + DISALLOW_COPY_AND_ASSIGN(ThreadsChildTest); +}; + +TEST(ProcessReaderFuchsia, ChildThreads) { + ThreadsChildTest test; + test.Run(); +} + } // namespace } // namespace test } // namespace crashpad diff --git a/snapshot/fuchsia/thread_snapshot_fuchsia.cc b/snapshot/fuchsia/thread_snapshot_fuchsia.cc index a4e0c1a9..03055d8c 100644 --- a/snapshot/fuchsia/thread_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/thread_snapshot_fuchsia.cc @@ -50,9 +50,14 @@ bool ThreadSnapshotFuchsia::Initialize( #error Port. #endif - // TODO(scottmg): https://crashpad.chromium.org/bug/196. Initialize stack_ and - // TLS address here. API request for stack range filed upstream at ZX-1748. - stack_.Initialize(process_reader, 0, 0); + if (thread.stack_regions.empty()) { + stack_.Initialize(process_reader, 0, 0); + } else { + stack_.Initialize(process_reader, + thread.stack_regions[0].base(), + thread.stack_regions[0].size()); + // TODO(scottmg): Handle split stack by adding other parts to ExtraMemory(). + } thread_id_ = thread.id; From bce68d7975baaac1345243167bc2cb9bdb445bce Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 30 Apr 2018 12:58:11 -0700 Subject: [PATCH 265/326] fuchsia: Miscellaneous fixes to get symbol resolution working - Endian-swaps the 3 integer fields of the build id when returning it for use as the module id (see bug 229). - Removes the "app:" prefix on the main binary, as this prevents the crash server from matching the binary name (and it isn't particularly useful anyway) - Map "" to "libzircon.so" as that's what it actually is, so that symbols for it can be found. Bug: crashpad:196, crashpad:229 Change-Id: Ie4abc732b7696345b96c34dbb1a7d2cc2cfcf77f Reviewed-on: https://chromium-review.googlesource.com/1035461 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- snapshot/elf/module_snapshot_elf.cc | 9 +++++++++ snapshot/fuchsia/process_reader_fuchsia.cc | 14 ++++++++++++-- snapshot/fuchsia/process_reader_fuchsia.h | 3 +-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/snapshot/elf/module_snapshot_elf.cc b/snapshot/elf/module_snapshot_elf.cc index 5c5039ee..fbea74ba 100644 --- a/snapshot/elf/module_snapshot_elf.cc +++ b/snapshot/elf/module_snapshot_elf.cc @@ -14,6 +14,8 @@ #include "snapshot/elf/module_snapshot_elf.h" +#include + #include #include "base/files/file_path.h" @@ -144,6 +146,13 @@ void ModuleSnapshotElf::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const { notes->NextNote(nullptr, nullptr, &desc); desc.insert(desc.end(), 16 - std::min(desc.size(), size_t{16}), '\0'); uuid->InitializeFromBytes(reinterpret_cast(&desc[0])); + + // TODO(scottmg): https://crashpad.chromium.org/bug/229. These are + // endian-swapped to match FileID::ConvertIdentifierToUUIDString() in + // Breakpad. This is necessary as this identifier is used for symbol lookup. + uuid->data_1 = htobe32(uuid->data_1); + uuid->data_2 = htobe16(uuid->data_2); + uuid->data_3 = htobe16(uuid->data_3); } std::string ModuleSnapshotElf::DebugFileName() const { diff --git a/snapshot/fuchsia/process_reader_fuchsia.cc b/snapshot/fuchsia/process_reader_fuchsia.cc index b9fcf16d..0a6a84bb 100644 --- a/snapshot/fuchsia/process_reader_fuchsia.cc +++ b/snapshot/fuchsia/process_reader_fuchsia.cc @@ -130,7 +130,7 @@ void ProcessReaderFuchsia::InitializeModules() { // retrieves (some of) the data into internal structures. It may be worth // trying to refactor/upstream some of this into Fuchsia. - std::string app_name("app:"); + std::string app_name; { char name[ZX_MAX_NAME_LEN]; zx_status_t status = @@ -140,7 +140,7 @@ void ProcessReaderFuchsia::InitializeModules() { return; } - app_name += name; + app_name = name; } // Starting from the ld.so's _dl_debug_addr, read the link_map structure and @@ -206,6 +206,16 @@ void ProcessReaderFuchsia::InitializeModules() { LOG(ERROR) << "ReadCString name"; } + // The vDSO is libzircon.so, but it's not actually loaded normally, it's + // injected by the kernel, so doesn't have a normal name. When dump_syms is + // run on libzircon.so, it uses that file name, and in order for the crash + // server to match symbols both the debug id and the name of the binary have + // to match. So, map from "" to "libzircon.so" so that symbol + // resolution works correctly. + if (dsoname == "") { + dsoname = "libzircon.so"; + } + Module module; if (dsoname.empty()) { module.name = app_name; diff --git a/snapshot/fuchsia/process_reader_fuchsia.h b/snapshot/fuchsia/process_reader_fuchsia.h index 39d56895..2d0878c3 100644 --- a/snapshot/fuchsia/process_reader_fuchsia.h +++ b/snapshot/fuchsia/process_reader_fuchsia.h @@ -41,8 +41,7 @@ class ProcessReaderFuchsia { Module(); ~Module(); - //! \brief The `ZX_PROP_NAME` of the module. Will be prepended with "app:" - //! for the main executable. + //! \brief The `ZX_PROP_NAME` of the module. std::string name; //! \brief An image reader for the module. From d766e659bb3e5674d421db52e9c4f2c8ed3b5c91 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 30 Apr 2018 14:10:36 -0700 Subject: [PATCH 266/326] Roll mini_chromium to 40cb6722 mini_chromium$ git log --oneline 6e0fdb2e..40cb6722 40cb672 fuchsia: Update mini_chromium/Crashpad build for SDK reorg ce9be13 Set |posix| to false for the Fuchsia build. ff7ad85 win: Use target_out_dir instead of source_out_dir for pdbname Bug: crashpad:196 Change-Id: I8d23b0dece8222a2dc883f6c4e4d43aaf7f5fc89 Reviewed-on: https://chromium-review.googlesource.com/1036189 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index e6719e2a..f027c358 100644 --- a/DEPS +++ b/DEPS @@ -29,7 +29,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '6e0fdb2e4966ec44b1ce7b8464fd7c80d1b59203', + '40cb6722bbc6bd6fb5fccecd80362313440a0537', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', From 240e28df2b886198ddd4d9b0c07f10cb38d7c1c7 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 1 May 2018 12:21:38 -0700 Subject: [PATCH 267/326] Make run_with_crashpad buildable on Fuchsia too Exception handling in Zircon is very similar to Mach (https://fuchsia.googlesource.com/zircon/+/HEAD/docs/exceptions.md), so run_with_crashpad will be useful for testing. Bug: crashpad:196 Change-Id: Ib6956be284e92671c2e338a5056c18deb948daff Reviewed-on: https://chromium-review.googlesource.com/1036191 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- tools/BUILD.gn | 30 +++++++++++++++------------- tools/{mac => }/run_with_crashpad.cc | 0 tools/{mac => }/run_with_crashpad.md | 8 ++++---- tools/tools.gyp | 2 +- 4 files changed, 21 insertions(+), 19 deletions(-) rename tools/{mac => }/run_with_crashpad.cc (100%) rename tools/{mac => }/run_with_crashpad.md (93%) diff --git a/tools/BUILD.gn b/tools/BUILD.gn index a711bdf7..cd1e95f4 100644 --- a/tools/BUILD.gn +++ b/tools/BUILD.gn @@ -91,6 +91,22 @@ crashpad_executable("generate_dump") { } } +if (crashpad_is_mac || crashpad_is_fuchsia) { + crashpad_executable("run_with_crashpad") { + sources = [ + "run_with_crashpad.cc", + ] + + deps = [ + ":tool_support", + "../client", + "../compat", + "../third_party/mini_chromium:base", + "../util", + ] + } +} + if (crashpad_is_mac) { crashpad_executable("catch_exception_tool") { sources = [ @@ -147,18 +163,4 @@ if (crashpad_is_mac) { "../util", ] } - - crashpad_executable("run_with_crashpad") { - sources = [ - "mac/run_with_crashpad.cc", - ] - - deps = [ - ":tool_support", - "../client", - "../compat", - "../third_party/mini_chromium:base", - "../util", - ] - } } diff --git a/tools/mac/run_with_crashpad.cc b/tools/run_with_crashpad.cc similarity index 100% rename from tools/mac/run_with_crashpad.cc rename to tools/run_with_crashpad.cc diff --git a/tools/mac/run_with_crashpad.md b/tools/run_with_crashpad.md similarity index 93% rename from tools/mac/run_with_crashpad.md rename to tools/run_with_crashpad.md index c87cb150..9758f9af 100644 --- a/tools/mac/run_with_crashpad.md +++ b/tools/run_with_crashpad.md @@ -32,10 +32,10 @@ setting an exception port referencing the handler. Then, executes _COMMAND_ along with any arguments specified (_ARG…_) with the new exception port in effect. -The exception port is configured to receive exceptions of type `EXC_CRASH`, -`EXC_RESOURCE`, and `EXC_GUARD`. The exception behavior is configured as -`EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES`. The thread state flavor is -set to `MACHINE_THREAD_STATE`. +On macOS, the exception port is configured to receive exceptions of type +`EXC_CRASH`, `EXC_RESOURCE`, and `EXC_GUARD`. The exception behavior is +configured as `EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES`. The thread +state flavor is set to `MACHINE_THREAD_STATE`. Programs that use the Crashpad client library directly will not normally use this tool. This tool exists to allow programs that are unaware of Crashpad to be diff --git a/tools/tools.gyp b/tools/tools.gyp index 3639b531..d2ab29aa 100644 --- a/tools/tools.gyp +++ b/tools/tools.gyp @@ -200,7 +200,7 @@ '..', ], 'sources': [ - 'mac/run_with_crashpad.cc', + 'run_with_crashpad.cc', ], }, ], From a279673974e0d0ab9f1b70c29eef351aa83f6131 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 1 May 2018 13:41:06 -0700 Subject: [PATCH 268/326] fuchsia: Implement execvp() equivalent for run_with_crashpad There's no implementation of execvp() on Fuchsia, so attempt to emulate it in `run_with_crashpad` by using launchpad. Failures are mapped to exit codes similar to what the execvp() path would return for missing binary, and other non-subprocess errors. Bug: crashpad:196 Change-Id: I042cbcf82bfd4560442a9d7f301e97bbfea54042 Reviewed-on: https://chromium-review.googlesource.com/1038055 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- tools/BUILD.gn | 8 ++++++ tools/run_with_crashpad.cc | 54 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/tools/BUILD.gn b/tools/BUILD.gn index cd1e95f4..eb76a240 100644 --- a/tools/BUILD.gn +++ b/tools/BUILD.gn @@ -104,6 +104,14 @@ if (crashpad_is_mac || crashpad_is_fuchsia) { "../third_party/mini_chromium:base", "../util", ] + + if (crashpad_is_fuchsia) { + if (crashpad_is_in_fuchsia) { + deps += [ "//zircon/public/lib/launchpad" ] + } else { + libs = [ "launchpad" ] + } + } } } diff --git a/tools/run_with_crashpad.cc b/tools/run_with_crashpad.cc index 39095145..a65f5256 100644 --- a/tools/run_with_crashpad.cc +++ b/tools/run_with_crashpad.cc @@ -25,11 +25,20 @@ #include "base/files/file_path.h" #include "base/logging.h" +#include "build/build_config.h" #include "client/crashpad_client.h" #include "tools/tool_support.h" #include "util/stdlib/map_insert.h" #include "util/string/split_string.h" +#if defined(OS_FUCHSIA) +#include +#include +#include + +#include "base/fuchsia/fuchsia_logging.h" +#endif + namespace crashpad { namespace { @@ -38,6 +47,13 @@ void Usage(const std::string& me) { "Usage: %s [OPTION]... COMMAND [ARG]...\n" "Start a Crashpad handler and have it handle crashes from COMMAND.\n" "\n" +#if defined(OS_FUCHSIA) +"COMMAND is run via launchpad, so must be a qualified path to the subprocess to\n" +"be executed.\n" +#else +"COMMAND is run via execvp() so the PATH will be searched.\n" +#endif +"\n" " -h, --handler=HANDLER invoke HANDLER instead of crashpad_handler\n" " --annotation=KEY=VALUE passed to the handler as an --annotation argument\n" " --database=PATH passed to the handler as its --database argument\n" @@ -173,11 +189,49 @@ int RunWithCrashpadMain(int argc, char* argv[]) { return kExitFailure; } +#if defined(OS_FUCHSIA) + // Fuchsia doesn't implement execvp(), launch with launchpad here. + launchpad_t* lp; + launchpad_create(zx_job_default(), argv[0], &lp); + launchpad_load_from_file(lp, argv[0]); + launchpad_set_args(lp, argc, argv); + launchpad_clone(lp, LP_CLONE_ALL); + const char* error_message; + zx_handle_t child; + zx_status_t status = launchpad_go(lp, &child, &error_message); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "launchpad_go: " << error_message; + return status == ZX_ERR_IO ? kExitExecENOENT : kExitExecFailure; + } + + zx_signals_t observed; + status = zx_object_wait_one( + child, ZX_TASK_TERMINATED, ZX_TIME_INFINITE, &observed); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_object_wait_one"; + return kExitExecFailure; + } + if (!(observed & ZX_TASK_TERMINATED)) { + LOG(ERROR) << "did not observe ZX_TASK_TERMINATED"; + return kExitExecFailure; + } + + zx_info_process_t proc_info; + status = zx_object_get_info( + child, ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), nullptr, nullptr); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_object_get_info"; + return kExitExecFailure; + } + + return proc_info.return_code; +#else // Using the remaining arguments, start a new program with the new exception // port in effect. execvp(argv[0], argv); PLOG(ERROR) << "execvp " << argv[0]; return errno == ENOENT ? kExitExecENOENT : kExitExecFailure; +#endif } } // namespace From 847d06db2946a8b3c0a35a265c2bebb2dd57708a Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 1 May 2018 15:32:27 -0700 Subject: [PATCH 269/326] fuchsia: Add header with shared exception port key When binding to an exception port on Fuchsia, a key is supplied and passed back to coordinate between the registerer and the handler. An arbitrary value is used by both devmgr: https://fuchsia.googlesource.com/zircon/+/HEAD/system/core/devmgr/devmgr.c#203 and by crashlogger: https://fuchsia.googlesource.com/zircon/+/HEAD/system/core/crashlogger/crashlogger.cpp#149 . In order to be able to have crashpad_handler be a drop-in for crashlogger (at least for now), Crashpad will use this same key in subsequent patches for this purpose. Pull this value out in a header so it can be shared by different bits that will need to refer to it. Bug: crashpad:196 Change-Id: I00e0178156a792bd80fc83b1b7d85b5ce6742e9a Reviewed-on: https://chromium-review.googlesource.com/1038123 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- util/BUILD.gn | 1 + util/fuchsia/system_exception_port_key.h | 27 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 util/fuchsia/system_exception_port_key.h diff --git a/util/BUILD.gn b/util/BUILD.gn index d3578dd4..21e6fed2 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -379,6 +379,7 @@ static_library("util") { if (crashpad_is_fuchsia) { sources += [ + "fuchsia/system_exception_port_key.h", "fuchsia/koid_utilities.cc", "fuchsia/koid_utilities.h", "fuchsia/scoped_task_suspend.cc", diff --git a/util/fuchsia/system_exception_port_key.h b/util/fuchsia/system_exception_port_key.h new file mode 100644 index 00000000..0bbae690 --- /dev/null +++ b/util/fuchsia/system_exception_port_key.h @@ -0,0 +1,27 @@ +// Copyright 2018 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_UTIL_FUCHSIA_EXCEPTION_PORT_KEY_H_ +#define CRASHPAD_UTIL_FUCHSIA_EXCEPTION_PORT_KEY_H_ + +namespace crashpad { + +//! \brief The key used in `zx_task_bind_exception_port()` and packet +//! processing. This matches the value that Zircon's `devmgr` and +//! `crashlogger` use for interoperability, for now. +constexpr uint64_t kSystemExceptionPortKey = 1166444u; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_FUCHSIA_EXCEPTION_PORT_KEY_H_ From e78789b9e0c2d1528ba8ec8e9af8bff0a269ced1 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 3 May 2018 13:31:48 -0700 Subject: [PATCH 270/326] fuchsia: Add implementation of ExceptionSnapshot Bug: crashpad:196 Change-Id: I5aea3484c185096898bafe847c83474a91f5d8c7 Reviewed-on: https://chromium-review.googlesource.com/1038128 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- snapshot/BUILD.gn | 2 + .../fuchsia/exception_snapshot_fuchsia.cc | 129 ++++++++++++++++++ snapshot/fuchsia/exception_snapshot_fuchsia.h | 80 +++++++++++ 3 files changed, 211 insertions(+) create mode 100644 snapshot/fuchsia/exception_snapshot_fuchsia.cc create mode 100644 snapshot/fuchsia/exception_snapshot_fuchsia.h diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index ed60fbea..825c9f16 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -178,6 +178,8 @@ static_library("snapshot") { sources += [ "fuchsia/cpu_context_fuchsia.cc", "fuchsia/cpu_context_fuchsia.h", + "fuchsia/exception_snapshot_fuchsia.cc", + "fuchsia/exception_snapshot_fuchsia.h", "fuchsia/memory_map_fuchsia.cc", "fuchsia/memory_map_fuchsia.h", "fuchsia/process_reader_fuchsia.cc", diff --git a/snapshot/fuchsia/exception_snapshot_fuchsia.cc b/snapshot/fuchsia/exception_snapshot_fuchsia.cc new file mode 100644 index 00000000..44b4e5cd --- /dev/null +++ b/snapshot/fuchsia/exception_snapshot_fuchsia.cc @@ -0,0 +1,129 @@ +// Copyright 2018 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/fuchsia/exception_snapshot_fuchsia.h" + +#include "base/numerics/safe_conversions.h" +#include "snapshot/fuchsia/cpu_context_fuchsia.h" +#include "snapshot/fuchsia/process_reader_fuchsia.h" + +namespace crashpad { +namespace internal { + +ExceptionSnapshotFuchsia::ExceptionSnapshotFuchsia() = default; +ExceptionSnapshotFuchsia::~ExceptionSnapshotFuchsia() = default; + +void ExceptionSnapshotFuchsia::Initialize( + ProcessReaderFuchsia* process_reader, + zx_koid_t thread_id, + const zx_exception_report_t& exception_report) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + exception_ = exception_report.header.type; + thread_id_ = thread_id; + + // TODO(scottmg): Not sure whether these values for exception_info_ are + // helpful or correct. Other values in the structures are stored below into + // Codes() in case they are useful. +#if defined(ARCH_CPU_X86_64) + DCHECK(base::IsValueInRangeForNumericType( + exception_report.context.arch.u.x86_64.err_code)); + exception_info_ = exception_report.context.arch.u.x86_64.err_code; +#elif defined(ARCH_CPU_ARM64) + exception_info_ = exception_report.context.arch.u.arm_64.esr; +#endif + + codes_.push_back(exception_); + codes_.push_back(exception_info_); + +#if defined(ARCH_CPU_X86_64) + codes_.push_back(exception_report.context.arch.u.x86_64.vector); + codes_.push_back(exception_report.context.arch.u.x86_64.cr2); +#elif defined(ARCH_CPU_ARM64) + codes_.push_back(exception_report.context.arch.u.arm_64.far); +#endif + + for (const auto& t : process_reader->Threads()) { + if (t.id == thread_id) { +#if defined(ARCH_CPU_X86_64) + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_arch_; + // TODO(scottmg): Float context, once Fuchsia has a debug API to capture + // floating point registers. ZX-1750 upstream. + InitializeCPUContextX86_64(t.general_registers, context_.x86_64); +#elif defined(ARCH_CPU_ARM64) + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_arch_; + // TODO(scottmg): Implement context capture for arm64. +#else +#error Port. +#endif + } + } + + if (context_.InstructionPointer() != 0 && + (exception_ == ZX_EXCP_UNDEFINED_INSTRUCTION || + exception_ == ZX_EXCP_SW_BREAKPOINT || + exception_ == ZX_EXCP_HW_BREAKPOINT)) { + exception_address_ = context_.InstructionPointer(); + } else { +#if defined(ARCH_CPU_X86_64) + exception_address_ = exception_report.context.arch.u.x86_64.cr2; +#elif defined(ARCH_CPU_ARM64) + exception_address_ = exception_report.context.arch.u.arm_64.far; +#endif + } + + + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +const CPUContext* ExceptionSnapshotFuchsia::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +uint64_t ExceptionSnapshotFuchsia::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +uint32_t ExceptionSnapshotFuchsia::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_; +} + +uint32_t ExceptionSnapshotFuchsia::ExceptionInfo() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_info_; +} + +uint64_t ExceptionSnapshotFuchsia::ExceptionAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_address_; +} + +const std::vector& ExceptionSnapshotFuchsia::Codes() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return codes_; +} + +std::vector ExceptionSnapshotFuchsia::ExtraMemory() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/fuchsia/exception_snapshot_fuchsia.h b/snapshot/fuchsia/exception_snapshot_fuchsia.h new file mode 100644 index 00000000..eda1c66d --- /dev/null +++ b/snapshot/fuchsia/exception_snapshot_fuchsia.h @@ -0,0 +1,80 @@ +// Copyright 2018 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_FUCHSIA_EXCEPTION_SNAPSHOT_FUCHSIA_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_EXCEPTION_SNAPSHOT_FUCHSIA_H_ + +#include +#include +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/exception_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +class ProcessReaderFuchsia; + +namespace internal { + +//! \brief An ExceptionSnapshot of an exception sustained by a process on a +//! Fuchsia system. +class ExceptionSnapshotFuchsia final : public ExceptionSnapshot { + public: + ExceptionSnapshotFuchsia(); + ~ExceptionSnapshotFuchsia() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReaderFuchsia for the process that + //! sustained the exception. + //! \param[in] thread_id The koid of the thread that sustained the exception. + //! \param[in] exception_report The `zx_exception_report_t` retrieved from the + //! thread in the exception state, corresponding to \a thread_id. + void Initialize(ProcessReaderFuchsia* process_reader, + zx_koid_t thread_id, + const zx_exception_report_t& exception_report); + + // ExceptionSnapshot: + const CPUContext* Context() const override; + uint64_t ThreadID() const override; + uint32_t Exception() const override; + uint32_t ExceptionInfo() const override; + uint64_t ExceptionAddress() const override; + const std::vector& Codes() const override; + std::vector ExtraMemory() const override; + + private: +#if defined(ARCH_CPU_X86_64) + CPUContextX86_64 context_arch_; +#elif defined(ARCH_CPU_ARM64) + CPUContextARM64 context_arch_; +#endif + CPUContext context_; + std::vector codes_; + zx_koid_t thread_id_; + zx_vaddr_t exception_address_; + uint32_t exception_; + uint32_t exception_info_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionSnapshotFuchsia); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_EXCEPTION_SNAPSHOT_FUCHSIA_H_ From c82309f0e599b10338b3c89d7449fba3afae3bd6 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 4 May 2018 12:20:52 -0700 Subject: [PATCH 271/326] fuchsia: Implement StartHandler() and ExceptionHandlerServer StartHandler() binds to the default job's exception port, and launches the handler process (normally this is crashpad_handler), passing it the task handle and a handle to the exception port as startup parameters. This follows the protocol used by crashlogger. Additionally, implement ExceptionHandlerServer in crashpad_handler, which contains the exception processing loop. It currently dispatches to an empty CrashReportExceptionHandler where a report will be written eventually. Bug: crashpad:196 Change-Id: Ie27ff6f67adfbcc7d03551ae7e84a885da43df5a Reviewed-on: https://chromium-review.googlesource.com/1043282 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- client/BUILD.gn | 10 +++ client/crashpad_client.h | 3 + client/crashpad_client_fuchsia.cc | 84 ++++++++++++++++++- .../fuchsia/crash_report_exception_handler.h | 20 +++++ handler/fuchsia/exception_handler_server.cc | 37 +++++++- handler/fuchsia/exception_handler_server.h | 15 +++- handler/handler_main.cc | 38 +++++++-- 7 files changed, 194 insertions(+), 13 deletions(-) diff --git a/client/BUILD.gn b/client/BUILD.gn index 9d8b736f..9d1caaf1 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -87,6 +87,16 @@ static_library("client") { libs = [ "rpcrt4.lib" ] cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union } + + if (crashpad_is_fuchsia) { + if (crashpad_is_in_fuchsia) { + deps += [ + "//zircon/public/lib/launchpad", + ] + } else { + libs = [ "launchpad" ] + } + } } source_set("client_test") { diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 267a6e20..ae409d43 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -75,6 +75,9 @@ class CrashpadClient { //! Crashpad. Optionally, use WaitForHandlerStart() to join with the //! background thread and retrieve the status of handler startup. //! + //! On Fuchsia, this method binds to the exception port of the current default + //! job, and starts a Crashpad handler to monitor that port. + //! //! \param[in] handler The path to a Crashpad handler executable. //! \param[in] database The path to a Crashpad database. The handler will be //! started with this path as its `--database` argument. diff --git a/client/crashpad_client_fuchsia.cc b/client/crashpad_client_fuchsia.cc index ea179b85..59cf3873 100644 --- a/client/crashpad_client_fuchsia.cc +++ b/client/crashpad_client_fuchsia.cc @@ -14,7 +14,16 @@ #include "client/crashpad_client.h" +#include +#include +#include + +#include "base/fuchsia/fuchsia_logging.h" +#include "base/fuchsia/scoped_zx_handle.h" #include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "client/client_argv_handling.h" +#include "util/fuchsia/system_exception_port_key.h" namespace crashpad { @@ -31,8 +40,79 @@ bool CrashpadClient::StartHandler( const std::vector& arguments, bool restartable, bool asynchronous_start) { - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return false; + DCHECK_EQ(restartable, false); // Not used on Fuchsia. + DCHECK_EQ(asynchronous_start, false); // Not used on Fuchsia. + + zx_handle_t exception_port_raw; + zx_status_t status = zx_port_create(0, &exception_port_raw); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_port_create"; + return false; + } + base::ScopedZxHandle exception_port(exception_port_raw); + + status = zx_task_bind_exception_port( + zx_job_default(), exception_port.get(), kSystemExceptionPortKey, 0); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_task_bind_exception_port"; + return false; + } + + std::vector argv_strings; + BuildHandlerArgvStrings(handler, + database, + metrics_dir, + url, + annotations, + arguments, + &argv_strings); + + std::vector argv; + ConvertArgvStrings(argv_strings, &argv); + // ConvertArgvStrings adds an unnecessary nullptr at the end of the argv list, + // which causes launchpad_set_args() to hang. + argv.pop_back(); + + launchpad_t* lp; + launchpad_create(zx_job_default(), argv[0], &lp); + launchpad_load_from_file(lp, argv[0]); + launchpad_set_args(lp, argv.size(), &argv[0]); + + // TODO(scottmg): https://crashpad.chromium.org/bug/196, this is useful during + // bringup, but should probably be made minimal for real usage. + launchpad_clone(lp, + LP_CLONE_FDIO_NAMESPACE | LP_CLONE_FDIO_STDIO | + LP_CLONE_ENVIRON | LP_CLONE_DEFAULT_JOB); + + // Follow the same protocol as devmgr and crashlogger in Zircon (that is, + // process handle as handle 0, with type USER0, exception port handle as + // handle 1, also with type PA_USER0) so that it's trivial to replace + // crashlogger with crashpad_handler. The exception port is passed on, so + // released here. Currently it is assumed that this process's default job + // handle is the exception port that should be monitored. In the future, it + // might be useful for this to be configurable by the client. + zx_handle_t handles[] = {ZX_HANDLE_INVALID, ZX_HANDLE_INVALID}; + status = + zx_handle_duplicate(zx_job_default(), ZX_RIGHT_SAME_RIGHTS, &handles[0]); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_handle_duplicate"; + return false; + } + handles[1] = exception_port.release(); + uint32_t handle_types[] = {PA_HND(PA_USER0, 0), PA_HND(PA_USER0, 1)}; + + launchpad_add_handles(lp, arraysize(handles), handles, handle_types); + + const char* error_message; + zx_handle_t child_raw; + status = launchpad_go(lp, &child_raw, &error_message); + base::ScopedZxHandle child(child_raw); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "launchpad_go: " << error_message; + return false; + } + + return true; } } // namespace crashpad diff --git a/handler/fuchsia/crash_report_exception_handler.h b/handler/fuchsia/crash_report_exception_handler.h index c987a5dc..eefbd54c 100644 --- a/handler/fuchsia/crash_report_exception_handler.h +++ b/handler/fuchsia/crash_report_exception_handler.h @@ -15,6 +15,8 @@ #ifndef CRASHPAD_HANDLER_FUCHSIA_CRASH_REPORT_EXCEPTION_HANDLER_H_ #define CRASHPAD_HANDLER_FUCHSIA_CRASH_REPORT_EXCEPTION_HANDLER_H_ +#include + #include #include @@ -56,6 +58,24 @@ class CrashReportExceptionHandler { ~CrashReportExceptionHandler(); + //! \brief Called when the exception handler server has caught an exception + //! and wants a crash dump to be taken. + //! + //! This function is expected to call `zx_task_resume()` in order to complete + //! handling of the exception. + //! + //! \note TODO(scottmg): This is not yet implemented. + //! + //! \param[in] type The type of exception, a `ZX_EXCP_*` value. + //! \param[in] pid The koid of the process which sustained the exception. + //! \param[in] tid The koid of the thread which sustained the exception. + //! \return `true` on success, or `false` with an error logged. + bool HandleException(uint32_t type, + uint64_t pid, + uint64_t tid) { + return false; + } + private: DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler); }; diff --git a/handler/fuchsia/exception_handler_server.cc b/handler/fuchsia/exception_handler_server.cc index d1dfcdf0..550a04af 100644 --- a/handler/fuchsia/exception_handler_server.cc +++ b/handler/fuchsia/exception_handler_server.cc @@ -14,16 +14,47 @@ #include "handler/fuchsia/exception_handler_server.h" +#include +#include + +#include + +#include "base/fuchsia/fuchsia_logging.h" #include "base/logging.h" +#include "handler/fuchsia/crash_report_exception_handler.h" +#include "util/fuchsia/system_exception_port_key.h" namespace crashpad { -ExceptionHandlerServer::ExceptionHandlerServer() {} +ExceptionHandlerServer::ExceptionHandlerServer( + base::ScopedZxHandle root_job, + base::ScopedZxHandle exception_port) + : root_job_(std::move(root_job)), + exception_port_(std::move(exception_port)) {} -ExceptionHandlerServer::~ExceptionHandlerServer() {} +ExceptionHandlerServer::~ExceptionHandlerServer() = default; void ExceptionHandlerServer::Run(CrashReportExceptionHandler* handler) { - NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + while (true) { + zx_port_packet_t packet; + zx_status_t status = + zx_port_wait(exception_port_.get(), ZX_TIME_INFINITE, &packet, 1); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_port_wait, aborting"; + return; + } + + if (packet.key != kSystemExceptionPortKey) { + LOG(ERROR) << "unexpected packet key, ignoring"; + continue; + } + + bool result = handler->HandleException( + packet.type, packet.exception.pid, packet.exception.tid); + if (!result) { + LOG(ERROR) << "HandleException failed"; + } + } } } // namespace crashpad diff --git a/handler/fuchsia/exception_handler_server.h b/handler/fuchsia/exception_handler_server.h index b998ba9c..184d1489 100644 --- a/handler/fuchsia/exception_handler_server.h +++ b/handler/fuchsia/exception_handler_server.h @@ -16,17 +16,25 @@ #define CRASHPAD_HANDLER_FUCHSIA_EXCEPTION_HANDLER_SERVER_H_ #include "base/macros.h" +#include "base/fuchsia/scoped_zx_handle.h" namespace crashpad { class CrashReportExceptionHandler; //! \brief Runs the main exception-handling server in Crashpad's handler -//! process. This class is not yet implemented. +//! process. class ExceptionHandlerServer { public: //! \brief Constructs an ExceptionHandlerServer object. - ExceptionHandlerServer(); + //! + //! \param[in] root_job The root of the tree of processes that will be handled + //! by this server. It is assumed that \a exception_port is the exception + //! port of this job. + //! \param[in] exception_port The exception port that this server will + //! monitor. + ExceptionHandlerServer(base::ScopedZxHandle root_job, + base::ScopedZxHandle exception_port); ~ExceptionHandlerServer(); //! \brief Runs the exception-handling server. @@ -36,6 +44,9 @@ class ExceptionHandlerServer { void Run(CrashReportExceptionHandler* handler); private: + base::ScopedZxHandle root_job_; + base::ScopedZxHandle exception_port_; + DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer); }; diff --git a/handler/handler_main.cc b/handler/handler_main.cc index dd7adf65..70192cf4 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -83,6 +83,9 @@ #include "util/win/initial_client_data.h" #include "util/win/session_end_watcher.h" #elif defined(OS_FUCHSIA) +#include +#include + #include "handler/fuchsia/crash_report_exception_handler.h" #include "handler/fuchsia/exception_handler_server.h" #elif defined(OS_LINUX) @@ -390,17 +393,19 @@ void InstallCrashHandler() { ALLOW_UNUSED_LOCAL(terminate_handler); } -#elif defined(OS_FUCHSIA) || defined(OS_LINUX) +#elif defined(OS_FUCHSIA) void InstallCrashHandler() { - // TODO(scottmg): Fuchsia: https://crashpad.chromium.org/bug/196 - // TODO(jperaza): Linux: https://crashpad.chromium.org/bug/30 - NOTREACHED(); + // There's nothing to do here. Crashes in this process will already be caught + // here because this handler process is in the same job that has had its + // exception port bound. + + // TODO(scottmg): This should collect metrics on handler crashes, at a + // minimum. https://crashpad.chromium.org/bug/230. } void ReinstallCrashHandler() { // TODO(scottmg): Fuchsia: https://crashpad.chromium.org/bug/196 - // TODO(jperaza): Linux: https://crashpad.chromium.org/bug/30 NOTREACHED(); } @@ -901,7 +906,28 @@ int HandlerMain(int argc, if (!options.pipe_name.empty()) { exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name)); } -#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) +#elif defined(OS_FUCHSIA) + // These handles are logically "moved" into these variables when retrieved by + // zx_get_startup_handle(). Both are given to ExceptionHandlerServer which + // owns them in this process. There is currently no "connect-later" mode on + // Fuchsia, all the binding must be done by the client before starting + // crashpad_handler. + base::ScopedZxHandle root_job(zx_get_startup_handle(PA_HND(PA_USER0, 0))); + if (!root_job.is_valid()) { + LOG(ERROR) << "no process handle passed in startup handle 0"; + return EXIT_FAILURE; + } + + base::ScopedZxHandle exception_port( + zx_get_startup_handle(PA_HND(PA_USER0, 1))); + if (!exception_port.is_valid()) { + LOG(ERROR) << "no exception port handle passed in startup handle 1"; + return EXIT_FAILURE; + } + + ExceptionHandlerServer exception_handler_server(std::move(root_job), + std::move(exception_port)); +#elif defined(OS_LINUX) || defined(OS_ANDROID) ExceptionHandlerServer exception_handler_server; #endif // OS_MACOSX From 02adab2e80c71c74866f9e40dc23c2260493a30e Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 4 May 2018 16:59:20 -0700 Subject: [PATCH 272/326] fuchsia: InitializeException, and write dump in exception handler Implements InitializeException() in ProcessSnapshot, and pulls it all together writing the dump in crash handler. Sample output at crash 00163eff624e653e on the staging server. Also adds a child-retrieve helper to koid_utilities. Bug: crashpad:196 Change-Id: I4bee7655e81e3243ac0ae896ff0caea7ce4acdad Reviewed-on: https://chromium-review.googlesource.com/1044771 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- .../fuchsia/crash_report_exception_handler.cc | 136 +++++++++++++++++- .../fuchsia/crash_report_exception_handler.h | 20 +-- snapshot/fuchsia/process_snapshot_fuchsia.cc | 12 +- snapshot/fuchsia/process_snapshot_fuchsia.h | 21 +++ util/fuchsia/koid_utilities.cc | 21 +-- util/fuchsia/koid_utilities.h | 9 ++ 6 files changed, 198 insertions(+), 21 deletions(-) diff --git a/handler/fuchsia/crash_report_exception_handler.cc b/handler/fuchsia/crash_report_exception_handler.cc index a7f61994..06e9d145 100644 --- a/handler/fuchsia/crash_report_exception_handler.cc +++ b/handler/fuchsia/crash_report_exception_handler.cc @@ -14,14 +14,148 @@ #include "handler/fuchsia/crash_report_exception_handler.h" +#include + +#include "base/fuchsia/fuchsia_logging.h" +#include "client/settings.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/minidump_user_extension_stream_data_source.h" +#include "snapshot/fuchsia/process_snapshot_fuchsia.h" +#include "util/fuchsia/koid_utilities.h" +#include "util/fuchsia/scoped_task_suspend.h" + namespace crashpad { +namespace { + +struct ScopedZxTaskResumeAfterException { + ScopedZxTaskResumeAfterException(zx_handle_t thread) : thread_(thread) {} + ~ScopedZxTaskResumeAfterException() { + DCHECK_NE(thread_, ZX_HANDLE_INVALID); + // Resuming with ZX_RESUME_TRY_NEXT chains to the next handler. In normal + // operation, there won't be another beyond this one, which will result in + // the kernel terminating the process. + zx_status_t status = + zx_task_resume(thread_, ZX_RESUME_EXCEPTION | ZX_RESUME_TRY_NEXT); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_task_resume"; + } + } + + private: + zx_handle_t thread_; // weak +}; + +} // namespace + CrashReportExceptionHandler::CrashReportExceptionHandler( CrashReportDatabase* database, CrashReportUploadThread* upload_thread, const std::map* process_annotations, - const UserStreamDataSources* user_stream_data_sources) {} + const UserStreamDataSources* user_stream_data_sources) + : database_(database), + upload_thread_(upload_thread), + process_annotations_(process_annotations), + user_stream_data_sources_(user_stream_data_sources) {} CrashReportExceptionHandler::~CrashReportExceptionHandler() {} +bool CrashReportExceptionHandler::HandleException(uint32_t type, + uint64_t process_id, + uint64_t thread_id) { + // TODO(scottmg): This function needs to be instrumented with metrics calls, + // https://crashpad.chromium.org/bug/230. + + base::ScopedZxHandle process(GetProcessFromKoid(process_id)); + if (!process.is_valid()) { + // There's no way to zx_task_resume() the thread if the process retrieval + // fails. Assume that the process has been already killed, and bail. + return false; + } + + ScopedTaskSuspend suspend(process.get()); + + base::ScopedZxHandle thread(GetChildHandleByKoid(process.get(), thread_id)); + if (!thread.is_valid()) { + return false; + } + + // Now that the thread has been successfully retrieved, it is possible to + // correctly call zx_task_resume() to continue exception processing, even if + // something else during this function fails. + ScopedZxTaskResumeAfterException resume(thread.get()); + + ProcessSnapshotFuchsia process_snapshot; + if (!process_snapshot.Initialize(process.get())) { + return false; + } + + CrashpadInfoClientOptions client_options; + process_snapshot.GetCrashpadOptions(&client_options); + + if (client_options.crashpad_handler_behavior != TriState::kDisabled) { + zx_exception_report_t report; + zx_status_t status = zx_object_get_info(thread.get(), + ZX_INFO_THREAD_EXCEPTION_REPORT, + &report, + sizeof(report), + nullptr, + nullptr); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) + << "zx_object_get_info ZX_INFO_THREAD_EXCEPTION_REPORT"; + return false; + } + + DCHECK_EQ(type, report.header.type); + + if (!process_snapshot.InitializeException(thread_id, report)) { + return false; + } + + UUID client_id; + Settings* const settings = database_->GetSettings(); + if (settings) { + // If GetSettings() or GetClientID() fails, something else will log a + // message and client_id will be left at its default value, all zeroes, + // which is appropriate. + settings->GetClientID(&client_id); + } + + process_snapshot.SetClientID(client_id); + process_snapshot.SetAnnotationsSimpleMap(*process_annotations_); + + std::unique_ptr new_report; + CrashReportDatabase::OperationStatus database_status = + database_->PrepareNewCrashReport(&new_report); + if (database_status != CrashReportDatabase::kNoError) { + return false; + } + + process_snapshot.SetReportID(new_report->ReportID()); + + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(&process_snapshot); + AddUserExtensionStreams( + user_stream_data_sources_, &process_snapshot, &minidump); + + if (!minidump.WriteEverything(new_report->Writer())) { + return false; + } + + UUID uuid; + database_status = + database_->FinishedWritingCrashReport(std::move(new_report), &uuid); + if (database_status != CrashReportDatabase::kNoError) { + return false; + } + + if (upload_thread_) { + upload_thread_->ReportPending(uuid); + } + } + + return true; +} + } // namespace crashpad diff --git a/handler/fuchsia/crash_report_exception_handler.h b/handler/fuchsia/crash_report_exception_handler.h index eefbd54c..a6bda613 100644 --- a/handler/fuchsia/crash_report_exception_handler.h +++ b/handler/fuchsia/crash_report_exception_handler.h @@ -28,7 +28,7 @@ namespace crashpad { //! \brief An exception handler that writes crash reports for exception messages -//! to a CrashReportDatabase. This class is not yet implemented. +//! to a CrashReportDatabase. class CrashReportExceptionHandler { public: //! \brief Creates a new object that will store crash reports in \a database. @@ -64,19 +64,19 @@ class CrashReportExceptionHandler { //! This function is expected to call `zx_task_resume()` in order to complete //! handling of the exception. //! - //! \note TODO(scottmg): This is not yet implemented. - //! //! \param[in] type The type of exception, a `ZX_EXCP_*` value. - //! \param[in] pid The koid of the process which sustained the exception. - //! \param[in] tid The koid of the thread which sustained the exception. + //! \param[in] process_id The koid of the process which sustained the + //! exception. + //! \param[in] thread_id The koid of the thread which sustained the exception. //! \return `true` on success, or `false` with an error logged. - bool HandleException(uint32_t type, - uint64_t pid, - uint64_t tid) { - return false; - } + bool HandleException(uint32_t type, uint64_t process_id, uint64_t thread_id); private: + CrashReportDatabase* database_; // weak + CrashReportUploadThread* upload_thread_; // weak + const std::map* process_annotations_; // weak + const UserStreamDataSources* user_stream_data_sources_; // weak + DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler); }; diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc index c2d5ab3e..26e7b3be 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -46,6 +46,15 @@ bool ProcessSnapshotFuchsia::Initialize(zx_handle_t process) { return true; } +bool ProcessSnapshotFuchsia::InitializeException( + zx_koid_t thread_id, + const zx_exception_report_t& report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + exception_.reset(new internal::ExceptionSnapshotFuchsia()); + exception_->Initialize(&process_reader_, thread_id, report); + return true; +} + void ProcessSnapshotFuchsia::GetCrashpadOptions( CrashpadInfoClientOptions* options) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); @@ -161,8 +170,7 @@ std::vector ProcessSnapshotFuchsia::UnloadedModules() const ExceptionSnapshot* ProcessSnapshotFuchsia::Exception() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - // TODO(scottmg): https://crashpad.chromium.org/bug/196 - return nullptr; + return exception_.get(); } std::vector ProcessSnapshotFuchsia::MemoryMap() diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.h b/snapshot/fuchsia/process_snapshot_fuchsia.h index 72078a78..bf1e9a52 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.h +++ b/snapshot/fuchsia/process_snapshot_fuchsia.h @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -25,6 +26,7 @@ #include "snapshot/crashpad_info_client_options.h" #include "snapshot/elf/elf_image_reader.h" #include "snapshot/elf/module_snapshot_elf.h" +#include "snapshot/fuchsia/exception_snapshot_fuchsia.h" #include "snapshot/fuchsia/process_reader_fuchsia.h" #include "snapshot/fuchsia/system_snapshot_fuchsia.h" #include "snapshot/fuchsia/thread_snapshot_fuchsia.h" @@ -49,6 +51,24 @@ class ProcessSnapshotFuchsia : public ProcessSnapshot { //! an appropriate message logged. bool Initialize(zx_handle_t process); + //! \brief Initializes the object's exception. + //! + //! This populates the data to be returned by Exception(). The thread + //! identified by \a thread_id must be in an exception. + //! + //! This method must not be called until after a successful call to + //! Initialize(). + //! + //! \param[in] thread_id Koid of the thread which sustained the exception. + //! \param[in] report The `zx_exception_report_t` for the thread which + //! sustained the exception. + //! \return `true` if the exception information could be initialized, `false` + //! otherwise with an appropriate message logged. When this method returns + //! `false`, the ProcessSnapshotFuchsia object’s validity remains + //! unchanged. + bool InitializeException(zx_koid_t thread_id, + const zx_exception_report_t& report); + //! \brief Returns options from CrashpadInfo structures found in modules in //! the process. //! @@ -110,6 +130,7 @@ class ProcessSnapshotFuchsia : public ProcessSnapshot { internal::SystemSnapshotFuchsia system_; std::vector> threads_; std::vector> modules_; + std::unique_ptr exception_; ProcessReaderFuchsia process_reader_; std::map annotations_simple_map_; UUID report_id_; diff --git a/util/fuchsia/koid_utilities.cc b/util/fuchsia/koid_utilities.cc index b280f524..0d44f4af 100644 --- a/util/fuchsia/koid_utilities.cc +++ b/util/fuchsia/koid_utilities.cc @@ -113,19 +113,24 @@ std::vector GetHandlesForChildKoids( const std::vector& koids) { std::vector result; result.reserve(koids.size()); - for (zx_koid_t koid : koids) { - zx_handle_t handle; - if (zx_object_get_child(parent, koid, ZX_RIGHT_SAME_RIGHTS, &handle) == - ZX_OK) { - result.emplace_back(base::ScopedZxHandle(handle)); - } else { - result.push_back(base::ScopedZxHandle()); - } + result.emplace_back(GetChildHandleByKoid(parent, koid)); } return result; } +base::ScopedZxHandle GetChildHandleByKoid(zx_handle_t parent, + zx_koid_t child_koid) { + zx_handle_t handle; + zx_status_t status = + zx_object_get_child(parent, child_koid, ZX_RIGHT_SAME_RIGHTS, &handle); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_object_get_child"; + return base::ScopedZxHandle(); + } + return base::ScopedZxHandle(handle); +} + zx_koid_t GetKoidForHandle(zx_handle_t object) { zx_info_handle_basic_t info; zx_status_t status = zx_object_get_info( diff --git a/util/fuchsia/koid_utilities.h b/util/fuchsia/koid_utilities.h index 326bcac2..5bcea105 100644 --- a/util/fuchsia/koid_utilities.h +++ b/util/fuchsia/koid_utilities.h @@ -68,6 +68,15 @@ std::vector GetHandlesForChildKoids( zx_handle_t parent, const std::vector& koids); +//! \brief Retrieve the child of a parent handle, based on koid. +//! +//! \param[in] parent The parent object to which the child belongs. +//! \param[in] child_koid The koid of the child to retrieve. +//! \return A handle representing \a child_koid, or `ZX_HANDLE_INVALID` if the +//! handle could not be retrieved, in which case an error will be logged. +base::ScopedZxHandle GetChildHandleByKoid(zx_handle_t parent, + zx_koid_t child_koid); + //! \brief Gets a process handle given the process' koid. //! //! \param[in] koid The process id. From 31703a585fb3f191b060b534b6ae13b1f9c36dc3 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 4 May 2018 17:06:13 -0700 Subject: [PATCH 273/326] fuchsia: When in Fuchsia tree, disable tests requiring external files The package deployment/running is in flux at the moment. In order to get all the other tests on to the main Fuchsia waterfall, disable the ~25 tests that require external files (for launching child processes, loading modules, or data files) because those operations all fail on Fuchsia-without-packages right now. Upstream this is PKG-46. Once test packaging and running has been resolved, this can be reverted. These tests are still run when building Crashpad standalone on Fuchsia as the standalone build simply copies all the relevant data files to the device in /tmp. Bug: crashpad:196 Change-Id: I1677c394a2b9d709c59363ebeea8aff193d4c21d Reviewed-on: https://chromium-review.googlesource.com/1045547 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- build/BUILD.gn | 6 ++++++ snapshot/crashpad_info_client_options_test.cc | 7 +++++++ snapshot/elf/elf_image_reader_test.cc | 4 ++++ .../fuchsia/process_reader_fuchsia_test.cc | 5 +++++ test/BUILD.gn | 5 ++++- test/multiprocess_exec_test.cc | 4 ++++ test/test_paths.cc | 9 +++++++++ test/test_paths.h | 9 +++++++++ test/test_paths_test.cc | 4 ++++ util/net/http_body_test.cc | 10 ++++++++++ util/net/http_multipart_builder_test.cc | 10 ++++++++++ util/net/http_transport_test.cc | 19 +++++++++++++++++++ 12 files changed, 91 insertions(+), 1 deletion(-) diff --git a/build/BUILD.gn b/build/BUILD.gn index ceae81ec..5e7aed0c 100644 --- a/build/BUILD.gn +++ b/build/BUILD.gn @@ -24,6 +24,12 @@ config("crashpad_is_in_chromium") { } } +config("crashpad_is_in_fuchsia") { + if (crashpad_is_in_fuchsia) { + defines = [ "CRASHPAD_IS_IN_FUCHSIA" ] + } +} + group("default_exe_manifest_win") { if (crashpad_is_in_chromium) { deps = [ diff --git a/snapshot/crashpad_info_client_options_test.cc b/snapshot/crashpad_info_client_options_test.cc index 1fe38acf..f23a179f 100644 --- a/snapshot/crashpad_info_client_options_test.cc +++ b/snapshot/crashpad_info_client_options_test.cc @@ -22,6 +22,7 @@ #include "client/crashpad_info.h" #include "gtest/gtest.h" #include "test/errors.h" +#include "test/gtest_disabled.h" #include "test/scoped_module_handle.h" #include "test/test_paths.h" @@ -147,6 +148,9 @@ TEST(CrashpadInfoClientOptions, OneModule) { } TEST(CrashpadInfoClientOptions, TwoModules) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + // Open the module, which has its own CrashpadInfo structure. base::FilePath module_path = TestPaths::BuildArtifact(FILE_PATH_LITERAL("snapshot"), @@ -240,6 +244,9 @@ class CrashpadInfoSizes_ClientOptions : public testing::TestWithParam {}; TEST_P(CrashpadInfoSizes_ClientOptions, DifferentlySizedStruct) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + base::FilePath::StringType artifact(FILE_PATH_LITERAL("module_")); artifact += GetParam(); diff --git a/snapshot/elf/elf_image_reader_test.cc b/snapshot/elf/elf_image_reader_test.cc index d64fd5c4..e0f0ea3d 100644 --- a/snapshot/elf/elf_image_reader_test.cc +++ b/snapshot/elf/elf_image_reader_test.cc @@ -21,6 +21,7 @@ #include "base/logging.h" #include "build/build_config.h" #include "gtest/gtest.h" +#include "test/gtest_disabled.h" #include "test/multiprocess_exec.h" #include "test/process_type.h" #include "test/scoped_module_handle.h" @@ -314,6 +315,9 @@ TEST(ElfImageReader, OneModuleChild) { // TODO(scottmg): Separately, the location of the ELF on Android needs some // work, and then the test could also be enabled there. TEST(ElfImageReader, DtHashAndDtGnuHashMatch) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + base::FilePath module_path = TestPaths::BuildArtifact(FILE_PATH_LITERAL("snapshot"), FILE_PATH_LITERAL("both_dt_hash_styles"), diff --git a/snapshot/fuchsia/process_reader_fuchsia_test.cc b/snapshot/fuchsia/process_reader_fuchsia_test.cc index e9d04834..b5997fe6 100644 --- a/snapshot/fuchsia/process_reader_fuchsia_test.cc +++ b/snapshot/fuchsia/process_reader_fuchsia_test.cc @@ -19,7 +19,9 @@ #include #include "gtest/gtest.h" +#include "test/gtest_disabled.h" #include "test/multiprocess_exec.h" +#include "test/test_paths.h" #include "util/fuchsia/scoped_task_suspend.h" namespace crashpad { @@ -159,6 +161,9 @@ class ThreadsChildTest : public MultiprocessExec { }; TEST(ProcessReaderFuchsia, ChildThreads) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + ThreadsChildTest test; test.Run(); } diff --git a/test/BUILD.gn b/test/BUILD.gn index ab37dc16..ec51e7d2 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -99,7 +99,10 @@ static_library("test") { public_configs = [ "..:crashpad_config" ] - configs += [ "../build:crashpad_is_in_chromium" ] + configs += [ + "../build:crashpad_is_in_chromium", + "../build:crashpad_is_in_fuchsia" + ] data = [ "test_paths_test_data_root.txt", diff --git a/test/multiprocess_exec_test.cc b/test/multiprocess_exec_test.cc index 99a1b01f..1f3c2449 100644 --- a/test/multiprocess_exec_test.cc +++ b/test/multiprocess_exec_test.cc @@ -19,6 +19,7 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "gtest/gtest.h" +#include "test/gtest_disabled.h" #include "test/test_paths.h" #include "util/file/file_io.h" @@ -48,6 +49,9 @@ class TestMultiprocessExec final : public MultiprocessExec { }; TEST(MultiprocessExec, MultiprocessExec) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + TestMultiprocessExec multiprocess_exec; base::FilePath child_test_executable = TestPaths::BuildArtifact( FILE_PATH_LITERAL("test"), diff --git a/test/test_paths.cc b/test/test_paths.cc index 0d7db061..674d04f4 100644 --- a/test/test_paths.cc +++ b/test/test_paths.cc @@ -222,5 +222,14 @@ bool TestPaths::Has32BitBuildArtifacts() { #endif // defined(OS_WIN) && defined(ARCH_CPU_64_BITS) +// static +bool TestPaths::ExternalFilesUnavailable() { +#if defined(OS_FUCHSIA) && defined(CRASHPAD_IS_IN_FUCHSIA) + return true; +#else + return false; +#endif +} + } // namespace test } // namespace crashpad diff --git a/test/test_paths.h b/test/test_paths.h index 5540f77d..df07b5fe 100644 --- a/test/test_paths.h +++ b/test/test_paths.h @@ -138,6 +138,15 @@ class TestPaths { static bool Has32BitBuildArtifacts(); #endif // OS_WIN && ARCH_CPU_64_BITS + //! \return `true` if no external data files are available. + //! + //! TODO(scottmg): https://crashpad.chromium.org/bug/196: This is a temporary + //! hack to disable tests that require external files, either to relaunch + //! subprocesses, or to load data files. The Fuchsia packaging system is in + //! flux (see PKG-46 upstream) and when that's resolved this should be + //! deleted. + static bool ExternalFilesUnavailable(); + DISALLOW_IMPLICIT_CONSTRUCTORS(TestPaths); }; diff --git a/test/test_paths_test.cc b/test/test_paths_test.cc index 9d43b594..cec54543 100644 --- a/test/test_paths_test.cc +++ b/test/test_paths_test.cc @@ -16,6 +16,7 @@ #include "base/files/file_path.h" #include "gtest/gtest.h" +#include "test/gtest_disabled.h" #include "util/file/file_io.h" namespace crashpad { @@ -23,6 +24,9 @@ namespace test { namespace { TEST(TestPaths, TestDataRoot) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + base::FilePath test_data_root = TestPaths::TestDataRoot(); ScopedFileHandle file(LoggingOpenFileForRead( test_data_root.Append(FILE_PATH_LITERAL("test")) diff --git a/util/net/http_body_test.cc b/util/net/http_body_test.cc index e48cbf91..caa53c28 100644 --- a/util/net/http_body_test.cc +++ b/util/net/http_body_test.cc @@ -17,6 +17,7 @@ #include #include "gtest/gtest.h" +#include "test/gtest_disabled.h" #include "test/test_paths.h" #include "util/misc/implicit_cast.h" #include "util/net/http_body_test_util.h" @@ -97,6 +98,9 @@ TEST(StringHTTPBodyStream, MultipleReads) { } TEST(FileReaderHTTPBodyStream, ReadASCIIFile) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + base::FilePath path = TestPaths::TestDataRoot().Append( FILE_PATH_LITERAL("util/net/testdata/ascii_http_body.txt")); @@ -117,6 +121,9 @@ TEST(FileReaderHTTPBodyStream, ReadASCIIFile) { } TEST(FileReaderHTTPBodyStream, ReadBinaryFile) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + // HEX contents of file: |FEEDFACE A11A15|. base::FilePath path = TestPaths::TestDataRoot().Append( FILE_PATH_LITERAL("util/net/testdata/binary_http_body.dat")); @@ -187,6 +194,9 @@ TEST_P(CompositeHTTPBodyStreamBufferSize, ThreeStringParts) { } TEST_P(CompositeHTTPBodyStreamBufferSize, StringsAndFile) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + std::string string1("Hello! "); std::string string2(" Goodbye :)"); diff --git a/util/net/http_multipart_builder_test.cc b/util/net/http_multipart_builder_test.cc index dab9e7e7..d1bcf544 100644 --- a/util/net/http_multipart_builder_test.cc +++ b/util/net/http_multipart_builder_test.cc @@ -20,6 +20,7 @@ #include "gtest/gtest.h" #include "test/gtest_death.h" +#include "test/gtest_disabled.h" #include "test/test_paths.h" #include "util/net/http_body.h" #include "util/net/http_body_test_util.h" @@ -99,6 +100,9 @@ TEST(HTTPMultipartBuilder, ThreeStringFields) { } TEST(HTTPMultipartBuilder, ThreeFileAttachments) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + HTTPMultipartBuilder builder; base::FilePath ascii_http_body_path = TestPaths::TestDataRoot().Append( FILE_PATH_LITERAL("util/net/testdata/ascii_http_body.txt")); @@ -183,6 +187,9 @@ TEST(HTTPMultipartBuilder, OverwriteFormDataWithEscapedKey) { } TEST(HTTPMultipartBuilder, OverwriteFileAttachment) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + HTTPMultipartBuilder builder; static constexpr char kValue[] = "1 2 3 test"; builder.SetFormData("a key", kValue); @@ -241,6 +248,9 @@ TEST(HTTPMultipartBuilder, OverwriteFileAttachment) { } TEST(HTTPMultipartBuilder, SharedFormDataAndAttachmentKeyNamespace) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + HTTPMultipartBuilder builder; static constexpr char kValue1[] = "11111"; builder.SetFormData("one", kValue1); diff --git a/util/net/http_transport_test.cc b/util/net/http_transport_test.cc index abaa1727..f8413338 100644 --- a/util/net/http_transport_test.cc +++ b/util/net/http_transport_test.cc @@ -30,6 +30,7 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "gtest/gtest.h" +#include "test/gtest_disabled.h" #include "test/multiprocess_exec.h" #include "test/test_paths.h" #include "util/file/file_io.h" @@ -210,6 +211,9 @@ void ValidFormData(HTTPTransportTestFixture* fixture, } TEST(HTTPTransport, ValidFormData) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + HTTPMultipartBuilder builder; builder.SetFormData("key1", "test"); builder.SetFormData("key2", "--abcdefg123"); @@ -223,6 +227,9 @@ TEST(HTTPTransport, ValidFormData) { } TEST(HTTPTransport, ValidFormData_Gzip) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + HTTPMultipartBuilder builder; builder.SetGzipEnabled(true); builder.SetFormData("key1", "test"); @@ -246,6 +253,9 @@ void ErrorResponse(HTTPTransportTestFixture* fixture, } TEST(HTTPTransport, ErrorResponse) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + HTTPMultipartBuilder builder; HTTPHeaders headers; headers[kContentType] = kTextPlain; @@ -274,6 +284,9 @@ void UnchunkedPlainText(HTTPTransportTestFixture* fixture, } TEST(HTTPTransport, UnchunkedPlainText) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + std::unique_ptr body_stream( new StringHTTPBodyStream(kTextBody)); @@ -314,10 +327,16 @@ void RunUpload33k(bool has_content_length) { } TEST(HTTPTransport, Upload33k) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + RunUpload33k(true); } TEST(HTTPTransport, Upload33k_LengthUnknown) { + if (TestPaths::ExternalFilesUnavailable()) + DISABLED_TEST(); + // The same as Upload33k, but without declaring Content-Length ahead of time. RunUpload33k(false); } From f115659a672371786bc8cb39fb61cfccbc3d7b81 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 8 May 2018 12:20:43 -0700 Subject: [PATCH 274/326] fuchsia: Include crashpad_database_util in package Also, add both crashpad_handler and crashpad_database_util to "binaries" which causes them to be included in the final system image. Bug: crashpad:196 Change-Id: Iaf1bf7bbc0a5370695372fd01d7c3da35906dfc3 Reviewed-on: https://chromium-review.googlesource.com/1050576 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- BUILD.gn | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/BUILD.gn b/BUILD.gn index 709ce495..a23bbd3e 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -54,6 +54,16 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { deps = [ "handler:crashpad_handler", + "tools:crashpad_database_util", + ] + + binaries = [ + { + name = "crashpad_handler" + }, + { + name = "crashpad_database_util" + }, ] } } From f55a8d4ff3f24e8cf825e54bc864c041c646976f Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 10 May 2018 10:27:58 -0700 Subject: [PATCH 275/326] fuchsia: Work around lack of packaging in Fuchsia tree build Packaged test running seems to be a ways off, but with a bit of path fiddling in test_paths.cc we can actually use the paths where the tests are copied, so do that instead to get all the tests re-enabled. The setup in BUILD.gn should be mostly-useful once packaging is working as all helper/data files will need to specified there anyway. Also, attempted fix to flaky behaviour in ProcessReaderFuchsia.ChildThreads exposed because the tests are now being run. zx_object_wait_many() waits on *any* of the objects, not *all* of them. Derp! And finally, for the same test, work around some unintuitive behaviour in zx_task_suspend(), in particular that the thread will not be suspended for the purpose of reading registers right away, but instead only "sometime later", which appears in pratice to be after the next context switch. Have ScopedTaskSuspend block for a while to try to ensure the registers become readble, and if they don't, at least fail noisily at that point. Bug: crashpad:196 Change-Id: I01fb3590ede96301c941c2a88eba47fdbfe74ea7 Reviewed-on: https://chromium-review.googlesource.com/1053797 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- BUILD.gn | 42 +++++++++++++++++++ snapshot/crashpad_info_client_options_test.cc | 7 ---- snapshot/elf/elf_image_reader_test.cc | 4 -- .../fuchsia/process_reader_fuchsia_test.cc | 32 +++++++------- test/multiprocess_exec_test.cc | 4 -- test/test_paths.cc | 31 ++++++++++---- test/test_paths.h | 9 ---- test/test_paths_test.cc | 4 -- util/fuchsia/scoped_task_suspend.cc | 34 +++++++++++++-- util/net/http_body_test.cc | 10 ----- util/net/http_multipart_builder_test.cc | 10 ----- util/net/http_transport_test.cc | 19 --------- 12 files changed, 112 insertions(+), 94 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index a23bbd3e..c9da7c8a 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -40,12 +40,54 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { deps = [ ":crashpad_tests", + "snapshot:crashpad_snapshot_test_both_dt_hash_styles(//build/toolchain/fuchsia:x64-shared)", + "snapshot:crashpad_snapshot_test_module(//build/toolchain/fuchsia:x64-shared)", + "snapshot:crashpad_snapshot_test_module_large(//build/toolchain/fuchsia:x64-shared)", + "snapshot:crashpad_snapshot_test_module_small(//build/toolchain/fuchsia:x64-shared)", + "test:crashpad_test_test_multiprocess_exec_test_child", + "util:http_transport_test_server", ] tests = [ { name = "crashpad_tests" }, + { + name = "crashpad_test_test_multiprocess_exec_test_child" + }, + { + name = "http_transport_test_server" + }, + ] + + libraries = [ + { + name = "crashpad_snapshot_test_both_dt_hash_styles.so" + }, + { + name = "crashpad_snapshot_test_module.so" + }, + { + name = "crashpad_snapshot_test_module_large.so" + }, + { + name = "crashpad_snapshot_test_module_small.so" + }, + ] + + resources = [ + { + path = rebase_path("util/net/testdata/ascii_http_body.txt") + dest = "crashpad_test_data/util/net/testdata/ascii_http_body.txt" + }, + { + path = rebase_path("util/net/testdata/binary_http_body.dat") + dest = "crashpad_test_data/util/net/testdata/binary_http_body.dat" + }, + { + path = rebase_path("test/test_paths_test_data_root.txt") + dest = "crashpad_test_data/test/test_paths_test_data_root.txt" + }, ] } diff --git a/snapshot/crashpad_info_client_options_test.cc b/snapshot/crashpad_info_client_options_test.cc index f23a179f..1fe38acf 100644 --- a/snapshot/crashpad_info_client_options_test.cc +++ b/snapshot/crashpad_info_client_options_test.cc @@ -22,7 +22,6 @@ #include "client/crashpad_info.h" #include "gtest/gtest.h" #include "test/errors.h" -#include "test/gtest_disabled.h" #include "test/scoped_module_handle.h" #include "test/test_paths.h" @@ -148,9 +147,6 @@ TEST(CrashpadInfoClientOptions, OneModule) { } TEST(CrashpadInfoClientOptions, TwoModules) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - // Open the module, which has its own CrashpadInfo structure. base::FilePath module_path = TestPaths::BuildArtifact(FILE_PATH_LITERAL("snapshot"), @@ -244,9 +240,6 @@ class CrashpadInfoSizes_ClientOptions : public testing::TestWithParam {}; TEST_P(CrashpadInfoSizes_ClientOptions, DifferentlySizedStruct) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - base::FilePath::StringType artifact(FILE_PATH_LITERAL("module_")); artifact += GetParam(); diff --git a/snapshot/elf/elf_image_reader_test.cc b/snapshot/elf/elf_image_reader_test.cc index e0f0ea3d..d64fd5c4 100644 --- a/snapshot/elf/elf_image_reader_test.cc +++ b/snapshot/elf/elf_image_reader_test.cc @@ -21,7 +21,6 @@ #include "base/logging.h" #include "build/build_config.h" #include "gtest/gtest.h" -#include "test/gtest_disabled.h" #include "test/multiprocess_exec.h" #include "test/process_type.h" #include "test/scoped_module_handle.h" @@ -315,9 +314,6 @@ TEST(ElfImageReader, OneModuleChild) { // TODO(scottmg): Separately, the location of the ELF on Android needs some // work, and then the test could also be enabled there. TEST(ElfImageReader, DtHashAndDtGnuHashMatch) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - base::FilePath module_path = TestPaths::BuildArtifact(FILE_PATH_LITERAL("snapshot"), FILE_PATH_LITERAL("both_dt_hash_styles"), diff --git a/snapshot/fuchsia/process_reader_fuchsia_test.cc b/snapshot/fuchsia/process_reader_fuchsia_test.cc index b5997fe6..329752b8 100644 --- a/snapshot/fuchsia/process_reader_fuchsia_test.cc +++ b/snapshot/fuchsia/process_reader_fuchsia_test.cc @@ -17,9 +17,9 @@ #include #include #include +#include #include "gtest/gtest.h" -#include "test/gtest_disabled.h" #include "test/multiprocess_exec.h" #include "test/test_paths.h" #include "util/fuchsia/scoped_task_suspend.h" @@ -100,28 +100,33 @@ TEST(ProcessReaderFuchsia, ChildBasic) { } void* SignalAndSleep(void* arg) { - zx_object_signal(*reinterpret_cast(arg), 0, ZX_EVENT_SIGNALED); + zx_port_packet_t packet = {}; + packet.type = ZX_PKT_TYPE_USER; + zx_port_queue(*reinterpret_cast(arg), &packet, 1); zx_nanosleep(UINT64_MAX); return nullptr; } CRASHPAD_CHILD_TEST_MAIN(ProcessReaderChildThreadsTestMain) { // Create 5 threads with stack sizes of 4096, 8192, ... - zx_handle_t events[5]; - zx_wait_item_t items[arraysize(events)]; - for (size_t i = 0; i < arraysize(events); ++i) { - pthread_t thread; - EXPECT_EQ(zx_event_create(0, &events[i]), ZX_OK); + zx_handle_t port; + zx_status_t status = zx_port_create(0, &port); + EXPECT_EQ(status, ZX_OK); + + constexpr size_t kNumThreads = 5; + for (size_t i = 0; i < kNumThreads; ++i) { pthread_attr_t attr; EXPECT_EQ(pthread_attr_init(&attr), 0); EXPECT_EQ(pthread_attr_setstacksize(&attr, (i + 1) * 4096), 0); - EXPECT_EQ(pthread_create(&thread, &attr, &SignalAndSleep, &events[i]), 0); - items[i].waitfor = ZX_EVENT_SIGNALED; - items[i].handle = events[i]; + pthread_t thread; + EXPECT_EQ(pthread_create(&thread, &attr, &SignalAndSleep, &port), 0); } - EXPECT_EQ(zx_object_wait_many(items, arraysize(items), ZX_TIME_INFINITE), - ZX_OK); + // Wait until all threads are ready. + for (size_t i = 0; i < kNumThreads; ++i) { + zx_port_packet_t packet; + zx_port_wait(port, ZX_TIME_INFINITE, &packet, 1); + } char c = ' '; CheckedWriteFile( @@ -161,9 +166,6 @@ class ThreadsChildTest : public MultiprocessExec { }; TEST(ProcessReaderFuchsia, ChildThreads) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - ThreadsChildTest test; test.Run(); } diff --git a/test/multiprocess_exec_test.cc b/test/multiprocess_exec_test.cc index 1f3c2449..99a1b01f 100644 --- a/test/multiprocess_exec_test.cc +++ b/test/multiprocess_exec_test.cc @@ -19,7 +19,6 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "gtest/gtest.h" -#include "test/gtest_disabled.h" #include "test/test_paths.h" #include "util/file/file_io.h" @@ -49,9 +48,6 @@ class TestMultiprocessExec final : public MultiprocessExec { }; TEST(MultiprocessExec, MultiprocessExec) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - TestMultiprocessExec multiprocess_exec; base::FilePath child_test_executable = TestPaths::BuildArtifact( FILE_PATH_LITERAL("test"), diff --git a/test/test_paths.cc b/test/test_paths.cc index 674d04f4..b06fb7f3 100644 --- a/test/test_paths.cc +++ b/test/test_paths.cc @@ -45,6 +45,13 @@ bool IsTestDataRoot(const base::FilePath& candidate) { base::FilePath TestDataRootInternal() { #if defined(OS_FUCHSIA) base::FilePath asset_path("/pkg/assets"); +#if defined(CRASHPAD_IS_IN_FUCHSIA) + // Tests are not yet packaged when running in the Fuchsia tree, so assets do + // not appear as expected at /pkg/assets. Override the default so that tests + // can find their data for now. + // https://crashpad.chromium.org/bug/196. + asset_path = base::FilePath("/system/data/crashpad_test_data"); +#endif if (!IsTestDataRoot(asset_path)) { LOG(WARNING) << "Test data root seems invalid, continuing anyway"; } @@ -121,6 +128,13 @@ base::FilePath Output32BitDirectory() { base::FilePath TestPaths::Executable() { base::FilePath executable_path; CHECK(Paths::Executable(&executable_path)); +#if defined(CRASHPAD_IS_IN_FUCHSIA) + // Tests are not yet packaged when running in the Fuchsia tree, so binaries do + // not appear as expected at /pkg/bin. Override the default of /pkg/bin/app + // so that tests can find the correct location for now. + // https://crashpad.chromium.org/bug/196. + executable_path = base::FilePath("/system/test/app"); +#endif return executable_path; } @@ -188,7 +202,15 @@ base::FilePath TestPaths::BuildArtifact( #if defined(OS_WIN) extension = FILE_PATH_LITERAL(".exe"); #elif defined(OS_FUCHSIA) +#if defined(CRASHPAD_IS_IN_FUCHSIA) + // Tests are not yet packaged when running in the Fuchsia tree, so + // binaries do not appear as expected at /pkg/bin. Override the default of + // /pkg/bin/app so that tests can find the correct location for now. + // https://crashpad.chromium.org/bug/196. + directory = base::FilePath(FILE_PATH_LITERAL("/system/test")); +#else directory = base::FilePath(FILE_PATH_LITERAL("/pkg/bin")); +#endif #endif // OS_WIN break; @@ -222,14 +244,5 @@ bool TestPaths::Has32BitBuildArtifacts() { #endif // defined(OS_WIN) && defined(ARCH_CPU_64_BITS) -// static -bool TestPaths::ExternalFilesUnavailable() { -#if defined(OS_FUCHSIA) && defined(CRASHPAD_IS_IN_FUCHSIA) - return true; -#else - return false; -#endif -} - } // namespace test } // namespace crashpad diff --git a/test/test_paths.h b/test/test_paths.h index df07b5fe..5540f77d 100644 --- a/test/test_paths.h +++ b/test/test_paths.h @@ -138,15 +138,6 @@ class TestPaths { static bool Has32BitBuildArtifacts(); #endif // OS_WIN && ARCH_CPU_64_BITS - //! \return `true` if no external data files are available. - //! - //! TODO(scottmg): https://crashpad.chromium.org/bug/196: This is a temporary - //! hack to disable tests that require external files, either to relaunch - //! subprocesses, or to load data files. The Fuchsia packaging system is in - //! flux (see PKG-46 upstream) and when that's resolved this should be - //! deleted. - static bool ExternalFilesUnavailable(); - DISALLOW_IMPLICIT_CONSTRUCTORS(TestPaths); }; diff --git a/test/test_paths_test.cc b/test/test_paths_test.cc index cec54543..9d43b594 100644 --- a/test/test_paths_test.cc +++ b/test/test_paths_test.cc @@ -16,7 +16,6 @@ #include "base/files/file_path.h" #include "gtest/gtest.h" -#include "test/gtest_disabled.h" #include "util/file/file_io.h" namespace crashpad { @@ -24,9 +23,6 @@ namespace test { namespace { TEST(TestPaths, TestDataRoot) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - base::FilePath test_data_root = TestPaths::TestDataRoot(); ScopedFileHandle file(LoggingOpenFileForRead( test_data_root.Append(FILE_PATH_LITERAL("test")) diff --git a/util/fuchsia/scoped_task_suspend.cc b/util/fuchsia/scoped_task_suspend.cc index 594edf5c..c5871963 100644 --- a/util/fuchsia/scoped_task_suspend.cc +++ b/util/fuchsia/scoped_task_suspend.cc @@ -16,6 +16,7 @@ #include #include +#include #include @@ -39,10 +40,33 @@ zx_obj_type_t GetHandleType(zx_handle_t handle) { return basic.type; } -bool SuspendThread(zx_handle_t thread) { +enum class SuspensionResult { + FailedSuspendCall, + FailedToSuspendInTimelyFashion, + Succeeded, +}; + +SuspensionResult SuspendThread(zx_handle_t thread) { zx_status_t status = zx_task_suspend(thread); ZX_LOG_IF(ERROR, status != ZX_OK, status) << "zx_task_suspend"; - return status == ZX_OK; + if (status != ZX_OK) + return SuspensionResult::FailedSuspendCall; + // zx_task_suspend() suspends the thread "sometime soon", but it's hard to + // use when it's not guaranteed to be suspended after return. Try reading the + // thread state until the registers are retrievable, which means that the + // thread is actually suspended. Don't wait forever in case the suspend + // failed for whatever reason, but try a few times. + for (int i = 0; i < 5; ++i) { + zx_thread_state_general_regs_t regs; + status = zx_thread_read_state( + thread, ZX_THREAD_STATE_GENERAL_REGS, ®s, sizeof(regs)); + if (status == ZX_OK) { + return SuspensionResult::Succeeded; + } + zx_nanosleep(zx_deadline_after(ZX_MSEC(10))); + } + LOG(ERROR) << "thread failed to suspend"; + return SuspensionResult::FailedToSuspendInTimelyFashion; } bool ResumeThread(zx_handle_t thread) { @@ -59,7 +83,11 @@ ScopedTaskSuspend::ScopedTaskSuspend(zx_handle_t task) : task_(task) { zx_obj_type_t type = GetHandleType(task_); if (type == ZX_OBJ_TYPE_THREAD) { - if (!SuspendThread(task_)) { + // Note that task_ is only marked invalid if the zx_task_suspend() call + // completely fails, otherwise the suspension might just not have taken + // effect yet, so avoid leaving it suspended forever by still resuming on + // destruction. + if (SuspendThread(task_) == SuspensionResult::FailedSuspendCall) { task_ = ZX_HANDLE_INVALID; } } else if (type == ZX_OBJ_TYPE_PROCESS) { diff --git a/util/net/http_body_test.cc b/util/net/http_body_test.cc index caa53c28..e48cbf91 100644 --- a/util/net/http_body_test.cc +++ b/util/net/http_body_test.cc @@ -17,7 +17,6 @@ #include #include "gtest/gtest.h" -#include "test/gtest_disabled.h" #include "test/test_paths.h" #include "util/misc/implicit_cast.h" #include "util/net/http_body_test_util.h" @@ -98,9 +97,6 @@ TEST(StringHTTPBodyStream, MultipleReads) { } TEST(FileReaderHTTPBodyStream, ReadASCIIFile) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - base::FilePath path = TestPaths::TestDataRoot().Append( FILE_PATH_LITERAL("util/net/testdata/ascii_http_body.txt")); @@ -121,9 +117,6 @@ TEST(FileReaderHTTPBodyStream, ReadASCIIFile) { } TEST(FileReaderHTTPBodyStream, ReadBinaryFile) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - // HEX contents of file: |FEEDFACE A11A15|. base::FilePath path = TestPaths::TestDataRoot().Append( FILE_PATH_LITERAL("util/net/testdata/binary_http_body.dat")); @@ -194,9 +187,6 @@ TEST_P(CompositeHTTPBodyStreamBufferSize, ThreeStringParts) { } TEST_P(CompositeHTTPBodyStreamBufferSize, StringsAndFile) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - std::string string1("Hello! "); std::string string2(" Goodbye :)"); diff --git a/util/net/http_multipart_builder_test.cc b/util/net/http_multipart_builder_test.cc index d1bcf544..dab9e7e7 100644 --- a/util/net/http_multipart_builder_test.cc +++ b/util/net/http_multipart_builder_test.cc @@ -20,7 +20,6 @@ #include "gtest/gtest.h" #include "test/gtest_death.h" -#include "test/gtest_disabled.h" #include "test/test_paths.h" #include "util/net/http_body.h" #include "util/net/http_body_test_util.h" @@ -100,9 +99,6 @@ TEST(HTTPMultipartBuilder, ThreeStringFields) { } TEST(HTTPMultipartBuilder, ThreeFileAttachments) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - HTTPMultipartBuilder builder; base::FilePath ascii_http_body_path = TestPaths::TestDataRoot().Append( FILE_PATH_LITERAL("util/net/testdata/ascii_http_body.txt")); @@ -187,9 +183,6 @@ TEST(HTTPMultipartBuilder, OverwriteFormDataWithEscapedKey) { } TEST(HTTPMultipartBuilder, OverwriteFileAttachment) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - HTTPMultipartBuilder builder; static constexpr char kValue[] = "1 2 3 test"; builder.SetFormData("a key", kValue); @@ -248,9 +241,6 @@ TEST(HTTPMultipartBuilder, OverwriteFileAttachment) { } TEST(HTTPMultipartBuilder, SharedFormDataAndAttachmentKeyNamespace) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - HTTPMultipartBuilder builder; static constexpr char kValue1[] = "11111"; builder.SetFormData("one", kValue1); diff --git a/util/net/http_transport_test.cc b/util/net/http_transport_test.cc index f8413338..abaa1727 100644 --- a/util/net/http_transport_test.cc +++ b/util/net/http_transport_test.cc @@ -30,7 +30,6 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "gtest/gtest.h" -#include "test/gtest_disabled.h" #include "test/multiprocess_exec.h" #include "test/test_paths.h" #include "util/file/file_io.h" @@ -211,9 +210,6 @@ void ValidFormData(HTTPTransportTestFixture* fixture, } TEST(HTTPTransport, ValidFormData) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - HTTPMultipartBuilder builder; builder.SetFormData("key1", "test"); builder.SetFormData("key2", "--abcdefg123"); @@ -227,9 +223,6 @@ TEST(HTTPTransport, ValidFormData) { } TEST(HTTPTransport, ValidFormData_Gzip) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - HTTPMultipartBuilder builder; builder.SetGzipEnabled(true); builder.SetFormData("key1", "test"); @@ -253,9 +246,6 @@ void ErrorResponse(HTTPTransportTestFixture* fixture, } TEST(HTTPTransport, ErrorResponse) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - HTTPMultipartBuilder builder; HTTPHeaders headers; headers[kContentType] = kTextPlain; @@ -284,9 +274,6 @@ void UnchunkedPlainText(HTTPTransportTestFixture* fixture, } TEST(HTTPTransport, UnchunkedPlainText) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - std::unique_ptr body_stream( new StringHTTPBodyStream(kTextBody)); @@ -327,16 +314,10 @@ void RunUpload33k(bool has_content_length) { } TEST(HTTPTransport, Upload33k) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - RunUpload33k(true); } TEST(HTTPTransport, Upload33k_LengthUnknown) { - if (TestPaths::ExternalFilesUnavailable()) - DISABLED_TEST(); - // The same as Upload33k, but without declaring Content-Length ahead of time. RunUpload33k(false); } From e2b934cc404a25831bae9d1dbdb14c788b25039a Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 10 May 2018 13:32:11 -0700 Subject: [PATCH 276/326] fuchsia: Don't depend explicitly on the x64 toolchain Not surprisingly, x64-shared did not work in the arm64 build. 9_9 I've asked the Fuchsia toolchain team if this is the correct way to use the toolchain definition as it seems a bit awkward, but for now it seems like the only way to depend on the .so files in packages. Bug: crashpad:196 Change-Id: I998bf4feffd9499276bfc29229e5aadc1ae976f2 Reviewed-on: https://chromium-review.googlesource.com/1054487 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- BUILD.gn | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index c9da7c8a..00d86aca 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -40,10 +40,10 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { deps = [ ":crashpad_tests", - "snapshot:crashpad_snapshot_test_both_dt_hash_styles(//build/toolchain/fuchsia:x64-shared)", - "snapshot:crashpad_snapshot_test_module(//build/toolchain/fuchsia:x64-shared)", - "snapshot:crashpad_snapshot_test_module_large(//build/toolchain/fuchsia:x64-shared)", - "snapshot:crashpad_snapshot_test_module_small(//build/toolchain/fuchsia:x64-shared)", + "snapshot:crashpad_snapshot_test_both_dt_hash_styles(//build/toolchain/fuchsia:$current_cpu-shared)", + "snapshot:crashpad_snapshot_test_module(//build/toolchain/fuchsia:$current_cpu-shared)", + "snapshot:crashpad_snapshot_test_module_large(//build/toolchain/fuchsia:$current_cpu-shared)", + "snapshot:crashpad_snapshot_test_module_small(//build/toolchain/fuchsia:$current_cpu-shared)", "test:crashpad_test_test_multiprocess_exec_test_child", "util:http_transport_test_server", ] From 19e6087bb2084a3a4729b36d8ceb1c3e392fe747 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 10 May 2018 17:15:59 -0700 Subject: [PATCH 277/326] Don't restrict ImageAnnotationReader to a module's address range Annotations data structures may be dynamically allocated so could appear outside a modules's address range. Let ImageAnnotationReader use a ProcessMemoryRange for the process, rather than the module. Also add a test for linux. Bug: crashpad:30 Change-Id: Ibbf1d2fcb2e44b1b70c8a02e86c6f2fbd784535f Reviewed-on: https://chromium-review.googlesource.com/1054705 Commit-Queue: Joshua Peraza Reviewed-by: Scott Graham --- client/BUILD.gn | 1 + client/client_test.gyp | 1 + client/crashpad_client_linux_test.cc | 49 ++++++++++++++++++-- snapshot/elf/module_snapshot_elf.cc | 8 ++-- snapshot/elf/module_snapshot_elf.h | 5 +- snapshot/fuchsia/process_snapshot_fuchsia.cc | 10 ++-- snapshot/fuchsia/process_snapshot_fuchsia.h | 2 + snapshot/linux/process_snapshot_linux.cc | 26 +++++------ snapshot/linux/process_snapshot_linux.h | 2 + 9 files changed, 78 insertions(+), 26 deletions(-) diff --git a/client/BUILD.gn b/client/BUILD.gn index 9d1caaf1..662c2441 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -127,6 +127,7 @@ source_set("client_test") { deps = [ ":client", "../compat", + "../snapshot", "../test", "../third_party/gtest:gmock", "../third_party/gtest:gtest", diff --git a/client/client_test.gyp b/client/client_test.gyp index 61a4a7e5..0686cc67 100644 --- a/client/client_test.gyp +++ b/client/client_test.gyp @@ -24,6 +24,7 @@ 'client.gyp:crashpad_client', '../compat/compat.gyp:crashpad_compat', '../handler/handler.gyp:crashpad_handler', + '../snapshot/snapshot.gyp:crashpad_snapshot', '../test/test.gyp:crashpad_gmock_main', '../test/test.gyp:crashpad_test', '../third_party/gtest/gmock.gyp:gmock', diff --git a/client/crashpad_client_linux_test.cc b/client/crashpad_client_linux_test.cc index edfb5112..bacfcae3 100644 --- a/client/crashpad_client_linux_test.cc +++ b/client/crashpad_client_linux_test.cc @@ -21,14 +21,19 @@ #include #include "base/logging.h" +#include "client/annotation.h" +#include "client/annotation_list.h" #include "client/crash_report_database.h" #include "client/simulate_crash.h" #include "gtest/gtest.h" -#include "test/multiprocess_exec.h" +#include "snapshot/annotation_snapshot.h" +#include "snapshot/minidump/process_snapshot_minidump.h" #include "test/multiprocess.h" +#include "test/multiprocess_exec.h" #include "test/scoped_temp_dir.h" #include "test/test_paths.h" #include "util/file/file_io.h" +#include "util/file/filesystem.h" #include "util/linux/exception_handler_client.h" #include "util/linux/exception_information.h" #include "util/misc/address_types.h" @@ -94,6 +99,32 @@ TEST(CrashpadClient, SimulateCrash) { } } +constexpr char kTestAnnotationName[] = "name_of_annotation"; +constexpr char kTestAnnotationValue[] = "value_of_annotation"; + +void ValidateDump(const CrashReportDatabase::UploadReport* report) { + ProcessSnapshotMinidump minidump_snapshot; + ASSERT_TRUE(minidump_snapshot.Initialize(report->Reader())); + + for (const ModuleSnapshot* module : minidump_snapshot.Modules()) { + for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) { + if (static_cast(annotation.type) != + Annotation::Type::kString) { + continue; + } + + if (annotation.name == kTestAnnotationName) { + std::string value( + reinterpret_cast(annotation.value.data()), + annotation.value.size()); + EXPECT_EQ(value, kTestAnnotationValue); + return; + } + } + } + ADD_FAILURE(); +} + CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) { FileHandle in = StdioFileHandle(StdioStream::kStandardInput); @@ -106,6 +137,11 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) { base::FilePath handler_path = TestPaths::Executable().DirName().Append( FILE_PATH_LITERAL("crashpad_handler")); + crashpad::AnnotationList::Register(); + + static StringAnnotation<32> test_annotation(kTestAnnotationName); + test_annotation.Set(kTestAnnotationValue); + crashpad::CrashpadClient client; if (!client.StartHandlerAtCrash(handler_path, base::FilePath(temp_dir), @@ -145,14 +181,19 @@ class StartHandlerAtCrashTest : public MultiprocessExec { ASSERT_TRUE(database); std::vector reports; + ASSERT_EQ(database->GetCompletedReports(&reports), + CrashReportDatabase::kNoError); + EXPECT_EQ(reports.size(), 0u); + + reports.clear(); ASSERT_EQ(database->GetPendingReports(&reports), CrashReportDatabase::kNoError); EXPECT_EQ(reports.size(), 1u); - reports.clear(); - ASSERT_EQ(database->GetCompletedReports(&reports), + std::unique_ptr report; + ASSERT_EQ(database->GetReportForUploading(reports[0].uuid, &report), CrashReportDatabase::kNoError); - EXPECT_EQ(reports.size(), 0u); + ValidateDump(report.get()); } DISALLOW_COPY_AND_ASSIGN(StartHandlerAtCrashTest); diff --git a/snapshot/elf/module_snapshot_elf.cc b/snapshot/elf/module_snapshot_elf.cc index fbea74ba..62a961d3 100644 --- a/snapshot/elf/module_snapshot_elf.cc +++ b/snapshot/elf/module_snapshot_elf.cc @@ -27,10 +27,12 @@ namespace internal { ModuleSnapshotElf::ModuleSnapshotElf(const std::string& name, ElfImageReader* elf_reader, - ModuleSnapshot::ModuleType type) + ModuleSnapshot::ModuleType type, + ProcessMemoryRange* process_memory_range) : ModuleSnapshot(), name_(name), elf_reader_(elf_reader), + process_memory_range_(process_memory_range), crashpad_info_(), type_(type), initialized_() {} @@ -170,7 +172,7 @@ std::map ModuleSnapshotElf::AnnotationsSimpleMap() INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::map annotations; if (crashpad_info_ && crashpad_info_->SimpleAnnotations()) { - ImageAnnotationReader reader(elf_reader_->Memory()); + ImageAnnotationReader reader(process_memory_range_); reader.SimpleMap(crashpad_info_->SimpleAnnotations(), &annotations); } return annotations; @@ -180,7 +182,7 @@ std::vector ModuleSnapshotElf::AnnotationObjects() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::vector annotations; if (crashpad_info_ && crashpad_info_->AnnotationsList()) { - ImageAnnotationReader reader(elf_reader_->Memory()); + ImageAnnotationReader reader(process_memory_range_); reader.AnnotationsList(crashpad_info_->AnnotationsList(), &annotations); } return annotations; diff --git a/snapshot/elf/module_snapshot_elf.h b/snapshot/elf/module_snapshot_elf.h index 1f7cf651..15c3f3c2 100644 --- a/snapshot/elf/module_snapshot_elf.h +++ b/snapshot/elf/module_snapshot_elf.h @@ -41,9 +41,11 @@ class ModuleSnapshotElf final : public ModuleSnapshot { //! \param[in] name The pathname used to load the module from disk. //! \param[in] elf_reader An image reader for the module. //! \param[in] type The module's type. + //! \param[in] process_memory_range A memory reader for the target process. ModuleSnapshotElf(const std::string& name, ElfImageReader* elf_reader, - ModuleSnapshot::ModuleType type); + ModuleSnapshot::ModuleType type, + ProcessMemoryRange* process_memory_range); ~ModuleSnapshotElf() override; //! \brief Initializes the object. @@ -84,6 +86,7 @@ class ModuleSnapshotElf final : public ModuleSnapshot { private: std::string name_; ElfImageReader* elf_reader_; + ProcessMemoryRange* process_memory_range_; std::unique_ptr crashpad_info_; ModuleType type_; InitializationStateDcheck initialized_; diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc index 26e7b3be..62264c47 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -33,7 +33,8 @@ bool ProcessSnapshotFuchsia::Initialize(zx_handle_t process) { return false; } - if (!process_reader_.Initialize(process)) { + if (!process_reader_.Initialize(process) || + !memory_range_.Initialize(process_reader_->Memory(), true)) { return false; } @@ -204,8 +205,11 @@ void ProcessSnapshotFuchsia::InitializeThreads() { void ProcessSnapshotFuchsia::InitializeModules() { for (const ProcessReaderFuchsia::Module& reader_module : process_reader_.Modules()) { - auto module = std::make_unique( - reader_module.name, reader_module.reader, reader_module.type); + auto module = + std::make_unique(reader_module.name, + reader_module.reader, + reader_module.type, + &memory_range_); if (module->Initialize()) { modules_.push_back(std::move(module)); } diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.h b/snapshot/fuchsia/process_snapshot_fuchsia.h index bf1e9a52..2590ed58 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.h +++ b/snapshot/fuchsia/process_snapshot_fuchsia.h @@ -33,6 +33,7 @@ #include "snapshot/process_snapshot.h" #include "snapshot/unloaded_module_snapshot.h" #include "util/misc/initialization_state_dcheck.h" +#include "util/process/process_memory_range.h" namespace crashpad { @@ -132,6 +133,7 @@ class ProcessSnapshotFuchsia : public ProcessSnapshot { std::vector> modules_; std::unique_ptr exception_; ProcessReaderFuchsia process_reader_; + ProcessMemoryRange memory_range_; std::map annotations_simple_map_; UUID report_id_; UUID client_id_; diff --git a/snapshot/linux/process_snapshot_linux.cc b/snapshot/linux/process_snapshot_linux.cc index 9e212445..fbae9648 100644 --- a/snapshot/linux/process_snapshot_linux.cc +++ b/snapshot/linux/process_snapshot_linux.cc @@ -21,19 +21,9 @@ namespace crashpad { -ProcessSnapshotLinux::ProcessSnapshotLinux() - : ProcessSnapshot(), - annotations_simple_map_(), - snapshot_time_(), - report_id_(), - client_id_(), - threads_(), - exception_(), - system_(), - process_reader_(), - initialized_() {} +ProcessSnapshotLinux::ProcessSnapshotLinux() = default; -ProcessSnapshotLinux::~ProcessSnapshotLinux() {} +ProcessSnapshotLinux::~ProcessSnapshotLinux() = default; bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); @@ -43,11 +33,14 @@ bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection) { return false; } - if (!process_reader_.Initialize(connection)) { + if (!process_reader_.Initialize(connection) || + !memory_range_.Initialize(process_reader_.Memory(), + process_reader_.Is64Bit())) { return false; } system_.Initialize(&process_reader_, &snapshot_time_); + InitializeThreads(); InitializeModules(); @@ -227,8 +220,11 @@ void ProcessSnapshotLinux::InitializeThreads() { void ProcessSnapshotLinux::InitializeModules() { for (const ProcessReaderLinux::Module& reader_module : process_reader_.Modules()) { - auto module = std::make_unique( - reader_module.name, reader_module.elf_reader, reader_module.type); + auto module = + std::make_unique(reader_module.name, + reader_module.elf_reader, + reader_module.type, + &memory_range_); if (module->Initialize()) { modules_.push_back(std::move(module)); } diff --git a/snapshot/linux/process_snapshot_linux.h b/snapshot/linux/process_snapshot_linux.h index 25c414db..49d648f9 100644 --- a/snapshot/linux/process_snapshot_linux.h +++ b/snapshot/linux/process_snapshot_linux.h @@ -39,6 +39,7 @@ #include "util/linux/ptrace_connection.h" #include "util/misc/initialization_state_dcheck.h" #include "util/misc/uuid.h" +#include "util/process/process_memory_range.h" namespace crashpad { @@ -128,6 +129,7 @@ class ProcessSnapshotLinux final : public ProcessSnapshot { std::unique_ptr exception_; internal::SystemSnapshotLinux system_; ProcessReaderLinux process_reader_; + ProcessMemoryRange memory_range_; InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotLinux); From 95a052dc0d0d704ca0470aed934b43096175aad2 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 10 May 2018 16:51:14 -0700 Subject: [PATCH 278/326] fuchsia: When in Fuchsia, move helper binaries to subdir In my ongoing quest to break the Fuchsia tree in as many ways as possible... All binaries found in /system/test are automatically run. So when http_transport_test_server and crashpad_test_test_multiprocess_exec_test_child are unexpectedly run they just hang, breaking the overall test run. To avoid this, package them into a subdirectory which is the preferred solution. Bug: crashpad:196 Change-Id: If79c2c8c5493214fddbb2fa6de6f69ee0c78d9bd Reviewed-on: https://chromium-review.googlesource.com/1054782 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- BUILD.gn | 2 ++ test/test_paths.cc | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 00d86aca..872dfdef 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -54,9 +54,11 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { }, { name = "crashpad_test_test_multiprocess_exec_test_child" + dest = "crashpad_test_data/crashpad_test_test_multiprocess_exec_test_child" }, { name = "http_transport_test_server" + dest = "crashpad_test_data/http_transport_test_server" }, ] diff --git a/test/test_paths.cc b/test/test_paths.cc index b06fb7f3..f8bef00c 100644 --- a/test/test_paths.cc +++ b/test/test_paths.cc @@ -133,7 +133,7 @@ base::FilePath TestPaths::Executable() { // not appear as expected at /pkg/bin. Override the default of /pkg/bin/app // so that tests can find the correct location for now. // https://crashpad.chromium.org/bug/196. - executable_path = base::FilePath("/system/test/app"); + executable_path = base::FilePath("/system/test/crashpad_test_data/app"); #endif return executable_path; } @@ -207,7 +207,8 @@ base::FilePath TestPaths::BuildArtifact( // binaries do not appear as expected at /pkg/bin. Override the default of // /pkg/bin/app so that tests can find the correct location for now. // https://crashpad.chromium.org/bug/196. - directory = base::FilePath(FILE_PATH_LITERAL("/system/test")); + directory = + base::FilePath(FILE_PATH_LITERAL("/system/test/crashpad_test_data")); #else directory = base::FilePath(FILE_PATH_LITERAL("/pkg/bin")); #endif From cc1573cd52f108e1e31310943e93f5b88af5477a Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 10 May 2018 17:29:31 -0700 Subject: [PATCH 279/326] fuchsia: Fix compile error after 19e6087bb Bug: crashpad:196 Change-Id: I82cf1c5384ebfc2fb7882e69145b211c4b24f7c5 Reviewed-on: https://chromium-review.googlesource.com/1054576 Reviewed-by: Scott Graham Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- snapshot/fuchsia/process_snapshot_fuchsia.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapshot/fuchsia/process_snapshot_fuchsia.cc b/snapshot/fuchsia/process_snapshot_fuchsia.cc index 62264c47..cc66db25 100644 --- a/snapshot/fuchsia/process_snapshot_fuchsia.cc +++ b/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -34,7 +34,7 @@ bool ProcessSnapshotFuchsia::Initialize(zx_handle_t process) { } if (!process_reader_.Initialize(process) || - !memory_range_.Initialize(process_reader_->Memory(), true)) { + !memory_range_.Initialize(process_reader_.Memory(), true)) { return false; } From ef6d91b16616df58a2379e55a3f9edecff0b904d Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 10 May 2018 22:58:14 -0700 Subject: [PATCH 280/326] Update cpp-httplib to 37130cd Also exclude http_transport_test_server from Android where it doesn't build. Change-Id: I51cc3f50e4fb9db982d91b2924b8ea87d86926d4 Reviewed-on: https://chromium-review.googlesource.com/1054160 Reviewed-by: Scott Graham Commit-Queue: Joshua Peraza --- third_party/cpp-httplib/README.crashpad | 2 +- third_party/cpp-httplib/cpp-httplib/README.md | 47 ++- third_party/cpp-httplib/cpp-httplib/httplib.h | 282 ++++++++++++------ util/BUILD.gn | 50 ++-- util/net/http_transport_test_server.cc | 2 +- util/util_test.gyp | 52 ++-- 6 files changed, 288 insertions(+), 147 deletions(-) diff --git a/third_party/cpp-httplib/README.crashpad b/third_party/cpp-httplib/README.crashpad index e9a0bf1c..81010326 100644 --- a/third_party/cpp-httplib/README.crashpad +++ b/third_party/cpp-httplib/README.crashpad @@ -1,7 +1,7 @@ Name: cpp-httplib Short Name: cpp-httplib URL: https://github.com/yhirose/cpp-httplib -Revision: 4320d7ba3e8b7388e1443eb54c039a1304cf7a6b +Revision: 37130cd7f9df4fb4454d94dd9f0a3bb7c6ccbad8 License: MIT License File: cpp-httplib/LICENSE Security Critical: no (test only) diff --git a/third_party/cpp-httplib/cpp-httplib/README.md b/third_party/cpp-httplib/cpp-httplib/README.md index 291e4bd7..84685b4a 100644 --- a/third_party/cpp-httplib/cpp-httplib/README.md +++ b/third_party/cpp-httplib/cpp-httplib/README.md @@ -19,11 +19,11 @@ int main(void) Server svr; - svr.get("/hi", [](const Request& req, Response& res) { + svr.Get("/hi", [](const Request& req, Response& res) { res.set_content("Hello World!", "text/plain"); }); - svr.get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) { + svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) { auto numbers = req.matches[1]; res.set_content(numbers, "text/plain"); }); @@ -32,13 +32,15 @@ int main(void) } ``` +`Post`, `Put`, `Delete` and `Options` methods are also supported. + ### Method Chain ```cpp -svr.get("/get", [](const auto& req, auto& res) { +svr.Get("/get", [](const auto& req, auto& res) { res.set_content("get", "text/plain"); }) - .post("/post", [](const auto& req, auto& res) { + .Post("/post", [](const auto& req, auto& res) { res.set_content(req.body(), "text/plain"); }) .listen("localhost", 1234); @@ -72,7 +74,7 @@ svr.set_error_handler([](const auto& req, auto& res) { ### 'multipart/form-data' POST data ```cpp -svr.post("/multipart", [&](const auto& req, auto& res) { +svr.Post("/multipart", [&](const auto& req, auto& res) { auto size = req.files.size(); auto ret = req.has_file("name1")); const auto& file = req.get_file_value("name1"); @@ -95,7 +97,7 @@ int main(void) { httplib::Client cli("localhost", 1234); - auto res = cli.get("/hi"); + auto res = cli.Get("/hi"); if (res && res->status == 200) { std::cout << res->body << std::endl; } @@ -105,17 +107,36 @@ int main(void) ### POST ```c++ -res = cli.post("/post", "text", "text/plain"); -res = cli.post("/person", "name=john1¬e=coder", "application/x-www-form-urlencoded"); +res = cli.Post("/post", "text", "text/plain"); +res = cli.Post("/person", "name=john1¬e=coder", "application/x-www-form-urlencoded"); ``` ### POST with parameters ```c++ -httplib::Map params; +httplib::Params params; params["name"] = "john"; params["note"] = "coder"; -auto res = cli.post("/post", params); +auto res = cli.Post("/post", params); +``` + +### PUT + +```c++ +res = cli.Put("/resource/foo", "text", "text/plain"); +``` + +### DELETE + +```c++ +res = cli.Delete("/resource/foo"); +``` + +### OPTIONS + +```c++ +res = cli.Options("*"); +res = cli.Options("/resource/foo"); ``` ### Connection Timeout @@ -130,7 +151,7 @@ httplib::Client client(url, port); // prints: 0 / 000 bytes => 50% complete std::shared_ptr res = - cli.get("/", [](uint64_t len, uint64_t total) { + cli.Get("/", [](uint64_t len, uint64_t total) { printf("%lld / %lld bytes => %d%% complete\n", len, total, (int)((len/total)*100)); @@ -150,7 +171,7 @@ httplib::Client cli("httpbin.org", 80); // 'Range: bytes=1-10' httplib::Headers headers = { httplib::make_range_header(1, 10) }; -auto res = cli.get("/range/32", headers); +auto res = cli.Get("/range/32", headers); // res->status should be 206. // res->body should be "bcdefghijk". ``` @@ -185,4 +206,4 @@ The server applies gzip compression to the following MIME type contents: License ------- -MIT license (© 2017 Yuji Hirose) +MIT license (© 2018 Yuji Hirose) diff --git a/third_party/cpp-httplib/cpp-httplib/httplib.h b/third_party/cpp-httplib/cpp-httplib/httplib.h index 693f430a..9a8deedd 100644 --- a/third_party/cpp-httplib/cpp-httplib/httplib.h +++ b/third_party/cpp-httplib/cpp-httplib/httplib.h @@ -48,8 +48,10 @@ typedef SOCKET socket_t; #include #include #include +#include typedef int socket_t; +#define INVALID_SOCKET (-1) #endif #include @@ -193,8 +195,12 @@ public: virtual bool is_valid() const; - Server& get(const char* pattern, Handler handler); - Server& post(const char* pattern, Handler handler); + Server& Get(const char* pattern, Handler handler); + Server& Post(const char* pattern, Handler handler); + + Server& Put(const char* pattern, Handler handler); + Server& Delete(const char* pattern, Handler handler); + Server& Options(const char* pattern, Handler handler); bool set_base_dir(const char* path); @@ -235,6 +241,9 @@ private: std::string base_dir_; Handlers get_handlers_; Handlers post_handlers_; + Handlers put_handlers_; + Handlers delete_handlers_; + Handlers options_handlers_; Handler error_handler_; Logger logger_; @@ -255,17 +264,26 @@ public: virtual bool is_valid() const; - std::shared_ptr get(const char* path, Progress progress = nullptr); - std::shared_ptr get(const char* path, const Headers& headers, Progress progress = nullptr); + std::shared_ptr Get(const char* path, Progress progress = nullptr); + std::shared_ptr Get(const char* path, const Headers& headers, Progress progress = nullptr); - std::shared_ptr head(const char* path); - std::shared_ptr head(const char* path, const Headers& headers); + std::shared_ptr Head(const char* path); + std::shared_ptr Head(const char* path, const Headers& headers); - std::shared_ptr post(const char* path, const std::string& body, const char* content_type); - std::shared_ptr post(const char* path, const Headers& headers, const std::string& body, const char* content_type); + std::shared_ptr Post(const char* path, const std::string& body, const char* content_type); + std::shared_ptr Post(const char* path, const Headers& headers, const std::string& body, const char* content_type); - std::shared_ptr post(const char* path, const Params& params); - std::shared_ptr post(const char* path, const Headers& headers, const Params& params); + std::shared_ptr Post(const char* path, const Params& params); + std::shared_ptr Post(const char* path, const Headers& headers, const Params& params); + + std::shared_ptr Put(const char* path, const std::string& body, const char* content_type); + std::shared_ptr Put(const char* path, const Headers& headers, const std::string& body, const char* content_type); + + std::shared_ptr Delete(const char* path); + std::shared_ptr Delete(const char* path, const Headers& headers); + + std::shared_ptr Options(const char* path); + std::shared_ptr Options(const char* path, const Headers& headers); bool send(Request& req, Response& res); @@ -316,6 +334,7 @@ private: virtual bool read_and_close_socket(socket_t sock); SSL_CTX* ctx_; + std::mutex ctx_mutex_; }; class SSLClient : public Client { @@ -334,6 +353,7 @@ private: virtual bool read_and_close_socket(socket_t sock, Request& req, Response& res); SSL_CTX* ctx_; + std::mutex ctx_mutex_; }; #endif @@ -541,13 +561,13 @@ socket_t create_socket(const char* host, int port, Fn fn, int socket_flags = 0) auto service = std::to_string(port); if (getaddrinfo(host, service.c_str(), &hints, &result)) { - return -1; + return INVALID_SOCKET; } for (auto rp = result; rp; rp = rp->ai_next) { // Create a socket auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (sock == -1) { + if (sock == INVALID_SOCKET) { continue; } @@ -565,7 +585,7 @@ socket_t create_socket(const char* host, int port, Fn fn, int socket_flags = 0) } freeaddrinfo(result); - return -1; + return INVALID_SOCKET; } inline void set_nonblocking(socket_t sock, bool nonblocking) @@ -590,20 +610,18 @@ inline bool is_connection_error() inline std::string get_remote_addr(socket_t sock) { struct sockaddr_storage addr; - char ipstr[INET6_ADDRSTRLEN]; socklen_t len = sizeof(addr); - getpeername(sock, (struct sockaddr*)&addr, &len); - // deal with both IPv4 and IPv6: - if (addr.ss_family == AF_INET) { - auto s = (struct sockaddr_in *)&addr; - inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof(ipstr)); - } else { // AF_INET6 - auto s = (struct sockaddr_in6 *)&addr; - inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof(ipstr)); + if (!getpeername(sock, (struct sockaddr*)&addr, &len)) { + char ipstr[NI_MAXHOST]; + + if (!getnameinfo((struct sockaddr*)&addr, len, + ipstr, sizeof(ipstr), nullptr, 0, NI_NUMERICHOST)) { + return ipstr; + } } - return ipstr; + return std::string(); } inline bool is_file(const std::string& path) @@ -767,7 +785,7 @@ inline bool read_headers(Stream& strm, Headers& headers) return true; } -bool read_content_with_length(Stream& strm, std::string& out, size_t len, Progress progress) +inline bool read_content_with_length(Stream& strm, std::string& out, size_t len, Progress progress) { out.assign(len, 0); size_t r = 0; @@ -787,7 +805,7 @@ bool read_content_with_length(Stream& strm, std::string& out, size_t len, Progre return true; } -bool read_content_without_length(Stream& strm, std::string& out) +inline bool read_content_without_length(Stream& strm, std::string& out) { for (;;) { char byte; @@ -803,7 +821,7 @@ bool read_content_without_length(Stream& strm, std::string& out) return true; } -bool read_content_chunked(Stream& strm, std::string& out) +inline bool read_content_chunked(Stream& strm, std::string& out) { const auto bufsiz = 16; char buf[bufsiz]; @@ -1163,18 +1181,8 @@ inline bool can_compress(const std::string& content_type) { content_type == "application/xhtml+xml"; } -inline void compress(const Request& req, Response& res) +inline void compress(std::string& content) { - // TODO: Server version is HTTP/1.1 and 'Accpet-Encoding' has gzip, not gzip;q=0 - const auto& encodings = req.get_header_value("Accept-Encoding"); - if (encodings.find("gzip") == std::string::npos) { - return; - } - - if (!can_compress(res.get_header_value("Content-Type"))) { - return; - } - z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; @@ -1185,8 +1193,8 @@ inline void compress(const Request& req, Response& res) return; } - strm.avail_in = res.body.size(); - strm.next_in = (Bytef *)res.body.data(); + strm.avail_in = content.size(); + strm.next_in = (Bytef *)content.data(); std::string compressed; @@ -1199,17 +1207,13 @@ inline void compress(const Request& req, Response& res) compressed.append(buff, bufsiz - strm.avail_out); } while (strm.avail_out == 0); - res.set_header("Content-Encoding", "gzip"); - res.body.swap(compressed); + content.swap(compressed); deflateEnd(&strm); } -inline void decompress_request_body(Request& req) +inline void decompress(std::string& content) { - if (req.get_header_value("Content-Encoding") != "gzip") - return; - z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; @@ -1223,8 +1227,8 @@ inline void decompress_request_body(Request& req) return; } - strm.avail_in = req.body.size(); - strm.next_in = (Bytef *)req.body.data(); + strm.avail_in = content.size(); + strm.next_in = (Bytef *)content.data(); std::string decompressed; @@ -1237,7 +1241,7 @@ inline void decompress_request_body(Request& req) decompressed.append(buff, bufsiz - strm.avail_out); } while (strm.avail_out == 0); - req.body.swap(decompressed); + content.swap(decompressed); inflateEnd(&strm); } @@ -1412,7 +1416,7 @@ inline std::string SocketStream::get_remote_addr() { inline Server::Server(HttpVersion http_version) : http_version_(http_version) , is_running_(false) - , svr_sock_(-1) + , svr_sock_(INVALID_SOCKET) , running_threads_(0) { #ifndef _WIN32 @@ -1424,18 +1428,36 @@ inline Server::~Server() { } -inline Server& Server::get(const char* pattern, Handler handler) +inline Server& Server::Get(const char* pattern, Handler handler) { get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); return *this; } -inline Server& Server::post(const char* pattern, Handler handler) +inline Server& Server::Post(const char* pattern, Handler handler) { post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); return *this; } +inline Server& Server::Put(const char* pattern, Handler handler) +{ + put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server& Server::Delete(const char* pattern, Handler handler) +{ + delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server& Server::Options(const char* pattern, Handler handler) +{ + options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + inline bool Server::set_base_dir(const char* path) { if (detail::is_dir(path)) { @@ -1479,16 +1501,16 @@ inline bool Server::is_running() const inline void Server::stop() { if (is_running_) { - assert(svr_sock_ != -1); + assert(svr_sock_ != INVALID_SOCKET); detail::shutdown_socket(svr_sock_); detail::close_socket(svr_sock_); - svr_sock_ = -1; + svr_sock_ = INVALID_SOCKET; } } inline bool Server::parse_request_line(const char* s, Request& req) { - static std::regex re("(GET|HEAD|POST) ([^?]+)(?:\\?(.+?))? (HTTP/1\\.[01])\r\n"); + static std::regex re("(GET|HEAD|POST|PUT|DELETE|OPTIONS) ([^?]+)(?:\\?(.+?))? (HTTP/1\\.[01])\r\n"); std::cmatch m; if (std::regex_match(s, m, re)) { @@ -1529,7 +1551,13 @@ inline void Server::write_response(Stream& strm, bool last_connection, const Req if (!res.body.empty()) { #ifdef CPPHTTPLIB_ZLIB_SUPPORT - detail::compress(req, res); + // TODO: Server version is HTTP/1.1 and 'Accpet-Encoding' has gzip, not gzip;q=0 + const auto& encodings = req.get_header_value("Accept-Encoding"); + if (encodings.find("gzip") != std::string::npos && + detail::can_compress(res.get_header_value("Content-Type"))) { + detail::compress(res.body); + res.set_header("Content-Encoding", "gzip"); + } #endif if (!res.has_header("Content-Type")) { @@ -1597,7 +1625,7 @@ inline int Server::bind_internal(const char* host, int port, int socket_flags) } svr_sock_ = create_server_socket(host, port, socket_flags); - if (svr_sock_ == -1) { + if (svr_sock_ == INVALID_SOCKET) { return -1; } @@ -1623,7 +1651,7 @@ inline bool Server::listen_internal() auto val = detail::select_read(svr_sock_, 0, 100000); if (val == 0) { // Timeout - if (svr_sock_ == -1) { + if (svr_sock_ == INVALID_SOCKET) { // The server socket was closed by 'stop' method. break; } @@ -1632,8 +1660,8 @@ inline bool Server::listen_internal() socket_t sock = accept(svr_sock_, NULL, NULL); - if (sock == -1) { - if (svr_sock_ != -1) { + if (sock == INVALID_SOCKET) { + if (svr_sock_ != INVALID_SOCKET) { detail::close_socket(svr_sock_); ret = false; } else { @@ -1682,6 +1710,12 @@ inline bool Server::routing(Request& req, Response& res) return dispatch_request(req, res, get_handlers_); } else if (req.method == "POST") { return dispatch_request(req, res, post_handlers_); + } else if (req.method == "PUT") { + return dispatch_request(req, res, put_handlers_); + } else if (req.method == "DELETE") { + return dispatch_request(req, res, delete_handlers_); + } else if (req.method == "OPTIONS") { + return dispatch_request(req, res, options_handlers_); } return false; } @@ -1732,7 +1766,7 @@ inline bool Server::process_request(Stream& strm, bool last_connection) req.set_header("REMOTE_ADDR", strm.get_remote_addr().c_str()); // Body - if (req.method == "POST") { + if (req.method == "POST" || req.method == "PUT") { if (!detail::read_content(strm, req)) { res.status = 400; write_response(strm, last_connection, req, res); @@ -1741,15 +1775,15 @@ inline bool Server::process_request(Stream& strm, bool last_connection) const auto& content_type = req.get_header_value("Content-Type"); -#ifdef CPPHTTPLIB_ZLIB_SUPPORT - detail::decompress_request_body(req); -#else if (req.get_header_value("Content-Encoding") == "gzip") { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + detail::decompress(req.body); +#else res.status = 415; write_response(strm, last_connection, req, res); return ret; - } #endif + } if (!content_type.find("application/x-www-form-urlencoded")) { detail::parse_query_text(req.body, req.params); @@ -1861,7 +1895,7 @@ inline bool Client::send(Request& req, Response& res) } auto sock = create_client_socket(); - if (sock == -1) { + if (sock == INVALID_SOCKET) { return false; } @@ -1934,6 +1968,14 @@ inline bool Client::process_request(Stream& strm, Request& req, Response& res) if (!detail::read_content(strm, res, req.progress)) { return false; } + + if (res.get_header_value("Content-Encoding") == "gzip") { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + detail::decompress(res.body); +#else + return false; +#endif + } } return true; @@ -1946,12 +1988,12 @@ inline bool Client::read_and_close_socket(socket_t sock, Request& req, Response& }); } -inline std::shared_ptr Client::get(const char* path, Progress progress) +inline std::shared_ptr Client::Get(const char* path, Progress progress) { - return get(path, Headers(), progress); + return Get(path, Headers(), progress); } -inline std::shared_ptr Client::get(const char* path, const Headers& headers, Progress progress) +inline std::shared_ptr Client::Get(const char* path, const Headers& headers, Progress progress) { Request req; req.method = "GET"; @@ -1964,12 +2006,12 @@ inline std::shared_ptr Client::get(const char* path, const Headers& he return send(req, *res) ? res : nullptr; } -inline std::shared_ptr Client::head(const char* path) +inline std::shared_ptr Client::Head(const char* path) { - return head(path, Headers()); + return Head(path, Headers()); } -inline std::shared_ptr Client::head(const char* path, const Headers& headers) +inline std::shared_ptr Client::Head(const char* path, const Headers& headers) { Request req; req.method = "HEAD"; @@ -1981,13 +2023,13 @@ inline std::shared_ptr Client::head(const char* path, const Headers& h return send(req, *res) ? res : nullptr; } -inline std::shared_ptr Client::post( +inline std::shared_ptr Client::Post( const char* path, const std::string& body, const char* content_type) { - return post(path, Headers(), body, content_type); + return Post(path, Headers(), body, content_type); } -inline std::shared_ptr Client::post( +inline std::shared_ptr Client::Post( const char* path, const Headers& headers, const std::string& body, const char* content_type) { Request req; @@ -2003,12 +2045,12 @@ inline std::shared_ptr Client::post( return send(req, *res) ? res : nullptr; } -inline std::shared_ptr Client::post(const char* path, const Params& params) +inline std::shared_ptr Client::Post(const char* path, const Params& params) { - return post(path, Headers(), params); + return Post(path, Headers(), params); } -inline std::shared_ptr Client::post(const char* path, const Headers& headers, const Params& params) +inline std::shared_ptr Client::Post(const char* path, const Headers& headers, const Params& params) { std::string query; for (auto it = params.begin(); it != params.end(); ++it) { @@ -2020,7 +2062,63 @@ inline std::shared_ptr Client::post(const char* path, const Headers& h query += it->second; } - return post(path, headers, query, "application/x-www-form-urlencoded"); + return Post(path, headers, query, "application/x-www-form-urlencoded"); +} + +inline std::shared_ptr Client::Put( + const char* path, const std::string& body, const char* content_type) +{ + return Put(path, Headers(), body, content_type); +} + +inline std::shared_ptr Client::Put( + const char* path, const Headers& headers, const std::string& body, const char* content_type) +{ + Request req; + req.method = "PUT"; + req.headers = headers; + req.path = path; + + req.headers.emplace("Content-Type", content_type); + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +inline std::shared_ptr Client::Delete(const char* path) +{ + return Delete(path, Headers()); +} + +inline std::shared_ptr Client::Delete(const char* path, const Headers& headers) +{ + Request req; + req.method = "DELETE"; + req.path = path; + req.headers = headers; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +inline std::shared_ptr Client::Options(const char* path) +{ + return Options(path, Headers()); +} + +inline std::shared_ptr Client::Options(const char* path, const Headers& headers) +{ + Request req; + req.method = "OPTIONS"; + req.path = path; + req.headers = headers; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; } /* @@ -2032,12 +2130,19 @@ namespace detail { template inline bool read_and_close_socket_ssl( socket_t sock, bool keep_alive, - SSL_CTX* ctx, U SSL_connect_or_accept, V setup, + // TODO: OpenSSL 1.0.2 occasionally crashes... The upcoming 1.1.0 is going to be thread safe. + SSL_CTX* ctx, std::mutex& ctx_mutex, + U SSL_connect_or_accept, V setup, T callback) { - auto ssl = SSL_new(ctx); - if (!ssl) { - return false; + SSL* ssl = nullptr; + { + std::lock_guard guard(ctx_mutex); + + ssl = SSL_new(ctx); + if (!ssl) { + return false; + } } auto bio = BIO_new_socket(sock, BIO_NOCLOSE); @@ -2069,8 +2174,14 @@ inline bool read_and_close_socket_ssl( } SSL_shutdown(ssl); - SSL_free(ssl); + + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } + close_socket(sock); + return ret; } @@ -2158,7 +2269,7 @@ inline bool SSLServer::read_and_close_socket(socket_t sock) return detail::read_and_close_socket_ssl( sock, keep_alive, - ctx_, + ctx_, ctx_mutex_, SSL_accept, [](SSL* /*ssl*/) {}, [this](Stream& strm, bool last_connection) { @@ -2190,7 +2301,8 @@ inline bool SSLClient::read_and_close_socket(socket_t sock, Request& req, Respon { return is_valid() && detail::read_and_close_socket_ssl( sock, false, - ctx_, SSL_connect, + ctx_, ctx_mutex_, + SSL_connect, [&](SSL* ssl) { SSL_set_tlsext_host_name(ssl, host_.c_str()); }, diff --git a/util/BUILD.gn b/util/BUILD.gn index 21e6fed2..4e7e40ca 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -435,28 +435,30 @@ static_library("util") { } } -crashpad_executable("http_transport_test_server") { - testonly = true - sources = [ - "net/http_transport_test_server.cc", - ] - - deps = [ - ":util", - "../third_party/cpp-httplib", - "../third_party/mini_chromium:base", - "../third_party/zlib", - "../tools:tool_support", - ] - - if (crashpad_is_standalone) { - remove_configs = [ - "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", +if (!crashpad_is_android) { + crashpad_executable("http_transport_test_server") { + testonly = true + sources = [ + "net/http_transport_test_server.cc", ] - } - if (crashpad_is_win) { - libs = [ "ws2_32.lib" ] + deps = [ + ":util", + "../third_party/cpp-httplib", + "../third_party/mini_chromium:base", + "../third_party/zlib", + "../tools:tool_support", + ] + + if (crashpad_is_standalone) { + remove_configs = [ + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", + ] + } + + if (crashpad_is_win) { + libs = [ "ws2_32.lib" ] + } } } @@ -604,9 +606,11 @@ source_set("util_test") { "../third_party/zlib", ] - data_deps = [ - ":http_transport_test_server", - ] + if (!crashpad_is_android) { + data_deps = [ + ":http_transport_test_server", + ] + } if (crashpad_is_mac) { libs = [ "Foundation.framework" ] diff --git a/util/net/http_transport_test_server.cc b/util/net/http_transport_test_server.cc index 228d5c1c..b050a71f 100644 --- a/util/net/http_transport_test_server.cc +++ b/util/net/http_transport_test_server.cc @@ -57,7 +57,7 @@ int HttpTransportTestServerMain(int argc, char* argv[]) { std::string to_stdout; - svr.post("/upload", + svr.Post("/upload", [&response, &response_code, &svr, &to_stdout]( const httplib::Request& req, httplib::Response& res) { res.status = response_code; diff --git a/util/util_test.gyp b/util/util_test.gyp index e26b69aa..d28fe9ee 100644 --- a/util/util_test.gyp +++ b/util/util_test.gyp @@ -165,32 +165,36 @@ }], ], }, - { - 'target_name': 'http_transport_test_server', - 'type': 'executable', - 'dependencies': [ - '../third_party/mini_chromium/mini_chromium.gyp:base', - '../third_party/zlib/zlib.gyp:zlib', - '../tools/tools.gyp:crashpad_tool_support', - '../util/util.gyp:crashpad_util', - ], - 'sources': [ - 'net/http_transport_test_server.cc', - ], - 'include_dirs': [ - '..', - ], - 'xcode_settings': { - 'WARNING_CFLAGS!': [ - '-Wexit-time-destructors', - ], - }, - 'cflags!': [ - '-Wexit-time-destructors', - ], - }, ], 'conditions': [ + ['OS!="android"', { + 'targets': [ + { + 'target_name': 'http_transport_test_server', + 'type': 'executable', + 'dependencies': [ + '../third_party/mini_chromium/mini_chromium.gyp:base', + '../third_party/zlib/zlib.gyp:zlib', + '../tools/tools.gyp:crashpad_tool_support', + '../util/util.gyp:crashpad_util', + ], + 'sources': [ + 'net/http_transport_test_server.cc', + ], + 'include_dirs': [ + '..', + ], + 'xcode_settings': { + 'WARNING_CFLAGS!': [ + '-Wexit-time-destructors', + ], + }, + 'cflags!': [ + '-Wexit-time-destructors', + ], + }, + ], + }], ['OS=="win"', { 'targets': [ { From 9115494e8aa488bbd38bf3c012dc0a60ae2ad8cd Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 11 May 2018 11:54:42 -0700 Subject: [PATCH 281/326] fuchsia: Increase timeout for WorkerThread.DoWork test Because of Fuchsia's scheduler the seemingly reasonable time check fails occasionally. Bug: crashpad:196, crashpad:231 Change-Id: Ic212a50e73e283ce3d279dd8c28adecbc432e39c Reviewed-on: https://chromium-review.googlesource.com/1055805 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- util/thread/worker_thread_test.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/util/thread/worker_thread_test.cc b/util/thread/worker_thread_test.cc index 9b11fef1..4e4fbf9a 100644 --- a/util/thread/worker_thread_test.cc +++ b/util/thread/worker_thread_test.cc @@ -71,7 +71,18 @@ TEST(WorkerThread, DoWork) { thread.Stop(); EXPECT_FALSE(thread.is_running()); - EXPECT_GE(1 * kNanosecondsPerSecond, ClockMonotonicNanoseconds() - start); +// Fuchsia's scheduler is very antagonistic. The assumption that the two work +// items complete in some particular amount of time is strictly incorrect, but +// also somewhat useful. The expected time "should" be ~40-50ms with a work +// interval of 0.05s, but on Fuchsia, 1200ms was observed. So, on Fuchsia, use a +// much larger timeout. See https://crashpad.chromium.org/bug/231. +#if defined(OS_FUCHSIA) + constexpr uint64_t kUpperBoundTime = 10; +#else + constexpr uint64_t kUpperBoundTime = 1; +#endif + EXPECT_GE(kUpperBoundTime * kNanosecondsPerSecond, + ClockMonotonicNanoseconds() - start); } TEST(WorkerThread, StopBeforeDoWork) { From dea8fa51d0b5baef8140584285b98e21eba522f8 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 14 May 2018 12:45:51 -0700 Subject: [PATCH 282/326] fuchsia: Changes to allow analyzer-style exception handling The integration into the Fuchsia tree is still being negotiated, but I've added crashsvc on that side https://fuchsia.googlesource.com/zircon/+/HEAD/system/core/crashsvc/crashsvc.cpp, which handles exceptions and spawns something to actually deal with the crashed process. This means that there's no resident thing, so it seems simplest to use handler/crash_report_exception_handler directly, rather than add another wrapper binary. At some point it may make sense to roll this functionality back into Crashpad upstream, we'll see how it evolves. Additionally, the "normal" crashpad_handler model may still be necessary if Chrome wants to use a copy locally (rather than the system handler). To accomplish this: - Split crashpad_handler and crashpad_database_util packages so crashpad_database_util can still be included into the system image for debugging. - Add handle-based version of HandleException() to CrashReportExceptionHandler (and also remove the "type" argument because I've come around to realizing there's no point to it, finally. :) Bug: crashpad:196 Change-Id: I38872183ee3691c0938c5b761e6b73c80019f355 Reviewed-on: https://chromium-review.googlesource.com/1057833 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- BUILD.gn | 12 +++++++++++- .../fuchsia/crash_report_exception_handler.cc | 19 ++++++++++--------- .../fuchsia/crash_report_exception_handler.h | 17 +++++++++++++++-- handler/fuchsia/exception_handler_server.cc | 4 ++-- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 872dfdef..14c4387d 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -98,13 +98,23 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { deps = [ "handler:crashpad_handler", - "tools:crashpad_database_util", ] binaries = [ { name = "crashpad_handler" }, + ] + } + + package("crashpad_database_util") { + deprecated_system_image = true + + deps = [ + "tools:crashpad_database_util", + ] + + binaries = [ { name = "crashpad_database_util" }, diff --git a/handler/fuchsia/crash_report_exception_handler.cc b/handler/fuchsia/crash_report_exception_handler.cc index 06e9d145..80b34a76 100644 --- a/handler/fuchsia/crash_report_exception_handler.cc +++ b/handler/fuchsia/crash_report_exception_handler.cc @@ -60,8 +60,7 @@ CrashReportExceptionHandler::CrashReportExceptionHandler( CrashReportExceptionHandler::~CrashReportExceptionHandler() {} -bool CrashReportExceptionHandler::HandleException(uint32_t type, - uint64_t process_id, +bool CrashReportExceptionHandler::HandleException(uint64_t process_id, uint64_t thread_id) { // TODO(scottmg): This function needs to be instrumented with metrics calls, // https://crashpad.chromium.org/bug/230. @@ -73,20 +72,23 @@ bool CrashReportExceptionHandler::HandleException(uint32_t type, return false; } - ScopedTaskSuspend suspend(process.get()); - base::ScopedZxHandle thread(GetChildHandleByKoid(process.get(), thread_id)); if (!thread.is_valid()) { return false; } + return HandleExceptionHandles(process.get(), thread.get()); +} + +bool CrashReportExceptionHandler::HandleExceptionHandles(zx_handle_t process, + zx_handle_t thread) { // Now that the thread has been successfully retrieved, it is possible to // correctly call zx_task_resume() to continue exception processing, even if // something else during this function fails. - ScopedZxTaskResumeAfterException resume(thread.get()); + ScopedZxTaskResumeAfterException resume(thread); ProcessSnapshotFuchsia process_snapshot; - if (!process_snapshot.Initialize(process.get())) { + if (!process_snapshot.Initialize(process)) { return false; } @@ -95,7 +97,7 @@ bool CrashReportExceptionHandler::HandleException(uint32_t type, if (client_options.crashpad_handler_behavior != TriState::kDisabled) { zx_exception_report_t report; - zx_status_t status = zx_object_get_info(thread.get(), + zx_status_t status = zx_object_get_info(thread, ZX_INFO_THREAD_EXCEPTION_REPORT, &report, sizeof(report), @@ -107,8 +109,7 @@ bool CrashReportExceptionHandler::HandleException(uint32_t type, return false; } - DCHECK_EQ(type, report.header.type); - + zx_koid_t thread_id = GetKoidForHandle(thread); if (!process_snapshot.InitializeException(thread_id, report)) { return false; } diff --git a/handler/fuchsia/crash_report_exception_handler.h b/handler/fuchsia/crash_report_exception_handler.h index a6bda613..9f140512 100644 --- a/handler/fuchsia/crash_report_exception_handler.h +++ b/handler/fuchsia/crash_report_exception_handler.h @@ -16,6 +16,7 @@ #define CRASHPAD_HANDLER_FUCHSIA_CRASH_REPORT_EXCEPTION_HANDLER_H_ #include +#include #include #include @@ -64,12 +65,24 @@ class CrashReportExceptionHandler { //! This function is expected to call `zx_task_resume()` in order to complete //! handling of the exception. //! - //! \param[in] type The type of exception, a `ZX_EXCP_*` value. //! \param[in] process_id The koid of the process which sustained the //! exception. //! \param[in] thread_id The koid of the thread which sustained the exception. //! \return `true` on success, or `false` with an error logged. - bool HandleException(uint32_t type, uint64_t process_id, uint64_t thread_id); + bool HandleException(uint64_t process_id, uint64_t thread_id); + + //! \brief Called when the exception handler server has caught an exception + //! and wants a crash dump to be taken. + //! + //! This function is expected to call `zx_task_resume()` in order to complete + //! handling of the exception. + //! + //! \param[in] process The handle to the process which sustained the + //! exception. + //! \param[in] thread The handle to the thread of \a process which sustained + //! the exception. + //! \return `true` on success, or `false` with an error logged. + bool HandleExceptionHandles(zx_handle_t process, zx_handle_t thread); private: CrashReportDatabase* database_; // weak diff --git a/handler/fuchsia/exception_handler_server.cc b/handler/fuchsia/exception_handler_server.cc index 550a04af..80720d43 100644 --- a/handler/fuchsia/exception_handler_server.cc +++ b/handler/fuchsia/exception_handler_server.cc @@ -49,8 +49,8 @@ void ExceptionHandlerServer::Run(CrashReportExceptionHandler* handler) { continue; } - bool result = handler->HandleException( - packet.type, packet.exception.pid, packet.exception.tid); + bool result = + handler->HandleException(packet.exception.pid, packet.exception.tid); if (!result) { LOG(ERROR) << "HandleException failed"; } From d78bd067c7f3845b966c7a5694d585db6746f309 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 15 May 2018 11:04:20 -0700 Subject: [PATCH 283/326] linux: Initialize a crashing thread's stack from its exception context Bug: crashpad:30 Change-Id: I6b9927d391006f02a3515dbebe954cffaad00598 Reviewed-on: https://chromium-review.googlesource.com/1060031 Commit-Queue: Joshua Peraza Reviewed-by: Scott Graham --- snapshot/cpu_context.cc | 16 ++++++++++++ snapshot/cpu_context.h | 7 ++++++ snapshot/linux/process_reader_linux.cc | 7 ++++++ snapshot/linux/process_reader_linux.h | 14 +++++++++++ snapshot/linux/process_snapshot_linux.cc | 31 +++++++++++++++++++++++- 5 files changed, 74 insertions(+), 1 deletion(-) diff --git a/snapshot/cpu_context.cc b/snapshot/cpu_context.cc index 8d83e638..6ce059e7 100644 --- a/snapshot/cpu_context.cc +++ b/snapshot/cpu_context.cc @@ -174,4 +174,20 @@ uint64_t CPUContext::InstructionPointer() const { } } +uint64_t CPUContext::StackPointer() const { + switch (architecture) { + case kCPUArchitectureX86: + return x86->esp; + case kCPUArchitectureX86_64: + return x86_64->rsp; + case kCPUArchitectureARM: + return arm->sp; + case kCPUArchitectureARM64: + return arm64->sp; + default: + NOTREACHED(); + return ~0ull; + } +} + } // namespace crashpad diff --git a/snapshot/cpu_context.h b/snapshot/cpu_context.h index 3160b0eb..9cb2aec9 100644 --- a/snapshot/cpu_context.h +++ b/snapshot/cpu_context.h @@ -316,6 +316,13 @@ struct CPUContext { //! context structure. uint64_t InstructionPointer() const; + //! \brief Returns the stack pointer value from the context structure. + //! + //! This is a CPU architecture-independent method that is capable of + //! recovering the stack pointer from any supported CPU architecture’s + //! context structure. + uint64_t StackPointer() const; + //! \brief The CPU architecture of a context structure. This field controls //! the expression of the union. CPUArchitecture architecture; diff --git a/snapshot/linux/process_reader_linux.cc b/snapshot/linux/process_reader_linux.cc index 8e25e77d..6f3c9b22 100644 --- a/snapshot/linux/process_reader_linux.cc +++ b/snapshot/linux/process_reader_linux.cc @@ -101,7 +101,12 @@ void ProcessReaderLinux::Thread::InitializeStack(ProcessReaderLinux* reader) { #else #error Port. #endif + InitializeStackFromSP(reader, stack_pointer); +} +void ProcessReaderLinux::Thread::InitializeStackFromSP( + ProcessReaderLinux* reader, + LinuxVMAddress stack_pointer) { const MemoryMap* memory_map = reader->GetMemoryMap(); // If we can't find the mapping, it's probably a bad stack pointer @@ -267,6 +272,7 @@ const std::vector& ProcessReaderLinux::Modules() { void ProcessReaderLinux::InitializeThreads() { DCHECK(threads_.empty()); + initialized_threads_ = true; pid_t pid = ProcessID(); if (pid == getpid()) { @@ -324,6 +330,7 @@ void ProcessReaderLinux::InitializeThreads() { void ProcessReaderLinux::InitializeModules() { INITIALIZATION_STATE_DCHECK_VALID(initialized_); + initialized_modules_ = true; AuxiliaryVector aux; if (!aux.Initialize(connection_)) { diff --git a/snapshot/linux/process_reader_linux.h b/snapshot/linux/process_reader_linux.h index 27e2cea8..7c17d9d1 100644 --- a/snapshot/linux/process_reader_linux.h +++ b/snapshot/linux/process_reader_linux.h @@ -44,6 +44,20 @@ class ProcessReaderLinux { Thread(); ~Thread(); + //! \brief Initializes the thread's stack using \a stack_pointer instead of + //! the stack pointer in \a thread_info. + //! + //! This method initializes \a stack_region_address and \a stack_region_size + //! overwriting any values they previously contained. This is useful, for + //! example, if the thread is currently in a signal handler context, which + //! may execute on a different stack than was used before the signal was + //! received. + //! + //! \param[in] reader A process reader for the target process. + //! \param[in] stack_pointer The stack pointer for the stack to initialize. + void InitializeStackFromSP(ProcessReaderLinux* reader, + LinuxVMAddress stack_pointer); + ThreadInfo thread_info; LinuxVMAddress stack_region_address; LinuxVMSize stack_region_size; diff --git a/snapshot/linux/process_snapshot_linux.cc b/snapshot/linux/process_snapshot_linux.cc index fbae9648..190e8691 100644 --- a/snapshot/linux/process_snapshot_linux.cc +++ b/snapshot/linux/process_snapshot_linux.cc @@ -69,7 +69,36 @@ bool ProcessSnapshotLinux::InitializeException( return false; } - return true; + // The thread's existing snapshot will have captured the stack for the signal + // handler. Replace it with a thread snapshot which captures the stack for the + // exception context. + for (const auto& reader_thread : process_reader_.Threads()) { + if (reader_thread.tid == info.thread_id) { + ProcessReaderLinux::Thread thread = reader_thread; + thread.InitializeStackFromSP(&process_reader_, + exception_->Context()->StackPointer()); + + auto exc_thread_snapshot = + std::make_unique(); + if (!exc_thread_snapshot->Initialize(&process_reader_, thread)) { + return false; + } + + for (auto& thread_snapshot : threads_) { + if (thread_snapshot->ThreadID() == + static_cast(info.thread_id)) { + thread_snapshot.reset(exc_thread_snapshot.release()); + return true; + } + } + + LOG(ERROR) << "thread not found " << info.thread_id; + return false; + } + } + + LOG(ERROR) << "thread not found " << info.thread_id; + return false; } void ProcessSnapshotLinux::GetCrashpadOptions( From 75b672be06c5127f92aab89a2b4422d9ae6f9948 Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Tue, 15 May 2018 11:34:26 -0700 Subject: [PATCH 284/326] Fix arm build with glibc 2.27 This is a followup to [1] which fixed the x86 build for glibc 2.27. But there was a similar block of code that was only present on arm, which was causing the arm build to fail. This CL makes the equivalent change to [1]. [1] https://chromium.googlesource.com/crashpad/crashpad/+/3a20d34ac356ba4dbfffff5c77433695cecb16e3 BUG=chromium:843240 R=mark@chromium.org Change-Id: I4b2786ddcdd0d9920aca25b3ad00c5ba716ca30a Reviewed-on: https://chromium-review.googlesource.com/1060155 Commit-Queue: Scott Graham Reviewed-by: Scott Graham --- compat/linux/sys/ptrace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compat/linux/sys/ptrace.h b/compat/linux/sys/ptrace.h index 73861576..e5c95c7c 100644 --- a/compat/linux/sys/ptrace.h +++ b/compat/linux/sys/ptrace.h @@ -34,7 +34,7 @@ static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = #endif // !PTRACE_GET_THREAD_AREA && !PT_GET_THREAD_AREA && defined(__GLIBC__) // https://sourceware.org/bugzilla/show_bug.cgi?id=22433 -#if !defined(PTRACE_GETVFPREGS) && \ +#if !defined(PTRACE_GETVFPREGS) && !defined(PT_GETVFPREGS) && \ defined(__GLIBC__) && (defined(__arm__) || defined(__arm64__)) static constexpr __ptrace_request PTRACE_GETVFPREGS = static_cast<__ptrace_request>(27); From 1f3052c1ccec9d818afae9f806ca88e2788c7b16 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 18 May 2018 15:53:54 -0700 Subject: [PATCH 285/326] Add support for attachments to generic crash report database This is the beginning of support for attachments at the process level being stored alongside a report. Attachments will be uploaded by key as part of the multipart http upload. There's no interface at the client level yet to pass these through. As this is intended for Fuchsia, this is not yet implemented for the Mac/Windows database implementations. Bug: crashpad:196 Change-Id: Ieaf580675164b631d313193f97375710979ba2a9 Reviewed-on: https://chromium-review.googlesource.com/1060419 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- client/crash_report_database.cc | 18 +- client/crash_report_database.h | 36 +++- client/crash_report_database_generic.cc | 183 +++++++++++++++++- client/crash_report_database_mac.mm | 13 +- client/crash_report_database_test.cc | 96 +++++++++ client/crash_report_database_win.cc | 13 +- handler/crash_report_upload_thread.cc | 5 + .../fuchsia/crash_report_exception_handler.cc | 20 ++ .../fuchsia/crash_report_exception_handler.h | 7 + handler/handler_main.cc | 4 + 10 files changed, 380 insertions(+), 15 deletions(-) diff --git a/client/crash_report_database.cc b/client/crash_report_database.cc index afd751d8..aa365beb 100644 --- a/client/crash_report_database.cc +++ b/client/crash_report_database.cc @@ -14,6 +14,7 @@ #include "client/crash_report_database.h" +#include "base/logging.h" #include "build/build_config.h" namespace crashpad { @@ -29,13 +30,21 @@ CrashReportDatabase::Report::Report() upload_explicitly_requested(false) {} CrashReportDatabase::NewReport::NewReport() - : writer_(std::make_unique()), uuid_(), file_remover_() {} + : writer_(std::make_unique()), + file_remover_(), + attachment_writers_(), + attachment_removers_(), + uuid_(), + database_() {} CrashReportDatabase::NewReport::~NewReport() = default; bool CrashReportDatabase::NewReport::Initialize( + CrashReportDatabase* database, const base::FilePath& directory, const base::FilePath::StringType& extension) { + database_ = database; + if (!uuid_.InitializeWithNew()) { return false; } @@ -56,7 +65,11 @@ bool CrashReportDatabase::NewReport::Initialize( } CrashReportDatabase::UploadReport::UploadReport() - : Report(), reader_(std::make_unique()), database_(nullptr) {} + : Report(), + reader_(std::make_unique()), + database_(nullptr), + attachment_readers_(), + attachment_map_() {} CrashReportDatabase::UploadReport::~UploadReport() { if (database_) { @@ -67,6 +80,7 @@ CrashReportDatabase::UploadReport::~UploadReport() { bool CrashReportDatabase::UploadReport::Initialize(const base::FilePath path, CrashReportDatabase* db) { database_ = db; + InitializeAttachments(); return reader_->Open(path); } diff --git a/client/crash_report_database.h b/client/crash_report_database.h index efa7a770..9ceeddc3 100644 --- a/client/crash_report_database.h +++ b/client/crash_report_database.h @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -112,19 +113,35 @@ class CrashReportDatabase { //! A unique identifier by which this report will always be known to the //! database. - const UUID& ReportID() { return uuid_; } + const UUID& ReportID() const { return uuid_; } + + //! \brief Adds an attachment to the report. + //! + //! \note This function is not yet implemented on macOS or Windows. + //! + //! \param[in] name The key and name for the attachment, which will be + //! included in the http upload. The attachment will not appear in the + //! minidump report. \a name should only use characters from the set + //! `[a-zA-Z0-9._-]`. + //! \return A FileWriter that the caller should use to write the contents of + //! the attachment, or `nullptr` on failure with an error logged. + FileWriter* AddAttachment(const std::string& name); private: friend class CrashReportDatabaseGeneric; friend class CrashReportDatabaseMac; friend class CrashReportDatabaseWin; - bool Initialize(const base::FilePath& directory, + bool Initialize(CrashReportDatabase* database, + const base::FilePath& directory, const base::FilePath::StringType& extension); std::unique_ptr writer_; - UUID uuid_; ScopedRemoveFile file_remover_; + std::vector> attachment_writers_; + std::vector attachment_removers_; + UUID uuid_; + CrashReportDatabase* database_; DISALLOW_COPY_AND_ASSIGN(NewReport); }; @@ -137,9 +154,17 @@ class CrashReportDatabase { UploadReport(); virtual ~UploadReport(); - // An open FileReader with which to read the report. + //! \brief An open FileReader with which to read the report. FileReader* Reader() const { return reader_.get(); } + //! \brief Obtains a mapping of names to file readers for any attachments + //! for the report. + //! + //! This is not implemented on macOS or Windows. + std::map GetAttachments() const { + return attachment_map_; + }; + private: friend class CrashReportDatabase; friend class CrashReportDatabaseGeneric; @@ -147,9 +172,12 @@ class CrashReportDatabase { friend class CrashReportDatabaseWin; bool Initialize(const base::FilePath path, CrashReportDatabase* database); + void InitializeAttachments(); std::unique_ptr reader_; CrashReportDatabase* database_; + std::vector> attachment_readers_; + std::map attachment_map_; DISALLOW_COPY_AND_ASSIGN(UploadReport); }; diff --git a/client/crash_report_database_generic.cc b/client/crash_report_database_generic.cc index e10c24fd..6dc8e9e6 100644 --- a/client/crash_report_database_generic.cc +++ b/client/crash_report_database_generic.cc @@ -36,6 +36,20 @@ base::FilePath ReplaceFinalExtension( return base::FilePath(path.RemoveFinalExtension().value() + extension); } +UUID UUIDFromReportPath(const base::FilePath& path) { + UUID uuid; + uuid.InitializeFromString(path.RemoveFinalExtension().BaseName().value()); + return uuid; +} + +bool AttachmentNameIsOK(const std::string& name) { + for (const char c : name) { + if (c != '_' && c != '-' && c != '.' && !isalnum(c)) + return false; + } + return true; +} + using OperationStatus = CrashReportDatabase::OperationStatus; constexpr base::FilePath::CharType kSettings[] = @@ -53,6 +67,8 @@ constexpr base::FilePath::CharType kPendingDirectory[] = FILE_PATH_LITERAL("pending"); constexpr base::FilePath::CharType kCompletedDirectory[] = FILE_PATH_LITERAL("completed"); +constexpr base::FilePath::CharType kAttachmentsDirectory[] = + FILE_PATH_LITERAL("attachments"); constexpr const base::FilePath::CharType* kReportDirectories[] = { kNewDirectory, @@ -171,6 +187,9 @@ class CrashReportDatabaseGeneric : public CrashReportDatabase { OperationStatus RequestUpload(const UUID& uuid) override; int CleanDatabase(time_t lockfile_ttl) override; + // Build a filepath for the directory for the report to hold attachments. + base::FilePath AttachmentsPath(const UUID& uuid); + private: struct LockfileUploadReport : public UploadReport { ScopedLockFile lock_file; @@ -225,11 +244,18 @@ class CrashReportDatabaseGeneric : public CrashReportDatabase { // Cleans lone metadata, reports, or expired locks in a particular state. int CleanReportsInState(ReportState state, time_t lockfile_ttl); + // Cleans any attachments that have no associated report in any state. + void CleanOrphanedAttachments(); + + // Attempt to remove any attachments associated with the given report UUID. + // There may not be any, so failing is not an error. + void RemoveAttachmentsByUUID(const UUID& uuid); + // Reads the metadata for a report from path and returns it in report. static bool ReadMetadata(const base::FilePath& path, Report* report); // Wraps ReadMetadata and removes the report from the database on failure. - static bool CleaningReadMetadata(const base::FilePath& path, Report* report); + bool CleaningReadMetadata(const base::FilePath& path, Report* report); // Writes metadata for a new report to the filesystem at path. static bool WriteNewMetadata(const base::FilePath& path); @@ -244,6 +270,59 @@ class CrashReportDatabaseGeneric : public CrashReportDatabase { DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseGeneric); }; +FileWriter* CrashReportDatabase::NewReport::AddAttachment( + const std::string& name) { + if (!AttachmentNameIsOK(name)) { + LOG(ERROR) << "invalid name for attachment " << name; + return nullptr; + } + + base::FilePath attachments_dir = + static_cast(database_)->AttachmentsPath( + uuid_); + if (!LoggingCreateDirectory( + attachments_dir, FilePermissions::kOwnerOnly, true)) { + return nullptr; + } + + base::FilePath path = attachments_dir.Append(name); + + auto writer = std::make_unique(); + if (!writer->Open( + path, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)) { + LOG(ERROR) << "could not open " << path.value(); + return nullptr; + } + attachment_writers_.emplace_back(std::move(writer)); + attachment_removers_.emplace_back(ScopedRemoveFile(path)); + return attachment_writers_.back().get(); +} + +void CrashReportDatabase::UploadReport::InitializeAttachments() { + base::FilePath attachments_dir = + static_cast(database_)->AttachmentsPath( + uuid); + DirectoryReader reader; + if (!reader.Open(attachments_dir)) { + return; + } + + base::FilePath filename; + DirectoryReader::Result dir_result; + while ((dir_result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath filepath(attachments_dir.Append(filename)); + std::unique_ptr reader(std::make_unique()); + if (!reader->Open(filepath)) { + LOG(ERROR) << "attachment " << filepath.value() + << " couldn't be opened, skipping"; + continue; + } + attachment_readers_.emplace_back(std::move(reader)); + attachment_map_[filename.value()] = attachment_readers_.back().get(); + } +} + CrashReportDatabaseGeneric::CrashReportDatabaseGeneric() = default; CrashReportDatabaseGeneric::~CrashReportDatabaseGeneric() = default; @@ -260,13 +339,18 @@ bool CrashReportDatabaseGeneric::Initialize(const base::FilePath& path, } for (const base::FilePath::CharType* subdir : kReportDirectories) { - if (!LoggingCreateDirectory(base_dir_.Append(subdir), - FilePermissions::kOwnerOnly, - true)) { + if (!LoggingCreateDirectory( + base_dir_.Append(subdir), FilePermissions::kOwnerOnly, true)) { return false; } } + if (!LoggingCreateDirectory(base_dir_.Append(kAttachmentsDirectory), + FilePermissions::kOwnerOnly, + true)) { + return false; + } + if (!settings_.Initialize(base_dir_.Append(kSettings))) { return false; } @@ -299,8 +383,8 @@ OperationStatus CrashReportDatabaseGeneric::PrepareNewCrashReport( INITIALIZATION_STATE_DCHECK_VALID(initialized_); auto new_report = std::make_unique(); - if (!new_report->Initialize(base_dir_.Append(kNewDirectory), - kCrashReportExtension)) { + if (!new_report->Initialize( + this, base_dir_.Append(kNewDirectory), kCrashReportExtension)) { return kFileSystemError; } @@ -332,6 +416,14 @@ OperationStatus CrashReportDatabaseGeneric::FinishedWritingCrashReport( // We've moved the report to pending, so it no longer needs to be removed. ignore_result(report->file_remover_.release()); + // Close all the attachments and disarm their removers too. + for (auto& writer : report->attachment_writers_) { + writer->Close(); + } + for (auto& remover : report->attachment_removers_) { + ignore_result(remover.release()); + } + *uuid = report->ReportID(); Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated); @@ -440,6 +532,8 @@ OperationStatus CrashReportDatabaseGeneric::DeleteReport(const UUID& uuid) { return kDatabaseError; } + RemoveAttachmentsByUUID(uuid); + return kNoError; } @@ -505,6 +599,7 @@ int CrashReportDatabaseGeneric::CleanDatabase(time_t lockfile_ttl) { removed += CleanReportsInState(kPending, lockfile_ttl); removed += CleanReportsInState(kCompleted, lockfile_ttl); + CleanOrphanedAttachments(); return removed; } @@ -569,6 +664,16 @@ base::FilePath CrashReportDatabaseGeneric::ReportPath(const UUID& uuid, .Append(uuid_string + kCrashReportExtension); } +base::FilePath CrashReportDatabaseGeneric::AttachmentsPath(const UUID& uuid) { +#if defined(OS_WIN) + const std::wstring uuid_string = uuid.ToString16(); +#else + const std::string uuid_string = uuid.ToString(); +#endif + + return base_dir_.Append(kAttachmentsDirectory).Append(uuid_string); +} + OperationStatus CrashReportDatabaseGeneric::LocateAndLockReport( const UUID& uuid, ReportState desired_state, @@ -688,6 +793,7 @@ int CrashReportDatabaseGeneric::CleanReportsInState(ReportState state, if (report_lock.ResetAcquire(filepath) && !IsRegularFile(metadata_path) && LoggingRemoveFile(filepath)) { ++removed; + RemoveAttachmentsByUUID(UUIDFromReportPath(filepath)); } continue; } @@ -700,6 +806,7 @@ int CrashReportDatabaseGeneric::CleanReportsInState(ReportState state, if (report_lock.ResetAcquire(report_path) && !IsRegularFile(report_path) && LoggingRemoveFile(filepath)) { ++removed; + RemoveAttachmentsByUUID(UUIDFromReportPath(filepath)); } continue; } @@ -717,6 +824,7 @@ int CrashReportDatabaseGeneric::CleanReportsInState(ReportState state, if (LoggingRemoveFile(filepath)) { ++removed; + RemoveAttachmentsByUUID(UUIDFromReportPath(filepath)); } continue; } @@ -725,6 +833,67 @@ int CrashReportDatabaseGeneric::CleanReportsInState(ReportState state, return removed; } +void CrashReportDatabaseGeneric::CleanOrphanedAttachments() { + base::FilePath root_attachments_dir(base_dir_.Append(kAttachmentsDirectory)); + DirectoryReader reader; + if (!reader.Open(root_attachments_dir)) { + LOG(ERROR) << "no attachments dir"; + return; + } + + base::FilePath filename; + DirectoryReader::Result result; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath path(root_attachments_dir.Append(filename)); + if (IsDirectory(path, false)) { + UUID uuid; + if (!uuid.InitializeFromString(filename.value())) { + LOG(ERROR) << "unexpected attachment dir name " << filename.value(); + continue; + } + + // Check to see if the report is being created in "new". + base::FilePath new_dir_path = + base_dir_.Append(kNewDirectory) + .Append(uuid.ToString() + kCrashReportExtension); + if (IsRegularFile(new_dir_path)) { + continue; + } + + // Check to see if the report is in "pending" or "completed". + ScopedLockFile local_lock; + base::FilePath local_path; + OperationStatus os = + LocateAndLockReport(uuid, kSearchable, &local_path, &local_lock); + if (os != kReportNotFound) { + continue; + } + + // Couldn't find a report, assume these attachments are orphaned. + RemoveAttachmentsByUUID(uuid); + } + } +} + +void CrashReportDatabaseGeneric::RemoveAttachmentsByUUID(const UUID& uuid) { + base::FilePath attachments_dir = AttachmentsPath(uuid); + DirectoryReader reader; + if (!reader.Open(attachments_dir)) { + return; + } + + base::FilePath filename; + DirectoryReader::Result result; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath filepath(attachments_dir.Append(filename)); + LoggingRemoveFile(filepath); + } + + LoggingRemoveDirectory(attachments_dir); +} + // static bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path, Report* report) { @@ -766,7 +935,6 @@ bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path, return true; } -// static bool CrashReportDatabaseGeneric::CleaningReadMetadata( const base::FilePath& path, Report* report) { @@ -776,6 +944,7 @@ bool CrashReportDatabaseGeneric::CleaningReadMetadata( LoggingRemoveFile(path); LoggingRemoveFile(ReplaceFinalExtension(path, kMetadataExtension)); + RemoveAttachmentsByUUID(report->uuid); return false; } diff --git a/client/crash_report_database_mac.mm b/client/crash_report_database_mac.mm index d0197fce..79b876d2 100644 --- a/client/crash_report_database_mac.mm +++ b/client/crash_report_database_mac.mm @@ -245,6 +245,16 @@ class CrashReportDatabaseMac : public CrashReportDatabase { DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseMac); }; +FileWriter* CrashReportDatabase::NewReport::AddAttachment( + const std::string& name) { + // Attachments aren't implemented in the Mac database yet. + return nullptr; +} + +void CrashReportDatabase::UploadReport::InitializeAttachments() { + // Attachments aren't implemented in the Mac database yet. +} + CrashReportDatabaseMac::CrashReportDatabaseMac(const base::FilePath& path) : CrashReportDatabase(), base_dir_(path), @@ -311,7 +321,8 @@ CrashReportDatabaseMac::PrepareNewCrashReport( INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::unique_ptr report(new NewReport()); - if (!report->Initialize(base_dir_.Append(kWriteDirectory), + if (!report->Initialize(this, + base_dir_.Append(kWriteDirectory), std::string(".") + kCrashReportFileExtension)) { return kFileSystemError; } diff --git a/client/crash_report_database_test.cc b/client/crash_report_database_test.cc index 55bcf3cd..2dbb4fc1 100644 --- a/client/crash_report_database_test.cc +++ b/client/crash_report_database_test.cc @@ -20,6 +20,7 @@ #include "test/errors.h" #include "test/file.h" #include "test/filesystem.h" +#include "test/gtest_disabled.h" #include "test/scoped_temp_dir.h" #include "util/file/file_io.h" #include "util/file/filesystem.h" @@ -669,6 +670,101 @@ TEST_F(CrashReportDatabaseTest, RequestUpload) { CrashReportDatabase::kCannotRequestUpload); } +TEST_F(CrashReportDatabaseTest, Attachments) { +#if defined(OS_MACOSX) || defined(OS_WIN) + // Attachments aren't supported on Mac and Windows yet. + DISABLED_TEST(); +#else + std::unique_ptr new_report; + ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), + CrashReportDatabase::kNoError); + + FileWriter* attach_some_file = new_report->AddAttachment("some_file"); + ASSERT_NE(attach_some_file, nullptr); + static constexpr char test_data[] = "test data"; + attach_some_file->Write(test_data, sizeof(test_data)); + + FileWriter* failed_attach = new_report->AddAttachment("not/a valid fi!e"); + EXPECT_EQ(failed_attach, nullptr); + + UUID expect_uuid = new_report->ReportID(); + UUID uuid; + ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), + CrashReportDatabase::kNoError); + EXPECT_EQ(uuid, expect_uuid); + + CrashReportDatabase::Report report; + EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kNoError); + ExpectPreparedCrashReport(report); + + std::vector reports; + EXPECT_EQ(db()->GetPendingReports(&reports), CrashReportDatabase::kNoError); + ASSERT_EQ(reports.size(), 1u); + EXPECT_EQ(reports[0].uuid, report.uuid); + + std::unique_ptr upload_report; + ASSERT_EQ(db()->GetReportForUploading(reports[0].uuid, &upload_report), + CrashReportDatabase::kNoError); + std::map result_attachments = + upload_report->GetAttachments(); + EXPECT_EQ(result_attachments.size(), 1u); + EXPECT_NE(result_attachments.find("some_file"), result_attachments.end()); + char result_buffer[sizeof(test_data)]; + result_attachments["some_file"]->Read(result_buffer, sizeof(result_buffer)); + EXPECT_EQ(memcmp(test_data, result_buffer, sizeof(test_data)), 0); +#endif +} + +TEST_F(CrashReportDatabaseTest, OrphanedAttachments) { +#if defined(OS_MACOSX) || defined(OS_WIN) + // Attachments aren't supported on Mac and Windows yet. + DISABLED_TEST(); +#else + // TODO: This is using paths that are specific to the generic implementation + // and will need to be generalized for Mac and Windows. + std::unique_ptr new_report; + ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), + CrashReportDatabase::kNoError); + + FileWriter* file1 = new_report->AddAttachment("file1"); + ASSERT_NE(file1, nullptr); + FileWriter* file2 = new_report->AddAttachment("file2"); + ASSERT_NE(file2, nullptr); + + UUID expect_uuid = new_report->ReportID(); + UUID uuid; + ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), + CrashReportDatabase::kNoError); + EXPECT_EQ(uuid, expect_uuid); + + CrashReportDatabase::Report report; + ASSERT_EQ(db()->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kNoError); + + ASSERT_TRUE(LoggingRemoveFile(report.file_path)); + + ASSERT_TRUE(LoggingRemoveFile(base::FilePath( + report.file_path.RemoveFinalExtension().value() + ".meta"))); + + ASSERT_EQ(db()->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kReportNotFound); + + base::FilePath report_attachments_dir( + path().Append("attachments").Append(uuid.ToString())); + base::FilePath file_path1(report_attachments_dir.Append("file1")); + base::FilePath file_path2(report_attachments_dir.Append("file2")); + EXPECT_TRUE(FileExists(file_path1)); + EXPECT_TRUE(FileExists(file_path1)); + + EXPECT_EQ(db()->CleanDatabase(0), 0); + + EXPECT_FALSE(FileExists(file_path1)); + EXPECT_FALSE(FileExists(file_path2)); + EXPECT_FALSE(FileExists(report_attachments_dir)); +#endif +} + // This test uses knowledge of the database format to break it, so it only // applies to the unfified database implementation. #if !defined(OS_MACOSX) && !defined(OS_WIN) diff --git a/client/crash_report_database_win.cc b/client/crash_report_database_win.cc index fb3cd8f7..e19e6058 100644 --- a/client/crash_report_database_win.cc +++ b/client/crash_report_database_win.cc @@ -615,6 +615,16 @@ class CrashReportDatabaseWin : public CrashReportDatabase { DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseWin); }; +FileWriter* CrashReportDatabase::NewReport::AddAttachment( + const std::string& name) { + // Attachments aren't implemented in the Windows database yet. + return nullptr; +} + +void CrashReportDatabase::UploadReport::InitializeAttachments() { + // Attachments aren't implemented in the Windows database yet. +} + CrashReportDatabaseWin::CrashReportDatabaseWin(const base::FilePath& path) : CrashReportDatabase(), base_dir_(path), settings_(), initialized_() {} @@ -653,7 +663,8 @@ OperationStatus CrashReportDatabaseWin::PrepareNewCrashReport( INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::unique_ptr new_report(new NewReport()); - if (!new_report->Initialize(base_dir_.Append(kReportsDirectory), + if (!new_report->Initialize(this, + base_dir_.Append(kReportsDirectory), std::wstring(L".") + kCrashReportFileExtension)) { return kFileSystemError; } diff --git a/handler/crash_report_upload_thread.cc b/handler/crash_report_upload_thread.cc index 715c533a..4783ecb2 100644 --- a/handler/crash_report_upload_thread.cc +++ b/handler/crash_report_upload_thread.cc @@ -284,6 +284,11 @@ CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport( } } + for (const auto& it : report->GetAttachments()) { + http_multipart_builder.SetFileAttachment( + it.first, it.first, it.second, "application/octet-stream"); + } + http_multipart_builder.SetFileAttachment(kMinidumpKey, report->uuid.ToString() + ".dmp", reader, diff --git a/handler/fuchsia/crash_report_exception_handler.cc b/handler/fuchsia/crash_report_exception_handler.cc index 80b34a76..1da09851 100644 --- a/handler/fuchsia/crash_report_exception_handler.cc +++ b/handler/fuchsia/crash_report_exception_handler.cc @@ -52,10 +52,12 @@ CrashReportExceptionHandler::CrashReportExceptionHandler( CrashReportDatabase* database, CrashReportUploadThread* upload_thread, const std::map* process_annotations, + const std::map* process_attachments, const UserStreamDataSources* user_stream_data_sources) : database_(database), upload_thread_(upload_thread), process_annotations_(process_annotations), + process_attachments_(process_attachments), user_stream_data_sources_(user_stream_data_sources) {} CrashReportExceptionHandler::~CrashReportExceptionHandler() {} @@ -144,6 +146,24 @@ bool CrashReportExceptionHandler::HandleExceptionHandles(zx_handle_t process, return false; } + if (process_attachments_) { + // Note that attachments are read at this point each time rather than once + // so that if the contents of the file has changed it will be re-read for + // each upload (e.g. in the case of a log file). + for (const auto& it : *process_attachments_) { + FileWriter* writer = new_report->AddAttachment(it.first); + if (writer) { + std::string contents; + if (!LoggingReadEntireFile(it.second, &contents)) { + // Not being able to read the file isn't considered fatal, and + // should not prevent the report from being processed. + continue; + } + writer->Write(contents.data(), contents.size()); + } + } + } + UUID uuid; database_status = database_->FinishedWritingCrashReport(std::move(new_report), &uuid); diff --git a/handler/fuchsia/crash_report_exception_handler.h b/handler/fuchsia/crash_report_exception_handler.h index 9f140512..5043d7e9 100644 --- a/handler/fuchsia/crash_report_exception_handler.h +++ b/handler/fuchsia/crash_report_exception_handler.h @@ -21,6 +21,7 @@ #include #include +#include "base/files/file_path.h" #include "base/macros.h" #include "client/crash_report_database.h" #include "handler/crash_report_upload_thread.h" @@ -47,6 +48,10 @@ class CrashReportExceptionHandler { //! To interoperate with Breakpad servers, the recommended practice is to //! specify values for the `"prod"` and `"ver"` keys as process //! annotations. + //! \param[in] process_attachments A map of file name keys to file paths to be + //! included in the report. Each time a report is written, the file paths + //! will be read in their entirety and included in the report using the + //! file name key as the name in the http upload. //! \param[in] user_stream_data_sources Data sources to be used to extend //! crash reports. For each crash report that is written, the data sources //! are called in turn. These data sources may contribute additional @@ -55,6 +60,7 @@ class CrashReportExceptionHandler { CrashReportDatabase* database, CrashReportUploadThread* upload_thread, const std::map* process_annotations, + const std::map* process_attachments, const UserStreamDataSources* user_stream_data_sources); ~CrashReportExceptionHandler(); @@ -88,6 +94,7 @@ class CrashReportExceptionHandler { CrashReportDatabase* database_; // weak CrashReportUploadThread* upload_thread_; // weak const std::map* process_annotations_; // weak + const std::map* process_attachments_; // weak const UserStreamDataSources* user_stream_data_sources_; // weak DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler); diff --git a/handler/handler_main.cc b/handler/handler_main.cc index 70192cf4..901ee221 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -836,6 +836,10 @@ int HandlerMain(int argc, database.get(), static_cast(upload_thread.Get()), &options.annotations, +#if defined(OS_FUCHSIA) + // TODO(scottmg): Process level file attachments, and for all platforms. + nullptr, +#endif user_stream_sources); #if defined(OS_LINUX) || defined(OS_ANDROID) From f5564e6292b1b1c53b69d38c917dfc66e3d9e1f9 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 22 May 2018 13:43:45 -0700 Subject: [PATCH 286/326] Roll gtest (and mini_chromium) gtest started depending on zircon and launchpad directly for death tests, so tweak the build files. Logs for upstreams below: mini_chromium$ git log --oneline --no-merges --no-decorate 40cb6722bbc6bd6fb5fccecd80362313440a0537..53a3ff4cf81adb25fdf764e8547a8515fc8f70c2 53a3ff4 fuchsia: Add fdio to system includes 23b3647 fuchsia: Update sysroot.gni for sysroot location moving in SDK gtest$ git log --oneline --no-merges --no-decorate d175c8bf823e709d570772b038757fadf63bc632..145d05750b15324899473340c8dd5af50d125d33 49ecebd Downgrade to C++98. ec2c911 Downgrade to C++98 code. f91bf75 Remove unused variable in Fuchsia. 54e331b Add support for versioned standard libraries. fc66ae4 Update generated code. b8fa4d2 Add unit test for CanonicalizeForStdLibVersioning. 18abd8f Use NULL instead of nullptr, for pre-C++11 builds. ec5ad0f Fix the bug where ad_hoc_test_result() functions of UnitTest and TestCase objects would return failures registered at TestCase and UnitTest scopes, respectively. 960149f Remove magic number 242f0f6 Style fix 8497540 Fix comments 13af91f Fix more stuff and get tests to pass 96c3c42 Get all the things to work. a9653c4 Fix gmock not building when -fno-rtti b6cb5c3 Fix stuff d4b5281 Add Fuchsia support for death test. 95ec42d Add no-exception and no-rtti to build matrix b539167 merging, bb7a018 reverting, test 7b4ee66 reverting just to test 62a7c14 testing 2d3024f Fix friend declaration to use GTEST_API_ decl spec. b2f97ab Revert useless use of GTEST_API_ on WithoutMatchers decl. 1c79ad7 Add GTEST_API_ tag to WithoutMatchers class. Hopefully that fixes the problem on MSVC? d5725da Mark new GetWithoutMatchers method as part of the exported API, to address MSVC linker errors. f6551f2 Don't use generalized initializer list; is C++11 extension. f437f8c Clone of unsubmitted cr/176529515. Introduce parameterless expectations. f312435 more typos a0fd742 msvc b00e281 more typos 881ee30 typo 10e8ec2 move only types docs 4d554c3 typo b4cbf53 typo 78d7381 http://cl/193386206 c56ba73 merge, explicit, ( should be it) 4707c0f 193353312 80d6e26 cl/193060888 5dccf6b http://cl/193060888 dff32af http://cl/193060888 3f88bb1 test-meerging ec425d7 typo e4ab316 more msvc 1944bc0 typo e9eff48 msvc warnings bd2a1ae merging gmock generated matchers 9fba103 merging, testing, this should be it 1c6e68c merging 26c10dc merging d84eb86 more pizza f45728a more OSX pizzas b74a1af osx pizzas f9bd618 merging gmock actions test 0bfa823 merging, gmock actions test 2dc576e merging f7330f9 more fixing osx libstd++ bugs 6538049 fixing dc4f563 merging, fix OSX issue 092ca91 merging a79851f merging fa658e0 merging c67f51b msvc 9b5940e revert this one b2d81b4 merge, ... gmock-matchers test e77deb2 small cleanup 1324e2d Remove multiple inheritance from "unintesting call" mock classes. 373481c ...merging 8654c1c merging ab84d14 Upstream cl/192179348 c13ab60 merging 5cd213e ..and this should be it 6a7573a more 039d9b5 pizza work, cont f15fd96 osx pizzas, cont b15be9a fixing osx pizza 6f9db26 merging 9bc82ce merging 25d8176 merging e1071eb RE-Doing the merge, this time with gcc on mac in the PR so I can catch errors before merging the PR 8fbb419 Include gcc on mac into PR matrix ca54b67 Revert "gmock actions 2" 64d24b8 ... and this c1d4c34 this should be it f587100 yet more 05b5a53 formatting 2de24fb tuning 055f321 tuning 57d6e82 more 44da2b9 cont e93a0ec msvc c4684b4 more msvc 431bfdc msvc 14 8bc7c63 testing msvc again c4e3d77 More msvc 14 6525044 And also silence for MSVS14 35a709a preproc syntax ( I can never remember it) 61e8a0b syntax 03be5df cont. e0b3c26 continued 51f8ad4 Sync gmock-generated-nice-strict.h.pump with gmock-generated-nice-strict.h. dbd206e more mcvs fixing 701e1e5 linkage, fixing MSVC 2d4d4ef fixing MSVC 5fe8de5 more warnings cb13dc7 more warnings d9f3611 more MSVC warnings c75b76e warnings 04e3188 cont - 2 32ac949 cont 1831ac9 more warnings eb3e4aa deal with MSVC warn, cont 1 50c0bcd Cont. deal with MCVS warnings b5c87fb Deal with MCVS warnings 88fc7d7 merging gmock-actions 2 fe402c2 Merging gMock, 2 7e5f90d formatting 9286361 And more MCVS warnings e0f4cf0 fixing MCVS warn 7045138 Have to wait for this one 66eaf9f Have to wait for this one af93d59 Merging matchers test 0cd6a4f Merging matchers test d81b6a0 bad cut/paste a608d4a More on MSVC warning C4503, decorated name length exceeded 6f4e939 More on MSVC warning C4503, decorated name length exceeded 5b3d277 Address MSVC warning C4503, decorated name length exceeded, name was truncated aa14cc4 Fixing build break on MSVC e55089e merging gmock matchers 1 a0c27bd fix build break on locale windows 1776ed8 Tweaking https://github.com/google/googletest/pull/1523 to exclude nacl d52aaee Upstreaming, cl 191344765 a2dd136 merging port, cont. 191443078 aa349ac merging, cont - 2 2cedd5b merging gtest-port.h , 191439094 04d1e56 merging, just comments format 5beb452 testing, merge df5a48d Testing, gtest-port.h merge 87a4cdd merging gtest-port.h, again - 1 7888184 Include OSX builds back into PR builds b2373c6 Revert "merging gtest-port 1 of N" 54bb165 Revert "merging gtest-port, 2" d04bf07 typo 8e0364a merging gtest-port, 2 11855d8 provide alternative for DebugBreak() aea6fc3 merging gtest-port 1 of N a75a5c9 merges 1 3df7cbe merges, gtest 6aae206 merging gmock-matchers.h 3 2318705 merging gmock-matchers.h 2 8ea10d3 Upstreaming FloatingEq2Matcher, b907c26 Merging gmock-matchers.h -2 466a49a gmock-matchers merging -2 b7c5683 merging, gmock -1 4e89c76 reverting gtest_list_tests_unittest.py eaaa422 Update appveyor.yml da71e8c more merges 0f65679 more merges 691e38e More merges a3c2e10 cl 189032107, again 080fcbe cl 189032107 a178cc7 merge, again, IsRecursiveContainer 262aaf2 erging, cont 2814b4b merging, merging a719320 fixing, was removing too much af463c4 More merges, removing old dead code 7b70413 Allow macros inside of parametrized test names. cf9d634 merges-port(1) e891900 Merging, XML tests 89d6f70 merges-8 995a9df merges-7 086825d merges-6 8385928 merges-3 dbf63e3 merges-2 0d5e01a Merges-1 7a2050d Use a full message in the JSON output for failures 3431b69 Add options to parallelize builds. 6baf17e Support JSON output format in addition to XML 2eb31c1 Add documentation for VariantWith. 2bd1750 gmock merging -2 190e2cd Add matcher for std::variant. 84ec2e0 Switch default death test style back to "fast". 20074be Use DEBUG_POSTFIX instead of CMAKE_DEBUG_POSTFIX 4dbb437 merging unittests - 5 567b40e Try to handle unsigned wchar_t (arm) a bit better 004f6a0 merging unitests - check 4 29e9ca8 merging unitests, check 3299a23 merging unittests - 2 b7e0294 merging unitests 11e1dd2 Removed trailing comma in enum 0656830 TEST() arguments are invalid in an example df65632 merges 8a61587 Fix unused function warning on Mac OS. a3e322b cleanup, merges 09581b3 cleanup/merges 0697241 merging, cleaning up ab186a8 merges 30d276d cxxx11 3b1fe3e clang warnings d7c966c clang warnings 2a23ca0 https://travis-ci.org/google/googletest/jobs/340995238 a66d209 clang warning 'https://travis-ci.org/google/googletest/jobs/340987201' e76f4ee clang warning https://travis-ci.org/google/googletest/jobs/340978022 9e07281 merges 225e674 moving JoinAsTuple to internal b3a1759 Fix std::iscntrl use in gtest-printers.cc d84f58a Merging, coniniue 575c081 merging 49fc378 merges ec7faa9 merges c851050 Fixed typos 092d088 Add ability to throw from ASSERT a3c73ed Include MSVC14 on PRs as well e6ec8bc Merges and also adding new bazel build mode e55fded Code merges 2a46830 Ability to optionally depend on Abseil plus upstream of 183716547 6c0c389 Adding tests to googlemock bazel fbb48a7 Code merges efd49c2 Update Documentation.md b8ac390 Fix test build issue with GCC7.2. 3498a1a Use _CPPUNWIND instead of _HAS_EXCEPTIONS with MSVC. f915530 Pass -EHs-c- to disable exceptions with MSVC. b3a2048 Update README.md 7cced89 Remove Visual Studio 10,11,12 from build matrix b9651c0 placating gcc and its overzeauls size comparison warnings e29805a upstream cl 182543808 06c3cce revert, lets get this compiled 80defce Many code merge/upstream changes 6723b6c Merging, upstream http://cl/182836545 bbb17ad more code merge f1c87ad merges, cl/155419551 and other 9bc8666 more merging a0435a5 merging 8d707df code merge b1623e9 Adding python tests to Bazel build file. 8e86221 Use fully qualified in examples 354fc8d Document ScopedTrace utility ba99a04 Check whether _MSC_VER is defined when detecting presence of cxxabi.h under libc++. 9c82e77 Expose ScopedTrace utility in public interface 08b323f Reverting some changes, need to make the merge compile 9195571 Reverting some changes, need to make the merge compile 6d04de7 Reverting some changes, need to make the merge compile b9e2978 Reverting some changes, need to make the merge compile 304be8f Test files for corresponding changes 6befe42 Test files for corresponding changes f45c22c Test files for corresponding changes 5f4ce9d Test files for corresponding changes d629744 More code merges da1f7fe Code merging 33d73d4 Added support for WINAPI_PARTITION_TV_TITLE which is defined on XboxOne 569d713 Added support for WINAPI_PARTITION_TV_TITLE which is defined on XboxOne 93b7798 continue upstream/merge, etc ed8d02c Update .travis.yml 73d1251 Update .travis.yml cfd29e0 Update .travis.yml 29f94e0 Update .travis.yml 2982dc1 Trying to fix travis 6914ae2 Upstream cl 103120214 1d757db More merge, cleanup 481fe94 More merge, cleanup 6a26e47 Code merge, upstreaming accumulated changes, cleanup 62dbaa2 revert d630a8b code merges, cleanup f33902b revert googletest/test/gtest-param-test_test.cc 6eccdb7 Update .travis.yml d237297 code merge, cleanups 2ad5661 Upstream of cl 129104714 5eecadf Revert one file 9fce984 wip, cleanups/merge 258fd6e cleanup, merge cbd15d4 [Bazel] Detect Windows with cpu value x64_windows and x64_windows_msvc 91ba05c Small cleanups, merge 67d6e46 Use correct name for build event types. 67476c1 Revert one file for now adfdc65 Fixed test for pull request. d3acb4b Fixed output and test for 'enabled_on_pr' 19b5774 code cleanup in preparation for merges, cl 180857299 9cee677 Optimize build matrix (#1) 1c09831 upstreaming cl 124976692 1acf8c7 Also define GTEST_ATTRIBUTE_PRINTF_ in clang-cl. b3d9be5 Pass the -Wmissing-declarations warning. e7734fb OSS Sync, cl 163329677 90244a6 Fix testing::Combine on MSVC 2017. bcd3552 Avoid warning C4619 in MSVC 2017. ec2b0c3 Build both googletest and googlemock. 75b85d5 Create a autotools-based build for Travis. 7990f56 Re-enable MSVC++ C4389 warning in CmdHelperEq() e17907d Update internal_utils.cmake a7fceb4 Update gtest-param-test.h.pump 8a48f0e Update gtest-param-test.h.pump 23a014a Refactor docs about contributions to CONTRIBUTING.md. 74a5306 remove extra line 3ea0631 remove implicit casts c82cd5e Also add documentation around becoming a contributor. 1184117 Wrong LICENSE file, sorry. Corrected. [skip ci] bc3320b Implement bazel-based builds in Travis. e22d344 Add Apache-2.0 LICENSE file. cf3adad Add licenses() directive for googlemock/tests. 57bb0bb Remove C4996 warning in VS2017 3464f76 Improved description of VS std::tuple support 8d9d6bb Improved description of VS std::tuple support 4aa05bd Only switch to g++-4.9 on Linux. aea8580 Run autoconf from top-level directory. 0663ce9 Fix double free when building Gtest/GMock in shared libraries and linking a test executable with both. f46bd00 make includes system a7269e2 replaced back accidently removed static_cast with consistent ImplicitCast_ 82447f2 Fixes issue #826 by treating MinGW as "non-Windows" when determining colored output 1ae4096 fix for VS2017 deprecation of ::tr1::tuple change static_cast to ImplicitCast_ for consitency fixes for building with path names containing spaces aa0b545 remove GTEST_HAS_PARAM_TESTS 4d26df7 Speed up printing of characters which need hex escaping 99d0ef3 Also can build when included in source. 9538687 Remove trailing whitespaces in BUILD.bazel c113a71 Added support for WINAPI_PARTITION_TV_TITLE which is defined on XboxOne ca6a70c Pass MSVC's C4826 warning. Bug: crashpad:196 Change-Id: I60be5750667d1901e0ad47c3558de1338b083536 Reviewed-on: https://chromium-review.googlesource.com/1069562 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- DEPS | 4 ++-- third_party/gtest/BUILD.gn | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index f027c358..1818580a 100644 --- a/DEPS +++ b/DEPS @@ -23,13 +23,13 @@ deps = { '6fe4a3251488f7af86d64fc25cf442e817cf6133', 'crashpad/third_party/gtest/gtest': Var('chromium_git') + '/external/github.com/google/googletest@' + - 'd175c8bf823e709d570772b038757fadf63bc632', + '145d05750b15324899473340c8dd5af50d125d33', 'crashpad/third_party/gyp/gyp': Var('chromium_git') + '/external/gyp@' + '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '40cb6722bbc6bd6fb5fccecd80362313440a0537', + '53a3ff4cf81adb25fdf764e8547a8515fc8f70c2', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/third_party/gtest/BUILD.gn b/third_party/gtest/BUILD.gn index db5f4a56..37fceaf0 100644 --- a/third_party/gtest/BUILD.gn +++ b/third_party/gtest/BUILD.gn @@ -46,6 +46,13 @@ if (crashpad_is_in_chromium) { visibility = [ ":*" ] include_dirs = [ "gtest/googletest" ] defines = [ "GUNIT_NO_GOOGLE3=1" ] + + if (crashpad_is_fuchsia) { + libs = [ + "launchpad", + "zircon", + ] + } } config("gtest_public_config") { @@ -262,6 +269,13 @@ if (crashpad_is_in_chromium) { config("gmock_private_config") { visibility = [ ":*" ] include_dirs = [ "gtest/googlemock" ] + + if (crashpad_is_fuchsia) { + libs = [ + "launchpad", + "zircon", + ] + } } config("gmock_public_config") { From 9839ef5605cf274f98ad6af8390a5077a6437808 Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Wed, 23 May 2018 17:14:05 -0700 Subject: [PATCH 287/326] Clean up Fuchsia package() The libraries key is deprecated. The new loadable_modules key is the right thing to use. The dependencies should not specify the toolchain. Change-Id: I2f218adce96b7161a5a0097f84c7ed6b0c87e3bd Reviewed-on: https://chromium-review.googlesource.com/1071067 Reviewed-by: Scott Graham Commit-Queue: Scott Graham --- BUILD.gn | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 14c4387d..e09f72dd 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -40,10 +40,10 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { deps = [ ":crashpad_tests", - "snapshot:crashpad_snapshot_test_both_dt_hash_styles(//build/toolchain/fuchsia:$current_cpu-shared)", - "snapshot:crashpad_snapshot_test_module(//build/toolchain/fuchsia:$current_cpu-shared)", - "snapshot:crashpad_snapshot_test_module_large(//build/toolchain/fuchsia:$current_cpu-shared)", - "snapshot:crashpad_snapshot_test_module_small(//build/toolchain/fuchsia:$current_cpu-shared)", + "snapshot:crashpad_snapshot_test_both_dt_hash_styles", + "snapshot:crashpad_snapshot_test_module", + "snapshot:crashpad_snapshot_test_module_large", + "snapshot:crashpad_snapshot_test_module_small", "test:crashpad_test_test_multiprocess_exec_test_child", "util:http_transport_test_server", ] @@ -62,7 +62,7 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { }, ] - libraries = [ + loadable_modules = [ { name = "crashpad_snapshot_test_both_dt_hash_styles.so" }, @@ -79,15 +79,15 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { resources = [ { - path = rebase_path("util/net/testdata/ascii_http_body.txt") + path = "util/net/testdata/ascii_http_body.txt" dest = "crashpad_test_data/util/net/testdata/ascii_http_body.txt" }, { - path = rebase_path("util/net/testdata/binary_http_body.dat") + path = "util/net/testdata/binary_http_body.dat" dest = "crashpad_test_data/util/net/testdata/binary_http_body.dat" }, { - path = rebase_path("test/test_paths_test_data_root.txt") + path = "test/test_paths_test_data_root.txt" dest = "crashpad_test_data/test/test_paths_test_data_root.txt" }, ] From c86c9f0f730d4af1fc3d7cc5722cc46f9ac5e582 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 24 May 2018 15:41:55 -0700 Subject: [PATCH 288/326] Don't merge stack mappings with different names This is particularly a problem when the neighboring mapping is a special mapping not readable from another process. For example: 7fff96aeb000-7fff96b0c000 rw-p 00000000 00:00 0 [stack] 7fff96b0c000-7fff96b0e000 r--p 00000000 00:00 0 [vvar] [vvar] is a special mapping which makes some kernel data available for virtual system calls. Attempting to read this region via the /proc//maps file returns an IO error which causes Crashpad to abort capturing any of the thread's stack. Neighboring mappings with empty names are eligible to be merged since they result from changing permissions on existing named mappings. Change-Id: I587bd2ec6f9759d284f1f9b1d93f2a44ddf61e92 Reviewed-on: https://chromium-review.googlesource.com/1072803 Reviewed-by: Scott Graham Commit-Queue: Joshua Peraza --- snapshot/linux/process_reader_linux.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/snapshot/linux/process_reader_linux.cc b/snapshot/linux/process_reader_linux.cc index 6f3c9b22..9a5b092f 100644 --- a/snapshot/linux/process_reader_linux.cc +++ b/snapshot/linux/process_reader_linux.cc @@ -41,7 +41,9 @@ bool ShouldMergeStackMappings(const MemoryMap::Mapping& stack_mapping, const MemoryMap::Mapping& adj_mapping) { DCHECK(stack_mapping.readable); return adj_mapping.readable && stack_mapping.device == adj_mapping.device && - stack_mapping.inode == adj_mapping.inode; + stack_mapping.inode == adj_mapping.inode && + (stack_mapping.name == adj_mapping.name || + stack_mapping.name.empty() || adj_mapping.name.empty()); } } // namespace From 3033802ed4ad78c343b85f3b5b1c22762c66e976 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Fri, 25 May 2018 08:46:11 -0700 Subject: [PATCH 289/326] Remove unused port wait/queue argument Change-Id: Ifcfe6c2d18045ce3a2e443ee84d4dd84bb3db373 Reviewed-on: https://chromium-review.googlesource.com/1073567 Reviewed-by: Scott Graham Commit-Queue: Scott Graham --- handler/fuchsia/exception_handler_server.cc | 2 +- snapshot/fuchsia/process_reader_fuchsia_test.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/handler/fuchsia/exception_handler_server.cc b/handler/fuchsia/exception_handler_server.cc index 80720d43..63153d28 100644 --- a/handler/fuchsia/exception_handler_server.cc +++ b/handler/fuchsia/exception_handler_server.cc @@ -38,7 +38,7 @@ void ExceptionHandlerServer::Run(CrashReportExceptionHandler* handler) { while (true) { zx_port_packet_t packet; zx_status_t status = - zx_port_wait(exception_port_.get(), ZX_TIME_INFINITE, &packet, 1); + zx_port_wait(exception_port_.get(), ZX_TIME_INFINITE, &packet); if (status != ZX_OK) { ZX_LOG(ERROR, status) << "zx_port_wait, aborting"; return; diff --git a/snapshot/fuchsia/process_reader_fuchsia_test.cc b/snapshot/fuchsia/process_reader_fuchsia_test.cc index 329752b8..63b59e83 100644 --- a/snapshot/fuchsia/process_reader_fuchsia_test.cc +++ b/snapshot/fuchsia/process_reader_fuchsia_test.cc @@ -102,7 +102,7 @@ TEST(ProcessReaderFuchsia, ChildBasic) { void* SignalAndSleep(void* arg) { zx_port_packet_t packet = {}; packet.type = ZX_PKT_TYPE_USER; - zx_port_queue(*reinterpret_cast(arg), &packet, 1); + zx_port_queue(*reinterpret_cast(arg), &packet); zx_nanosleep(UINT64_MAX); return nullptr; } @@ -125,7 +125,7 @@ CRASHPAD_CHILD_TEST_MAIN(ProcessReaderChildThreadsTestMain) { // Wait until all threads are ready. for (size_t i = 0; i < kNumThreads; ++i) { zx_port_packet_t packet; - zx_port_wait(port, ZX_TIME_INFINITE, &packet, 1); + zx_port_wait(port, ZX_TIME_INFINITE, &packet); } char c = ' '; From 5191992ae54c5e349130e0e9394349568ee11b8b Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 28 May 2018 12:38:46 -0700 Subject: [PATCH 290/326] win: Fix SimulateCrash.ChildDumpWithoutCrashing under ASAN, disable others SimulateCrash.ChildDumpWithoutCrashing needed a larger threshold due to ASAN instrumentation. These tests expect children to crash, but ASAN captures the exception before letting Crashpad handle it: CrashpadClient.HandlerLaunchFailureCrash CrashpadClient.HandlerLaunchFailureDumpAndCrash CrashpadHandler.ExtensibilityCalloutsWork ExceptionSnapshotWinTest.ChildCrash (which is an upstreaming of https://chromium-review.googlesource.com/1067151). Additionally, because Chrome doesn't build all, I noticed a missing dependency on a test binary which is added here. Bug: chromium:845011 Change-Id: I5c3ae5673512be29edad21e7d20dd57b8b5ce2bf Reviewed-on: https://chromium-review.googlesource.com/1075715 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- client/crashpad_client_win_test.cc | 17 +++++++++++++++-- handler/BUILD.gn | 1 + handler/crashpad_handler_test.cc | 8 +++++++- snapshot/win/exception_snapshot_win_test.cc | 14 +++++++++++++- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/client/crashpad_client_win_test.cc b/client/crashpad_client_win_test.cc index fb47ad72..99778baa 100644 --- a/client/crashpad_client_win_test.cc +++ b/client/crashpad_client_win_test.cc @@ -126,7 +126,13 @@ class HandlerLaunchFailureCrash : public WinMultiprocess { } }; -TEST(CrashpadClient, HandlerLaunchFailureCrash) { +#if defined(ADDRESS_SANITIZER) +// https://crbug.com/845011 +#define MAYBE_HandlerLaunchFailureCrash DISABLED_HandlerLaunchFailureCrash +#else +#define MAYBE_HandlerLaunchFailureCrash HandlerLaunchFailureCrash +#endif +TEST(CrashpadClient, MAYBE_HandlerLaunchFailureCrash) { WinMultiprocess::Run(); } @@ -150,7 +156,14 @@ class HandlerLaunchFailureDumpAndCrash : public WinMultiprocess { } }; -TEST(CrashpadClient, HandlerLaunchFailureDumpAndCrash) { +#if defined(ADDRESS_SANITIZER) +// https://crbug.com/845011 +#define MAYBE_HandlerLaunchFailureDumpAndCrash \ + DISABLED_HandlerLaunchFailureDumpAndCrash +#else +#define MAYBE_HandlerLaunchFailureDumpAndCrash HandlerLaunchFailureDumpAndCrash +#endif +TEST(CrashpadClient, MAYBE_HandlerLaunchFailureDumpAndCrash) { WinMultiprocess::Run(); } diff --git a/handler/BUILD.gn b/handler/BUILD.gn index f94e4e74..7928461b 100644 --- a/handler/BUILD.gn +++ b/handler/BUILD.gn @@ -112,6 +112,7 @@ source_set("handler_test") { if (crashpad_is_win) { data_deps = [ ":crashpad_handler_test_extended_handler", + ":fake_handler_that_crashes_at_startup", ] } } diff --git a/handler/crashpad_handler_test.cc b/handler/crashpad_handler_test.cc index 9c468ddf..afa12014 100644 --- a/handler/crashpad_handler_test.cc +++ b/handler/crashpad_handler_test.cc @@ -133,7 +133,13 @@ void CrashWithExtendedHandler::ValidateGeneratedDump() { EXPECT_EQ(found_extension_streams, 1u); } -TEST(CrashpadHandler, ExtensibilityCalloutsWork) { +#if defined(ADDRESS_SANITIZER) +// https://crbug.com/845011 +#define MAYBE_ExtensibilityCalloutsWork DISABLED_ExtensibilityCalloutsWork +#else +#define MAYBE_ExtensibilityCalloutsWork ExtensibilityCalloutsWork +#endif +TEST(CrashpadHandler, MAYBE_ExtensibilityCalloutsWork) { WinMultiprocessWithTempDir::Run(); } diff --git a/snapshot/win/exception_snapshot_win_test.cc b/snapshot/win/exception_snapshot_win_test.cc index 376de7c9..537b4ca8 100644 --- a/snapshot/win/exception_snapshot_win_test.cc +++ b/snapshot/win/exception_snapshot_win_test.cc @@ -163,7 +163,13 @@ void TestCrashingChild(TestPaths::Architecture architecture) { EXPECT_EQ(child.WaitForExit(), EXCEPTION_BREAKPOINT); } -TEST(ExceptionSnapshotWinTest, ChildCrash) { +#if defined(ADDRESS_SANITIZER) +// https://crbug.com/845011 +#define MAYBE_ChildCrash DISABLED_ChildCrash +#else +#define MAYBE_ChildCrash ChildCrash +#endif +TEST(ExceptionSnapshotWinTest, MAYBE_ChildCrash) { TestCrashingChild(TestPaths::Architecture::kDefault); } @@ -204,7 +210,13 @@ class SimulateDelegate : public ExceptionHandlerServer::Delegate { // Verify the dump was captured at the expected location with some slop // space. +#if defined(ADDRESS_SANITIZER) + // ASan instrumentation inserts more instructions between the expected + // location and what's reported. https://crbug.com/845011. + constexpr uint64_t kAllowedOffset = 500; +#else constexpr uint64_t kAllowedOffset = 100; +#endif EXPECT_GT(snapshot.Exception()->Context()->InstructionPointer(), dump_near_); EXPECT_LT(snapshot.Exception()->Context()->InstructionPointer(), From 5c49c598477c69e642b4feb44c4dd3e8857cd82c Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 29 May 2018 12:51:41 -0700 Subject: [PATCH 291/326] fuchsia: Implement TLS support in HTTPTransportSocket With use_boringssl_for_http_transport_socket set, this also works on Linux, however the bots fail during run lacking libcrypto.so.1.1. So, not enabled on Linux until that's figured out. (Includes https://github.com/yhirose/cpp-httplib/pull/70, until it lands and I'll do a full roll of cpp-httplib then.) Bug: crashpad:30, crashpad:196 Change-Id: I987f6a87f8e47160c15e53fe1ce28611339069ff Reviewed-on: https://chromium-review.googlesource.com/1075726 Reviewed-by: Robert Sesek Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- test/test_paths.cc | 4 + test/test_paths.h | 3 + third_party/cpp-httplib/cpp-httplib/httplib.h | 14 +- util/BUILD.gn | 41 +++- util/net/generate_test_server_key.py | 26 ++ util/net/http_transport.cc | 4 + util/net/http_transport.h | 15 ++ util/net/http_transport_socket.cc | 224 ++++++++++++++++-- util/net/http_transport_test.cc | 94 ++++++-- util/net/http_transport_test_server.cc | 65 +++-- 10 files changed, 417 insertions(+), 73 deletions(-) create mode 100755 util/net/generate_test_server_key.py diff --git a/test/test_paths.cc b/test/test_paths.cc index f8bef00c..7bfc5de0 100644 --- a/test/test_paths.cc +++ b/test/test_paths.cc @@ -230,6 +230,10 @@ base::FilePath TestPaths::BuildArtifact( directory = base::FilePath(); #endif break; + + case FileType::kCertificate: + extension = FILE_PATH_LITERAL(".pem"); + break; } return directory.Append(test_name + FILE_PATH_LITERAL("_") + artifact + diff --git a/test/test_paths.h b/test/test_paths.h index 5540f77d..89ca4851 100644 --- a/test/test_paths.h +++ b/test/test_paths.h @@ -39,6 +39,9 @@ class TestPaths { //! \brief `.dll` will be used on Windows, and `.so` will be used on other //! platforms. kLoadableModule, + + //! \brief `.pem` used for all platforms. + kCertificate, }; //! \brief The architecture of the file requested of BuildArtifact(). diff --git a/third_party/cpp-httplib/cpp-httplib/httplib.h b/third_party/cpp-httplib/cpp-httplib/httplib.h index 9a8deedd..f64f9fb0 100644 --- a/third_party/cpp-httplib/cpp-httplib/httplib.h +++ b/third_party/cpp-httplib/cpp-httplib/httplib.h @@ -1630,12 +1630,18 @@ inline int Server::bind_internal(const char* host, int port, int socket_flags) } if (port == 0) { - struct sockaddr_in sin; - socklen_t len = sizeof(sin); - if (getsockname(svr_sock_, reinterpret_cast(&sin), &len) == -1) { + struct sockaddr_storage address; + socklen_t len = sizeof(address); + if (getsockname(svr_sock_, reinterpret_cast(&address), &len) == -1) { return -1; } - return ntohs(sin.sin_port); + if (address.ss_family == AF_INET) { + return ntohs(reinterpret_cast(&address)->sin_port); + } else if (address.ss_family == AF_INET6) { + return ntohs(reinterpret_cast(&address)->sin6_port); + } else { + return -1; + } } else { return port; } diff --git a/util/BUILD.gn b/util/BUILD.gn index 4e7e40ca..cc0badbd 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -14,6 +14,10 @@ import("../build/crashpad_buildconfig.gni") +declare_args() { + use_boringssl_for_http_transport_socket = crashpad_is_fuchsia +} + if (crashpad_is_mac) { if (crashpad_is_in_chromium) { import("//build/config/sysroot.gni") @@ -246,6 +250,13 @@ static_library("util") { if (crashpad_is_linux || crashpad_is_fuchsia) { sources += [ "net/http_transport_socket.cc" ] + if (use_boringssl_for_http_transport_socket) { + defines = [ "CRASHPAD_USE_BORINGSSL" ] + libs = [ + "crypto", + "ssl", + ] + } } else if (crashpad_is_android) { sources += [ "net/http_transport_none.cc" ] } @@ -379,11 +390,11 @@ static_library("util") { if (crashpad_is_fuchsia) { sources += [ - "fuchsia/system_exception_port_key.h", "fuchsia/koid_utilities.cc", "fuchsia/koid_utilities.h", "fuchsia/scoped_task_suspend.cc", "fuchsia/scoped_task_suspend.h", + "fuchsia/system_exception_port_key.h", "misc/capture_context_fuchsia.S", "misc/paths_fuchsia.cc", "process/process_memory_fuchsia.cc", @@ -435,6 +446,17 @@ static_library("util") { } } +if (use_boringssl_for_http_transport_socket) { + action("generate_test_server_key") { + script = "net/generate_test_server_key.py" + outputs = [ + "$root_out_dir/crashpad_util_test_cert.pem", + "$root_out_dir/crashpad_util_test_key.pem", + ] + data = outputs + } +} + if (!crashpad_is_android) { crashpad_executable("http_transport_test_server") { testonly = true @@ -451,14 +473,21 @@ if (!crashpad_is_android) { ] if (crashpad_is_standalone) { - remove_configs = [ - "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", - ] + remove_configs = [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors" ] } if (crashpad_is_win) { libs = [ "ws2_32.lib" ] } + + if (use_boringssl_for_http_transport_socket) { + data_deps = [ ":generate_test_server_key" ] + defines = [ "CRASHPAD_USE_BORINGSSL" ] + libs = [ + "crypto", + "ssl", + ] + } } } @@ -610,6 +639,10 @@ source_set("util_test") { data_deps = [ ":http_transport_test_server", ] + + if (use_boringssl_for_http_transport_socket) { + defines = [ "CRASHPAD_USE_BORINGSSL" ] + } } if (crashpad_is_mac) { diff --git a/util/net/generate_test_server_key.py b/util/net/generate_test_server_key.py new file mode 100755 index 00000000..5f67bb87 --- /dev/null +++ b/util/net/generate_test_server_key.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +# Copyright 2018 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. + +import subprocess + +# GN requires a Python script for actions, so this just wraps the openssl +# command needed to generate a test private key and a certificate. These names +# must correspond to what TestPaths::BuildArtifact() constructs. +key = 'crashpad_util_test_key.pem' +cert = 'crashpad_util_test_cert.pem' +subprocess.check_call( + ['openssl', 'req', '-x509', '-nodes', '-subj', '/CN=localhost', + '-days', '365', '-newkey', 'rsa:2048', '-keyout', key, '-out', cert]) diff --git a/util/net/http_transport.cc b/util/net/http_transport.cc index 5b1f611e..7ee381bd 100644 --- a/util/net/http_transport.cc +++ b/util/net/http_transport.cc @@ -52,4 +52,8 @@ void HTTPTransport::SetTimeout(double timeout) { timeout_ = timeout; } +void HTTPTransport::SetRootCACertificatePath(const base::FilePath& cert) { + root_ca_certificate_path_ = cert; +} + } // namespace crashpad diff --git a/util/net/http_transport.h b/util/net/http_transport.h index f9948aa2..f91a5561 100644 --- a/util/net/http_transport.h +++ b/util/net/http_transport.h @@ -18,6 +18,7 @@ #include #include +#include "base/files/file_path.h" #include "base/macros.h" #include "util/net/http_headers.h" @@ -71,6 +72,16 @@ class HTTPTransport { //! \param[in] timeout The request timeout, in seconds. void SetTimeout(double timeout); + //! \brief Sets a certificate file to be used in lieu of the system CA cert + //! bundle. + //! + //! This is exposed primarily for testing with a self-signed certificate, and + //! it isn't necessary to set it in normal use. + //! + //! \param[in] cert The filename of a file in PEM format containing the CA + //! cert to be used for TLS connections. + void SetRootCACertificatePath(const base::FilePath& cert); + //! \brief Performs the HTTP request with the configured parameters and waits //! for the execution to complete. //! @@ -90,10 +101,14 @@ class HTTPTransport { const HTTPHeaders& headers() const { return headers_; } HTTPBodyStream* body_stream() const { return body_stream_.get(); } double timeout() const { return timeout_; } + const base::FilePath& root_ca_certificate_path() const { + return root_ca_certificate_path_; + } private: std::string url_; std::string method_; + base::FilePath root_ca_certificate_path_; HTTPHeaders headers_; std::unique_ptr body_stream_; double timeout_; diff --git a/util/net/http_transport_socket.cc b/util/net/http_transport_socket.cc index a6bce455..f0e2dc14 100644 --- a/util/net/http_transport_socket.cc +++ b/util/net/http_transport_socket.cc @@ -32,6 +32,10 @@ #include "util/stdlib/string_number_conversion.h" #include "util/string/split_string.h" +#if defined(CRASHPAD_USE_BORINGSSL) +#include +#endif + namespace crashpad { namespace { @@ -56,6 +60,160 @@ struct ScopedAddrinfoTraits { using ScopedAddrinfo = base::ScopedGeneric; +class Stream { + public: + virtual ~Stream() = default; + + virtual bool LoggingWrite(const void* data, size_t size) = 0; + virtual bool LoggingRead(void* data, size_t size) = 0; + virtual bool LoggingReadToEOF(std::string* contents) = 0; +}; + +class FdStream : public Stream { + public: + explicit FdStream(int fd) : fd_(fd) { CHECK(fd_ >= 0); } + + bool LoggingWrite(const void* data, size_t size) override { + return LoggingWriteFile(fd_, data, size); + } + + bool LoggingRead(void* data, size_t size) override { + return LoggingReadFileExactly(fd_, data, size); + } + + bool LoggingReadToEOF(std::string* result) override{ + return crashpad::LoggingReadToEOF(fd_, result); + } + + private: + int fd_; + + DISALLOW_COPY_AND_ASSIGN(FdStream); +}; + +#if defined(CRASHPAD_USE_BORINGSSL) +class SSLStream : public Stream { + public: + SSLStream() = default; + + bool Initialize(const base::FilePath& root_cert_path, + int sock, + const std::string& hostname) { + SSL_library_init(); + + ctx_.reset(SSL_CTX_new(TLS_method())); + if (!ctx_.is_valid()) { + LOG(ERROR) << "SSL_CTX_new"; + return false; + } + + if (SSL_CTX_set_min_proto_version(ctx_.get(), TLS1_2_VERSION) <= 0) { + LOG(ERROR) << "SSL_CTX_set_min_proto_version"; + return false; + } + + SSL_CTX_set_verify(ctx_.get(), SSL_VERIFY_PEER, nullptr); + SSL_CTX_set_verify_depth(ctx_.get(), 5); + + if (!root_cert_path.empty()) { + if (SSL_CTX_load_verify_locations( + ctx_.get(), root_cert_path.value().c_str(), nullptr) <= 0) { + LOG(ERROR) << "SSL_CTX_load_verify_locations"; + return false; + } + } else { +#if defined(OS_LINUX) + if (SSL_CTX_load_verify_locations( + ctx_.get(), nullptr, "/etc/ssl/certs") <= 0) { + LOG(ERROR) << "SSL_CTX_load_verify_locations"; + return false; + } +#elif defined(OS_FUCHSIA) + if (SSL_CTX_load_verify_locations( + ctx_.get(), "/config/ssl/cert.pem", nullptr) <= 0) { + LOG(ERROR) << "SSL_CTX_load_verify_locations"; + return false; + } +#else +#error cert store location +#endif + } + + ssl_.reset(SSL_new(ctx_.get())); + if (!ssl_.is_valid()) { + LOG(ERROR) << "SSL_new"; + return false; + } + + BIO* bio = BIO_new_socket(sock, BIO_NOCLOSE); + if (!bio) { + LOG(ERROR) << "BIO_new_socket"; + return false; + } + + // SSL_set_bio() takes ownership of |bio|. + SSL_set_bio(ssl_.get(), bio, bio); + + if (SSL_set_tlsext_host_name(ssl_.get(), hostname.c_str()) == 0) { + LOG(ERROR) << "SSL_set_tlsext_host_name"; + return false; + } + + if (SSL_connect(ssl_.get()) <= 0) { + LOG(ERROR) << "SSL_connect"; + return false; + } + + return true; + } + + bool LoggingWrite(const void* data, size_t size) override { + return SSL_write(ssl_.get(), data, size) != 0; + } + + bool LoggingRead(void* data, size_t size) override { + return SSL_read(ssl_.get(), data, size) != 0; + } + + bool LoggingReadToEOF(std::string* contents) override { + contents->clear(); + char buffer[4096]; + FileOperationResult rv; + while ((rv = SSL_read(ssl_.get(), buffer, sizeof(buffer))) > 0) { + DCHECK_LE(static_cast(rv), sizeof(buffer)); + contents->append(buffer, rv); + } + if (rv < 0) { + LOG(ERROR) << "SSL_read"; + contents->clear(); + return false; + } + return true; + } + + private: + struct ScopedSSLCTXTraits { + static SSL_CTX* InvalidValue() { return nullptr; } + static void Free(SSL_CTX* ctx) { SSL_CTX_free(ctx); } + }; + using ScopedSSLCTX = base::ScopedGeneric; + + struct ScopedSSLTraits { + static SSL* InvalidValue() { return nullptr; } + static void Free(SSL* ssl) { + SSL_shutdown(ssl); + SSL_free(ssl); + } + }; + using ScopedSSL = base::ScopedGeneric; + + ScopedSSLCTX ctx_; + ScopedSSL ssl_; + + DISALLOW_COPY_AND_ASSIGN(SSLStream); +}; +#endif + bool WaitUntilSocketIsReady(int sock) { pollfd pollfds; pollfds.fd = sock; @@ -167,14 +325,14 @@ base::ScopedFD CreateSocket(const std::string& hostname, return base::ScopedFD(); } -bool WriteRequest(int sock, +bool WriteRequest(Stream* stream, const std::string& method, const std::string& resource, const HTTPHeaders& headers, HTTPBodyStream* body_stream) { std::string request_line = base::StringPrintf( "%s %s HTTP/1.0\r\n", method.c_str(), resource.c_str()); - if (!LoggingWriteFile(sock, request_line.data(), request_line.size())) + if (!stream->LoggingWrite(request_line.data(), request_line.size())) return false; // Write headers, and determine if Content-Length has been specified. @@ -188,7 +346,7 @@ bool WriteRequest(int sock, DCHECK(!chunked); } - if (!LoggingWriteFile(sock, header_str.data(), header_str.size())) + if (!stream->LoggingWrite(header_str.data(), header_str.size())) return false; } @@ -196,13 +354,13 @@ bool WriteRequest(int sock, if (chunked) { static constexpr const char kTransferEncodingChunked[] = "Transfer-Encoding: chunked\r\n"; - if (!LoggingWriteFile( - sock, kTransferEncodingChunked, strlen(kTransferEncodingChunked))) { + if (!stream->LoggingWrite(kTransferEncodingChunked, + strlen(kTransferEncodingChunked))) { return false; } } - if (!LoggingWriteFile(sock, kCRLFTerminator, strlen(kCRLFTerminator))) { + if (!stream->LoggingWrite(kCRLFTerminator, strlen(kCRLFTerminator))) { return false; } @@ -267,7 +425,7 @@ bool WriteRequest(int sock, // sent to signal EOF. This will happen when processing the EOF indicated by // a 0 return from body_stream()->GetBytesBuffer() above. if (write_size != 0) { - if (!LoggingWriteFile(sock, write_start, write_size)) + if (!stream->LoggingWrite(write_start, write_size)) return false; } } while (data_bytes > 0); @@ -275,11 +433,11 @@ bool WriteRequest(int sock, return true; } -bool ReadLine(int sock, std::string* line) { +bool ReadLine(Stream* stream, std::string* line) { line->clear(); for (;;) { char byte; - if (!LoggingReadFileExactly(sock, &byte, 1)) { + if (!stream->LoggingRead(&byte, 1)) { return false; } @@ -293,9 +451,9 @@ bool StartsWith(const std::string& str, const char* with, size_t len) { return str.compare(0, len, with) == 0; } -bool ReadResponseLine(int sock) { +bool ReadResponseLine(Stream* stream) { std::string response_line; - if (!ReadLine(sock, &response_line)) { + if (!ReadLine(stream, &response_line)) { LOG(ERROR) << "ReadLine"; return false; } @@ -305,10 +463,10 @@ bool ReadResponseLine(int sock) { StartsWith(response_line, kHttp11, strlen(kHttp11)); } -bool ReadResponseHeaders(int sock, HTTPHeaders* headers) { +bool ReadResponseHeaders(Stream* stream, HTTPHeaders* headers) { for (;;) { std::string line; - if (!ReadLine(sock, &line)) { + if (!ReadLine(stream, &line)) { return false; } @@ -330,21 +488,21 @@ bool ReadResponseHeaders(int sock, HTTPHeaders* headers) { } } -bool ReadContentChunked(int sock, std::string* body) { +bool ReadContentChunked(Stream* stream, std::string* body) { // TODO(scottmg): https://crashpad.chromium.org/bug/196. LOG(ERROR) << "TODO(scottmg): chunked response read"; return false; } -bool ReadResponse(int sock, std::string* response_body) { +bool ReadResponse(Stream* stream, std::string* response_body) { response_body->clear(); - if (!ReadResponseLine(sock)) { + if (!ReadResponseLine(stream)) { return false; } HTTPHeaders response_headers; - if (!ReadResponseHeaders(sock, &response_headers)) { + if (!ReadResponseHeaders(stream, &response_headers)) { return false; } @@ -359,7 +517,7 @@ bool ReadResponse(int sock, std::string* response_body) { if (len) { response_body->resize(len, 0); - return ReadFileExactly(sock, &(*response_body)[0], len); + return stream->LoggingRead(&(*response_body)[0], len); } it = response_headers.find("Transfer-Encoding"); @@ -368,8 +526,8 @@ bool ReadResponse(int sock, std::string* response_body) { chunked = true; } - return chunked ? ReadContentChunked(sock, response_body) - : LoggingReadToEOF(sock, response_body); + return chunked ? ReadContentChunked(stream, response_body) + : stream->LoggingReadToEOF(response_body); } bool HTTPTransportSocket::ExecuteSynchronously(std::string* response_body) { @@ -378,16 +536,38 @@ bool HTTPTransportSocket::ExecuteSynchronously(std::string* response_body) { return false; } +#if !defined(CRASHPAD_USE_BORINGSSL) + CHECK(scheme == "http"); +#endif + base::ScopedFD sock(CreateSocket(hostname, port)); if (!sock.is_valid()) { return false; } - if (!WriteRequest(sock.get(), method(), resource, headers(), body_stream())) { +#if defined(CRASHPAD_USE_BORINGSSL) + std::unique_ptr stream; + if (scheme == "https") { + auto ssl_stream = std::make_unique(); + if (!ssl_stream->Initialize( + root_ca_certificate_path(), sock.get(), hostname)) { + LOG(ERROR) << "SSLStream Initialize"; + return false; + } + stream = std::move(ssl_stream); + } else { + stream = std::make_unique(sock.get()); + } +#else // CRASHPAD_USE_BORINGSSL + std::unique_ptr stream(std::make_unique(sock.get())); +#endif // CRASHPAD_USE_BORINGSSL + + if (!WriteRequest( + stream.get(), method(), resource, headers(), body_stream())) { return false; } - if (!ReadResponse(sock.get(), response_body)) { + if (!ReadResponse(stream.get(), response_body)) { return false; } diff --git a/util/net/http_transport_test.cc b/util/net/http_transport_test.cc index abaa1727..7b5f41df 100644 --- a/util/net/http_transport_test.cc +++ b/util/net/http_transport_test.cc @@ -42,12 +42,23 @@ namespace crashpad { namespace test { namespace { +#if defined(OS_WIN) +std::string ToUTF8IfWin(const base::string16& x) { + return base::UTF16ToUTF8(x); +} +#else +std::string ToUTF8IfWin(const std::string& x) { + return x; +} +#endif + class HTTPTransportTestFixture : public MultiprocessExec { public: using RequestValidator = void(*)(HTTPTransportTestFixture*, const std::string&); - HTTPTransportTestFixture(const HTTPHeaders& headers, + HTTPTransportTestFixture(const base::FilePath::StringType& scheme, + const HTTPHeaders& headers, std::unique_ptr body_stream, uint16_t http_response_code, RequestValidator request_validator) @@ -55,14 +66,33 @@ class HTTPTransportTestFixture : public MultiprocessExec { headers_(headers), body_stream_(std::move(body_stream)), response_code_(http_response_code), - request_validator_(request_validator) { + request_validator_(request_validator), + cert_(), + scheme_and_host_() { base::FilePath server_path = TestPaths::Executable().DirName().Append( FILE_PATH_LITERAL("http_transport_test_server") #if defined(OS_WIN) FILE_PATH_LITERAL(".exe") #endif ); - SetChildCommand(server_path, nullptr); + + if (ToUTF8IfWin(scheme) == "http") { + scheme_and_host_ = "http://localhost"; + SetChildCommand(server_path, nullptr); + } else { + std::vector args; + cert_ = TestPaths::BuildArtifact(FILE_PATH_LITERAL("util"), + FILE_PATH_LITERAL("cert"), + TestPaths::FileType::kCertificate); + args.push_back(ToUTF8IfWin(cert_.value())); + args.emplace_back(ToUTF8IfWin( + TestPaths::BuildArtifact(FILE_PATH_LITERAL("util"), + FILE_PATH_LITERAL("key"), + TestPaths::FileType::kCertificate) + .value())); + SetChildCommand(server_path, &args); + scheme_and_host_ = "https://localhost"; + } } const HTTPHeaders& headers() { return headers_; } @@ -94,7 +124,12 @@ class HTTPTransportTestFixture : public MultiprocessExec { // Now execute the HTTP request. std::unique_ptr transport(HTTPTransport::Create()); transport->SetMethod("POST"); - transport->SetURL(base::StringPrintf("http://127.0.0.1:%d/upload", port)); + + if (!cert_.empty()) { + transport->SetRootCACertificatePath(cert_); + } + transport->SetURL( + base::StringPrintf("%s:%d/upload", scheme_and_host_.c_str(), port)); for (const auto& pair : headers_) { transport->SetHeader(pair.first, pair.second); } @@ -128,6 +163,8 @@ class HTTPTransportTestFixture : public MultiprocessExec { std::unique_ptr body_stream_; uint16_t response_code_; RequestValidator request_validator_; + base::FilePath cert_; + std::string scheme_and_host_; }; constexpr char kMultipartFormData[] = "multipart/form-data"; @@ -209,7 +246,10 @@ void ValidFormData(HTTPTransportTestFixture* fixture, EXPECT_EQ(request.substr(body_start), expected); } -TEST(HTTPTransport, ValidFormData) { +class HTTPTransport + : public testing::TestWithParam {}; + +TEST_P(HTTPTransport, ValidFormData) { HTTPMultipartBuilder builder; builder.SetFormData("key1", "test"); builder.SetFormData("key2", "--abcdefg123"); @@ -217,12 +257,12 @@ TEST(HTTPTransport, ValidFormData) { HTTPHeaders headers; builder.PopulateContentHeaders(&headers); - HTTPTransportTestFixture test( + HTTPTransportTestFixture test(GetParam(), headers, builder.GetBodyStream(), 200, &ValidFormData); test.Run(); } -TEST(HTTPTransport, ValidFormData_Gzip) { +TEST_P(HTTPTransport, ValidFormData_Gzip) { HTTPMultipartBuilder builder; builder.SetGzipEnabled(true); builder.SetFormData("key1", "test"); @@ -231,8 +271,8 @@ TEST(HTTPTransport, ValidFormData_Gzip) { HTTPHeaders headers; builder.PopulateContentHeaders(&headers); - HTTPTransportTestFixture test(headers, builder.GetBodyStream(), 200, - &ValidFormData); + HTTPTransportTestFixture test( + GetParam(), headers, builder.GetBodyStream(), 200, &ValidFormData); test.Run(); } @@ -245,11 +285,11 @@ void ErrorResponse(HTTPTransportTestFixture* fixture, EXPECT_EQ(content_type, kTextPlain); } -TEST(HTTPTransport, ErrorResponse) { +TEST_P(HTTPTransport, ErrorResponse) { HTTPMultipartBuilder builder; HTTPHeaders headers; headers[kContentType] = kTextPlain; - HTTPTransportTestFixture test(headers, builder.GetBodyStream(), + HTTPTransportTestFixture test(GetParam(), headers, builder.GetBodyStream(), 404, &ErrorResponse); test.Run(); } @@ -273,7 +313,7 @@ void UnchunkedPlainText(HTTPTransportTestFixture* fixture, EXPECT_EQ(request.substr(body_start + 2), kTextBody); } -TEST(HTTPTransport, UnchunkedPlainText) { +TEST_P(HTTPTransport, UnchunkedPlainText) { std::unique_ptr body_stream( new StringHTTPBodyStream(kTextBody)); @@ -281,12 +321,13 @@ TEST(HTTPTransport, UnchunkedPlainText) { headers[kContentType] = kTextPlain; headers[kContentLength] = base::StringPrintf("%" PRIuS, strlen(kTextBody)); - HTTPTransportTestFixture test( + HTTPTransportTestFixture test(GetParam(), headers, std::move(body_stream), 200, &UnchunkedPlainText); test.Run(); } -void RunUpload33k(bool has_content_length) { +void RunUpload33k(const base::FilePath::StringType& scheme, + bool has_content_length) { // On macOS, NSMutableURLRequest winds up calling into a CFReadStream’s Read() // callback with a 32kB buffer. Make sure that it’s able to get everything // when enough is available to fill this buffer, requiring more than one @@ -303,6 +344,7 @@ void RunUpload33k(bool has_content_length) { base::StringPrintf("%" PRIuS, request_string.size()); } HTTPTransportTestFixture test( + scheme, headers, std::move(body_stream), 200, @@ -313,15 +355,31 @@ void RunUpload33k(bool has_content_length) { test.Run(); } -TEST(HTTPTransport, Upload33k) { - RunUpload33k(true); +TEST_P(HTTPTransport, Upload33k) { + RunUpload33k(GetParam(), true); } -TEST(HTTPTransport, Upload33k_LengthUnknown) { +TEST_P(HTTPTransport, Upload33k_LengthUnknown) { // The same as Upload33k, but without declaring Content-Length ahead of time. - RunUpload33k(false); + RunUpload33k(GetParam(), false); } +#if defined(CRASHPAD_USE_BORINGSSL) +// The test server requires BoringSSL or OpenSSL, so https in tests can only be +// enabled where that's readily available. Additionally on Linux, the bots fail +// lacking libcrypto.so.1.1, so disabled there for now. On Mac, they could also +// likely be enabled relatively easily, if HTTPTransportMac learned to respect +// the user-supplied cert. +INSTANTIATE_TEST_CASE_P(HTTPTransport, + HTTPTransport, + testing::Values(FILE_PATH_LITERAL("http"), + FILE_PATH_LITERAL("https"))); +#else +INSTANTIATE_TEST_CASE_P(HTTPTransport, + HTTPTransport, + testing::Values(FILE_PATH_LITERAL("http"))); +#endif + } // namespace } // namespace test } // namespace crashpad diff --git a/util/net/http_transport_test_server.cc b/util/net/http_transport_test_server.cc index b050a71f..7bc0d40a 100644 --- a/util/net/http_transport_test_server.cc +++ b/util/net/http_transport_test_server.cc @@ -34,6 +34,10 @@ #pragma warning(disable: 4244 4245 4267 4702) #endif +#if defined(CRASHPAD_USE_BORINGSSL) +#define CPPHTTPLIB_OPENSSL_SUPPORT +#endif + #define CPPHTTPLIB_ZLIB_SUPPORT #include "third_party/cpp-httplib/cpp-httplib/httplib.h" @@ -45,9 +49,20 @@ namespace crashpad { namespace { int HttpTransportTestServerMain(int argc, char* argv[]) { - httplib::Server svr(httplib::HttpVersion::v1_0); + std::unique_ptr server; + if (argc == 1) { + server.reset(new httplib::Server); +#if defined(CRASHPAD_USE_BORINGSSL) + } else if (argc == 3) { + server.reset(new httplib::SSLServer(argv[1], argv[2])); +#endif + } else { + LOG(ERROR) << "usage: http_transport_test_server [cert.pem key.pem]"; + return 1; + } - if (!svr.is_valid()) { + + if (!server->is_valid()) { LOG(ERROR) << "server creation failed"; return 1; } @@ -57,30 +72,30 @@ int HttpTransportTestServerMain(int argc, char* argv[]) { std::string to_stdout; - svr.Post("/upload", - [&response, &response_code, &svr, &to_stdout]( - const httplib::Request& req, httplib::Response& res) { - res.status = response_code; - if (response_code == 200) { - res.set_content(std::string(response, 16) + "\r\n", - "text/plain"); - } else { - res.set_content("error", "text/plain"); - } + server->Post("/upload", + [&response, &response_code, &server, &to_stdout]( + const httplib::Request& req, httplib::Response& res) { + res.status = response_code; + if (response_code == 200) { + res.set_content(std::string(response, 16) + "\r\n", + "text/plain"); + } else { + res.set_content("error", "text/plain"); + } - to_stdout += "POST /upload HTTP/1.0\r\n"; - for (const auto& h : req.headers) { - to_stdout += base::StringPrintf( - "%s: %s\r\n", h.first.c_str(), h.second.c_str()); - } - to_stdout += "\r\n"; - to_stdout += req.body; + to_stdout += "POST /upload HTTP/1.0\r\n"; + for (const auto& h : req.headers) { + to_stdout += base::StringPrintf( + "%s: %s\r\n", h.first.c_str(), h.second.c_str()); + } + to_stdout += "\r\n"; + to_stdout += req.body; - svr.stop(); - }); + server->stop(); + }); uint16_t port = - base::checked_cast(svr.bind_to_any_port("127.0.0.1")); + base::checked_cast(server->bind_to_any_port("localhost")); CheckedWriteFile( StdioFileHandle(StdioStream::kStandardOutput), &port, sizeof(port)); @@ -93,7 +108,7 @@ int HttpTransportTestServerMain(int argc, char* argv[]) { &response, sizeof(response)); - svr.listen_after_bind(); + server->listen_after_bind(); LoggingWriteFile(StdioFileHandle(StdioStream::kStandardOutput), to_stdout.data(), @@ -102,8 +117,8 @@ int HttpTransportTestServerMain(int argc, char* argv[]) { return 0; } -} // namespace -} // namespace crashpad +} // namespace +} // namespace crashpad #if defined(OS_POSIX) || defined(OS_FUCHSIA) int main(int argc, char* argv[]) { From 26ef5c910fc7e2edb441f1d2b39944195342dee9 Mon Sep 17 00:00:00 2001 From: Raphael Kubo da Costa Date: Wed, 30 May 2018 18:34:14 +0200 Subject: [PATCH 292/326] aarch64: Use xzr instead of x31 in the ASM code Officially, register X31 does not exist. The code is zeroing out a location and thus actually needs XZR. LLVM seems to automatically translate X31 into XZR when compiling the code, but GCC (tested 7.3.0) refuses to accept the instruction: ../../third_party/crashpad/crashpad/util/misc/capture_context_linux.S: Assembler messages: ../../third_party/crashpad/crashpad/util/misc/capture_context_linux.S:291: Error: operand 1 must be an integer register -- `str x31,[x0,#0xb0]' Bug: chromium:819294 Change-Id: I85be3923ac56fca6e3ec59d7e22b2223cfc8fa63 Reviewed-on: https://chromium-review.googlesource.com/1078818 Reviewed-by: Joshua Peraza Commit-Queue: Raphael Kubo da Costa --- AUTHORS | 1 + util/misc/capture_context_linux.S | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 3ad137fd..7c40c4fe 100644 --- a/AUTHORS +++ b/AUTHORS @@ -7,6 +7,7 @@ # The email address is not required for organizations. Google Inc. +Intel Corporation Opera Software ASA Vewd Software AS LG Electronics, Inc. diff --git a/util/misc/capture_context_linux.S b/util/misc/capture_context_linux.S index 6ec72454..b8d6238b 100644 --- a/util/misc/capture_context_linux.S +++ b/util/misc/capture_context_linux.S @@ -291,7 +291,7 @@ CAPTURECONTEXT_SYMBOL2: #elif defined(__aarch64__) // Zero out fault_address, which is unused. - str x31, [x0, #0xb0] // context->uc_mcontext.fault_address + str xzr, [x0, #0xb0] // context->uc_mcontext.fault_address // Save general purpose registers in context->uc_mcontext.regs[i]. // The original x0 can't be recovered. From dc22f05f6189bb13e6d3bd4919be95a1e39e0920 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 30 May 2018 15:08:18 -0700 Subject: [PATCH 293/326] fuchsia: Fix build vs. TLS support when in-tree Depend on the BoringSSL target instead of the libs when building in the Fuchsia tree. Bug: crashpad:196 Change-Id: Ib1faa9335eedff1fd9dd072234df2d48612ab423 Reviewed-on: https://chromium-review.googlesource.com/1079434 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- util/BUILD.gn | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/util/BUILD.gn b/util/BUILD.gn index cc0badbd..514f4838 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -248,14 +248,23 @@ static_library("util") { sources += get_target_outputs(":mig") } + deps = [] + if (crashpad_is_linux || crashpad_is_fuchsia) { sources += [ "net/http_transport_socket.cc" ] if (use_boringssl_for_http_transport_socket) { defines = [ "CRASHPAD_USE_BORINGSSL" ] - libs = [ - "crypto", - "ssl", - ] + + if (crashpad_is_in_fuchsia) { + deps += [ + "//third_party/boringssl" + ] + } else { + libs = [ + "crypto", + "ssl", + ] + } } } else if (crashpad_is_android) { sources += [ "net/http_transport_none.cc" ] @@ -411,7 +420,7 @@ static_library("util") { "../compat", ] - deps = [ + deps += [ "../third_party/mini_chromium:base", "../third_party/zlib", ] @@ -483,10 +492,17 @@ if (!crashpad_is_android) { if (use_boringssl_for_http_transport_socket) { data_deps = [ ":generate_test_server_key" ] defines = [ "CRASHPAD_USE_BORINGSSL" ] - libs = [ - "crypto", - "ssl", - ] + + if (crashpad_is_in_fuchsia) { + deps += [ + "//third_party/boringssl" + ] + } else { + libs = [ + "crypto", + "ssl", + ] + } } } } From 129975417947d99e3094fc2fb20f87d12d27f79e Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 30 May 2018 17:22:32 -0700 Subject: [PATCH 294/326] fuchsia: Migrate off launchpad Rather than using liblaunchpad.so to create processes, we now use fdio_spawn. Bug: crashpad:196 Change-Id: I28a7c12c823f0a0d120962edfce2e2197302b9cb Reviewed-on: https://chromium-review.googlesource.com/1080234 Commit-Queue: Scott Graham Reviewed-by: Scott Graham --- client/BUILD.gn | 12 ++--- client/crashpad_client_fuchsia.cc | 51 ++++++++++----------- test/BUILD.gn | 12 ++--- test/multiprocess_exec_fuchsia.cc | 73 ++++++++++++++++++++----------- third_party/gtest/BUILD.gn | 2 - tools/BUILD.gn | 8 ---- tools/run_with_crashpad.cc | 19 +++----- 7 files changed, 88 insertions(+), 89 deletions(-) diff --git a/client/BUILD.gn b/client/BUILD.gn index 662c2441..a074b503 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -88,14 +88,10 @@ static_library("client") { cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union } - if (crashpad_is_fuchsia) { - if (crashpad_is_in_fuchsia) { - deps += [ - "//zircon/public/lib/launchpad", - ] - } else { - libs = [ "launchpad" ] - } + if (crashpad_is_fuchsia && crashpad_is_in_fuchsia) { + deps += [ + "//zircon/public/lib/fdio", + ] } } diff --git a/client/crashpad_client_fuchsia.cc b/client/crashpad_client_fuchsia.cc index 59cf3873..0d4b8727 100644 --- a/client/crashpad_client_fuchsia.cc +++ b/client/crashpad_client_fuchsia.cc @@ -14,7 +14,7 @@ #include "client/crashpad_client.h" -#include +#include #include #include @@ -69,20 +69,6 @@ bool CrashpadClient::StartHandler( std::vector argv; ConvertArgvStrings(argv_strings, &argv); - // ConvertArgvStrings adds an unnecessary nullptr at the end of the argv list, - // which causes launchpad_set_args() to hang. - argv.pop_back(); - - launchpad_t* lp; - launchpad_create(zx_job_default(), argv[0], &lp); - launchpad_load_from_file(lp, argv[0]); - launchpad_set_args(lp, argv.size(), &argv[0]); - - // TODO(scottmg): https://crashpad.chromium.org/bug/196, this is useful during - // bringup, but should probably be made minimal for real usage. - launchpad_clone(lp, - LP_CLONE_FDIO_NAMESPACE | LP_CLONE_FDIO_STDIO | - LP_CLONE_ENVIRON | LP_CLONE_DEFAULT_JOB); // Follow the same protocol as devmgr and crashlogger in Zircon (that is, // process handle as handle 0, with type USER0, exception port handle as @@ -91,24 +77,39 @@ bool CrashpadClient::StartHandler( // released here. Currently it is assumed that this process's default job // handle is the exception port that should be monitored. In the future, it // might be useful for this to be configurable by the client. - zx_handle_t handles[] = {ZX_HANDLE_INVALID, ZX_HANDLE_INVALID}; - status = - zx_handle_duplicate(zx_job_default(), ZX_RIGHT_SAME_RIGHTS, &handles[0]); + constexpr size_t kActionCount = 2; + fdio_spawn_action_t actions[] = { + {.action = FDIO_SPAWN_ACTION_ADD_HANDLE, + .h = {.id = PA_HND(PA_USER0, 0), .handle = ZX_HANDLE_INVALID}}, + {.action = FDIO_SPAWN_ACTION_ADD_HANDLE, + .h = {.id = PA_HND(PA_USER0, 1), .handle = ZX_HANDLE_INVALID}}, + }; + + status = zx_handle_duplicate( + zx_job_default(), ZX_RIGHT_SAME_RIGHTS, &actions[0].h.handle); if (status != ZX_OK) { ZX_LOG(ERROR, status) << "zx_handle_duplicate"; return false; } - handles[1] = exception_port.release(); - uint32_t handle_types[] = {PA_HND(PA_USER0, 0), PA_HND(PA_USER0, 1)}; + actions[1].h.handle = exception_port.release(); - launchpad_add_handles(lp, arraysize(handles), handles, handle_types); - - const char* error_message; + char error_message[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; zx_handle_t child_raw; - status = launchpad_go(lp, &child_raw, &error_message); + // TODO(scottmg): https://crashpad.chromium.org/bug/196, FDIO_SPAWN_CLONE_ALL + // is useful during bringup, but should probably be made minimal for real + // usage. + status = fdio_spawn_etc(ZX_HANDLE_INVALID, + FDIO_SPAWN_CLONE_ALL, + argv[0], + argv.data(), + nullptr, + kActionCount, + actions, + &child_raw, + error_message); base::ScopedZxHandle child(child_raw); if (status != ZX_OK) { - ZX_LOG(ERROR, status) << "launchpad_go: " << error_message; + ZX_LOG(ERROR, status) << "fdio_spawn_etc: " << error_message; return false; } diff --git a/test/BUILD.gn b/test/BUILD.gn index ec51e7d2..d1e6297e 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -127,14 +127,10 @@ static_library("test") { libs = [ "shell32.lib" ] } - if (crashpad_is_fuchsia) { - if (crashpad_is_in_fuchsia) { - deps += [ - "//zircon/public/lib/launchpad", - ] - } else { - libs = [ "launchpad" ] - } + if (crashpad_is_fuchsia && crashpad_is_in_fuchsia) { + deps += [ + "//zircon/public/lib/fdio", + ] } } diff --git a/test/multiprocess_exec_fuchsia.cc b/test/multiprocess_exec_fuchsia.cc index 7d571f91..98764222 100644 --- a/test/multiprocess_exec_fuchsia.cc +++ b/test/multiprocess_exec_fuchsia.cc @@ -14,8 +14,10 @@ #include "test/multiprocess_exec.h" -#include +#include +#include #include +#include #include #include "base/files/scoped_file.h" @@ -26,6 +28,21 @@ namespace crashpad { namespace test { +namespace { + +void AddPipe(fdio_spawn_action_t* action, int target_fd, int* fd_out) { + zx_handle_t handle; + uint32_t id; + zx_status_t status = fdio_pipe_half(&handle, &id); + ZX_CHECK(status < 0, status) << "Failed to create pipe."; + action->action = FDIO_SPAWN_ACTION_ADD_HANDLE; + action->h.id = PA_HND(PA_HND_TYPE(id), target_fd); + action->h.handle = handle; + *fd_out = status; +} + +} // namespace + namespace internal { struct MultiprocessInfo { @@ -38,10 +55,7 @@ struct MultiprocessInfo { } // namespace internal Multiprocess::Multiprocess() - : info_(nullptr), - code_(EXIT_SUCCESS), - reason_(kTerminationNormal) { -} + : info_(nullptr), code_(EXIT_SUCCESS), reason_(kTerminationNormal) {} void Multiprocess::Run() { // Set up and spawn the child process. @@ -122,8 +136,7 @@ void Multiprocess::RunChild() { } MultiprocessExec::MultiprocessExec() - : Multiprocess(), command_(), arguments_(), argv_() { -} + : Multiprocess(), command_(), arguments_(), argv_() {} void MultiprocessExec::SetChildCommand( const base::FilePath& command, @@ -147,37 +160,45 @@ void MultiprocessExec::PreFork() { for (const std::string& argument : arguments_) { argv_.push_back(argument.c_str()); } + argv_.push_back(nullptr); ASSERT_EQ(info(), nullptr); set_info(new internal::MultiprocessInfo()); } void MultiprocessExec::MultiprocessChild() { - launchpad_t* lp; - launchpad_create(zx_job_default(), command_.value().c_str(), &lp); - launchpad_load_from_file(lp, command_.value().c_str()); - launchpad_set_args(lp, argv_.size(), &argv_[0]); + constexpr size_t kActionCount = 3; + fdio_spawn_action_t actions[kActionCount]; + + int stdin_parent_side = -1; + AddPipe(&actions[0], STDIN_FILENO, &stdin_parent_side); + info()->stdin_write.reset(stdin_parent_side); + + int stdout_parent_side = -1; + AddPipe(&actions[1], STDOUT_FILENO, &stdout_parent_side); + info()->stdout_read.reset(stdout_parent_side); + + actions[2].action = FDIO_SPAWN_ACTION_CLONE_FD; + actions[2].fd.local_fd = STDERR_FILENO; + actions[2].fd.target_fd = STDERR_FILENO; // Pass the filesystem namespace, parent environment, and default job to the // child, but don't include any other file handles, preferring to set them // up explicitly below. - launchpad_clone( - lp, LP_CLONE_FDIO_NAMESPACE | LP_CLONE_ENVIRON | LP_CLONE_DEFAULT_JOB); + uint32_t flags = FDIO_SPAWN_CLONE_ALL & ~FDIO_SPAWN_CLONE_STDIO; - int stdin_parent_side; - launchpad_add_pipe(lp, &stdin_parent_side, STDIN_FILENO); - info()->stdin_write.reset(stdin_parent_side); - - int stdout_parent_side; - launchpad_add_pipe(lp, &stdout_parent_side, STDOUT_FILENO); - info()->stdout_read.reset(stdout_parent_side); - - launchpad_clone_fd(lp, STDERR_FILENO, STDERR_FILENO); - - const char* error_message; + char error_message[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; zx_handle_t child; - zx_status_t status = launchpad_go(lp, &child, &error_message); - ZX_CHECK(status == ZX_OK, status) << "launchpad_go: " << error_message; + zx_status_t status = fdio_spawn_etc(ZX_HANDLE_INVALID, + flags, + command_.value().c_str(), + argv_.data(), + nullptr, + kActionCount, + actions, + &child, + error_message); + ZX_CHECK(status == ZX_OK, status) << "fdio_spawn_etc: " << error_message; info()->child.reset(child); } diff --git a/third_party/gtest/BUILD.gn b/third_party/gtest/BUILD.gn index 37fceaf0..6662b6c7 100644 --- a/third_party/gtest/BUILD.gn +++ b/third_party/gtest/BUILD.gn @@ -49,7 +49,6 @@ if (crashpad_is_in_chromium) { if (crashpad_is_fuchsia) { libs = [ - "launchpad", "zircon", ] } @@ -272,7 +271,6 @@ if (crashpad_is_in_chromium) { if (crashpad_is_fuchsia) { libs = [ - "launchpad", "zircon", ] } diff --git a/tools/BUILD.gn b/tools/BUILD.gn index eb76a240..cd1e95f4 100644 --- a/tools/BUILD.gn +++ b/tools/BUILD.gn @@ -104,14 +104,6 @@ if (crashpad_is_mac || crashpad_is_fuchsia) { "../third_party/mini_chromium:base", "../util", ] - - if (crashpad_is_fuchsia) { - if (crashpad_is_in_fuchsia) { - deps += [ "//zircon/public/lib/launchpad" ] - } else { - libs = [ "launchpad" ] - } - } } } diff --git a/tools/run_with_crashpad.cc b/tools/run_with_crashpad.cc index a65f5256..f26dd2bc 100644 --- a/tools/run_with_crashpad.cc +++ b/tools/run_with_crashpad.cc @@ -32,7 +32,7 @@ #include "util/string/split_string.h" #if defined(OS_FUCHSIA) -#include +#include #include #include @@ -48,7 +48,7 @@ void Usage(const std::string& me) { "Start a Crashpad handler and have it handle crashes from COMMAND.\n" "\n" #if defined(OS_FUCHSIA) -"COMMAND is run via launchpad, so must be a qualified path to the subprocess to\n" +"COMMAND is run via fdio_spawn, so must be a qualified path to the subprocess to\n" "be executed.\n" #else "COMMAND is run via execvp() so the PATH will be searched.\n" @@ -190,17 +190,12 @@ int RunWithCrashpadMain(int argc, char* argv[]) { } #if defined(OS_FUCHSIA) - // Fuchsia doesn't implement execvp(), launch with launchpad here. - launchpad_t* lp; - launchpad_create(zx_job_default(), argv[0], &lp); - launchpad_load_from_file(lp, argv[0]); - launchpad_set_args(lp, argc, argv); - launchpad_clone(lp, LP_CLONE_ALL); - const char* error_message; - zx_handle_t child; - zx_status_t status = launchpad_go(lp, &child, &error_message); + // Fuchsia doesn't implement execvp(), launch with fdio_spawn here. + zx_handle_t child = ZX_HANDLE_INVALID; + zx_status_t status = fdio_spawn( + ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, argv[0], argv, &child); if (status != ZX_OK) { - ZX_LOG(ERROR, status) << "launchpad_go: " << error_message; + ZX_LOG(ERROR, status) << "fdio_spawn failed"; return status == ZX_ERR_IO ? kExitExecENOENT : kExitExecFailure; } From 51ebe79f0619c85a98f729b01ba17971e37eac1a Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 31 May 2018 12:44:09 -0700 Subject: [PATCH 295/326] fuchsia: Fix inverted CHECK condition in migration to fdio_spawn Bug: crashpad:196 Change-Id: I7465669d7d7ea9e0692fc5e4e8df140b4d388cc1 Reviewed-on: https://chromium-review.googlesource.com/1081288 Reviewed-by: Scott Graham Commit-Queue: Scott Graham --- test/multiprocess_exec_fuchsia.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/multiprocess_exec_fuchsia.cc b/test/multiprocess_exec_fuchsia.cc index 98764222..20f3ab65 100644 --- a/test/multiprocess_exec_fuchsia.cc +++ b/test/multiprocess_exec_fuchsia.cc @@ -34,7 +34,7 @@ void AddPipe(fdio_spawn_action_t* action, int target_fd, int* fd_out) { zx_handle_t handle; uint32_t id; zx_status_t status = fdio_pipe_half(&handle, &id); - ZX_CHECK(status < 0, status) << "Failed to create pipe."; + ZX_CHECK(status >= 0, status) << "fdio_pipe_half"; action->action = FDIO_SPAWN_ACTION_ADD_HANDLE; action->h.id = PA_HND(PA_HND_TYPE(id), target_fd); action->h.handle = handle; From 9de3535bbb035c3023d813fbeb8d786df22888c3 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Fri, 1 Jun 2018 16:15:59 -0700 Subject: [PATCH 296/326] fuchsia: Re-add launchpad for now launchpad usage directly in Crashpad was removed, but gtest still uses launchpad (for death tests) so keep it here for now (until the gtest upstream change has landed to remove launchpad there). Also, roll mini_chromium and remove link of zircon, since that's in the base configuration now. Bug: crashpad:196 Change-Id: I68b1092aaa6fc31efe693e3fcd5bde71c2d91d42 Reviewed-on: https://chromium-review.googlesource.com/1083611 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- DEPS | 2 +- third_party/gtest/BUILD.gn | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/DEPS b/DEPS index 1818580a..fdb148d7 100644 --- a/DEPS +++ b/DEPS @@ -29,7 +29,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '53a3ff4cf81adb25fdf764e8547a8515fc8f70c2', + 'd5a36d3c51a7a270afc2c888baaaec2a6e496219', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/third_party/gtest/BUILD.gn b/third_party/gtest/BUILD.gn index 6662b6c7..f9108281 100644 --- a/third_party/gtest/BUILD.gn +++ b/third_party/gtest/BUILD.gn @@ -46,11 +46,8 @@ if (crashpad_is_in_chromium) { visibility = [ ":*" ] include_dirs = [ "gtest/googletest" ] defines = [ "GUNIT_NO_GOOGLE3=1" ] - if (crashpad_is_fuchsia) { - libs = [ - "zircon", - ] + libs = [ "launchpad" ] } } @@ -268,11 +265,8 @@ if (crashpad_is_in_chromium) { config("gmock_private_config") { visibility = [ ":*" ] include_dirs = [ "gtest/googlemock" ] - if (crashpad_is_fuchsia) { - libs = [ - "zircon", - ] + libs = [ "launchpad" ] } } From 3edb7869da3173c31c74149c0e6d57b6129975d2 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 31 May 2018 21:53:44 -0700 Subject: [PATCH 297/326] Roll cpp-httplib to HEAD (5b3187e2f) Bug: crashpad:196, crashpad:227 Change-Id: If798744a71aec63e5320cc3300b70d24fed5ceae Reviewed-on: https://chromium-review.googlesource.com/1082025 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- third_party/cpp-httplib/README.crashpad | 2 +- third_party/cpp-httplib/cpp-httplib/httplib.h | 164 ++++++++++-------- util/net/http_transport_test_server.cc | 2 + 3 files changed, 90 insertions(+), 78 deletions(-) diff --git a/third_party/cpp-httplib/README.crashpad b/third_party/cpp-httplib/README.crashpad index 81010326..cfb476e0 100644 --- a/third_party/cpp-httplib/README.crashpad +++ b/third_party/cpp-httplib/README.crashpad @@ -1,7 +1,7 @@ Name: cpp-httplib Short Name: cpp-httplib URL: https://github.com/yhirose/cpp-httplib -Revision: 37130cd7f9df4fb4454d94dd9f0a3bb7c6ccbad8 +Revision: 5b3187e2f9e77c672063d49a1167bbb563da023e License: MIT License File: cpp-httplib/LICENSE Security Critical: no (test only) diff --git a/third_party/cpp-httplib/cpp-httplib/httplib.h b/third_party/cpp-httplib/cpp-httplib/httplib.h index f64f9fb0..dadab1d8 100644 --- a/third_party/cpp-httplib/cpp-httplib/httplib.h +++ b/third_party/cpp-httplib/cpp-httplib/httplib.h @@ -77,7 +77,6 @@ typedef int socket_t; /* * Configuration */ -#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0 @@ -121,6 +120,7 @@ typedef std::multimap MultipartFiles; struct Request { std::string version; std::string method; + std::string target; std::string path; Headers headers; std::string body; @@ -151,7 +151,7 @@ struct Response { std::string get_header_value(const char* key) const; void set_header(const char* key, const char* val); - void set_redirect(const char* url); + void set_redirect(const char* uri); void set_content(const char* s, size_t n, const char* content_type); void set_content(const std::string& s, const char* content_type); @@ -189,7 +189,7 @@ public: typedef std::function Handler; typedef std::function Logger; - Server(HttpVersion http_version = HttpVersion::v1_0); + Server(); virtual ~Server(); @@ -207,6 +207,8 @@ public: void set_error_handler(Handler handler); void set_logger(Logger logger); + void set_keep_alive_max_count(size_t count); + int bind_to_any_port(const char* host, int socket_flags = 0); bool listen_after_bind(); @@ -216,9 +218,9 @@ public: void stop(); protected: - bool process_request(Stream& strm, bool last_connection); + bool process_request(Stream& strm, bool last_connection, bool& connection_close); - const HttpVersion http_version_; + size_t keep_alive_max_count_; private: typedef std::vector> Handlers; @@ -257,8 +259,7 @@ public: Client( const char* host, int port = 80, - size_t timeout_sec = 300, - HttpVersion http_version = HttpVersion::v1_0); + size_t timeout_sec = 300); virtual ~Client(); @@ -288,12 +289,11 @@ public: bool send(Request& req, Response& res); protected: - bool process_request(Stream& strm, Request& req, Response& res); + bool process_request(Stream& strm, Request& req, Response& res, bool& connection_close); const std::string host_; const int port_; size_t timeout_sec_; - const HttpVersion http_version_; const std::string host_and_port_; private: @@ -323,8 +323,7 @@ private: class SSLServer : public Server { public: SSLServer( - const char* cert_path, const char* private_key_path, - HttpVersion http_version = HttpVersion::v1_0); + const char* cert_path, const char* private_key_path); virtual ~SSLServer(); @@ -342,8 +341,7 @@ public: SSLClient( const char* host, int port = 80, - size_t timeout_sec = 300, - HttpVersion http_version = HttpVersion::v1_0); + size_t timeout_sec = 300); virtual ~SSLClient(); @@ -362,8 +360,6 @@ private: */ namespace detail { -static std::vector http_version_strings = { "HTTP/1.0", "HTTP/1.1" }; - template void split(const char* b, const char* e, char d, Fn fn) { @@ -501,27 +497,31 @@ inline bool wait_until_socket_is_ready(socket_t sock, size_t sec, size_t usec) } template -inline bool read_and_close_socket(socket_t sock, bool keep_alive, T callback) +inline bool read_and_close_socket(socket_t sock, size_t keep_alive_max_count, T callback) { bool ret = false; - if (keep_alive) { - auto count = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; + if (keep_alive_max_count > 0) { + auto count = keep_alive_max_count; while (count > 0 && detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { - auto last_connection = count == 1; SocketStream strm(sock); - ret = callback(strm, last_connection); - if (!ret) { + auto last_connection = count == 1; + auto connection_close = false; + + ret = callback(strm, last_connection, connection_close); + if (!ret || connection_close) { break; } + count--; } } else { SocketStream strm(sock); - ret = callback(strm, true); + auto dummy_connection_close = false; + ret = callback(strm, true, dummy_connection_close); } close_socket(sock); @@ -1413,8 +1413,8 @@ inline std::string SocketStream::get_remote_addr() { } // HTTP server implementation -inline Server::Server(HttpVersion http_version) - : http_version_(http_version) +inline Server::Server() + : keep_alive_max_count_(5) , is_running_(false) , svr_sock_(INVALID_SOCKET) , running_threads_(0) @@ -1477,6 +1477,11 @@ inline void Server::set_logger(Logger logger) logger_ = logger; } +inline void Server::set_keep_alive_max_count(size_t count) +{ + keep_alive_max_count_ = count; +} + inline int Server::bind_to_any_port(const char* host, int socket_flags) { return bind_internal(host, 0, socket_flags); @@ -1510,18 +1515,19 @@ inline void Server::stop() inline bool Server::parse_request_line(const char* s, Request& req) { - static std::regex re("(GET|HEAD|POST|PUT|DELETE|OPTIONS) ([^?]+)(?:\\?(.+?))? (HTTP/1\\.[01])\r\n"); + static std::regex re("(GET|HEAD|POST|PUT|DELETE|OPTIONS) (([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n"); std::cmatch m; if (std::regex_match(s, m, re)) { req.version = std::string(m[4]); req.method = std::string(m[1]); - req.path = detail::decode_url(m[2]); + req.target = std::string(m[2]); + req.path = detail::decode_url(m[3]); // Parse query text - auto len = std::distance(m[3].first, m[3].second); + auto len = std::distance(m[4].first, m[4].second); if (len > 0) { - detail::parse_query_text(m[3], req.params); + detail::parse_query_text(m[4], req.params); } return true; @@ -1539,19 +1545,20 @@ inline void Server::write_response(Stream& strm, bool last_connection, const Req } // Response line - strm.write_format("%s %d %s\r\n", - detail::http_version_strings[static_cast(http_version_)], + strm.write_format("HTTP/1.1 %d %s\r\n", res.status, detail::status_message(res.status)); // Headers - if (!res.has_header("Connection") && (last_connection || req.version == "HTTP/1.0")) { + if (last_connection || + req.version == "HTTP/1.0" || + req.get_header_value("Connection") == "close") { res.set_header("Connection", "close"); } if (!res.body.empty()) { #ifdef CPPHTTPLIB_ZLIB_SUPPORT - // TODO: Server version is HTTP/1.1 and 'Accpet-Encoding' has gzip, not gzip;q=0 + // TODO: 'Accpet-Encoding' has gzip, not gzip;q=0 const auto& encodings = req.get_header_value("Accept-Encoding"); if (encodings.find("gzip") != std::string::npos && detail::can_compress(res.get_header_value("Content-Type"))) { @@ -1740,7 +1747,7 @@ inline bool Server::dispatch_request(Request& req, Response& res, Handlers& hand return false; } -inline bool Server::process_request(Stream& strm, bool last_connection) +inline bool Server::process_request(Stream& strm, bool last_connection, bool& connection_close) { const auto bufsiz = 2048; char buf[bufsiz]; @@ -1755,7 +1762,7 @@ inline bool Server::process_request(Stream& strm, bool last_connection) Request req; Response res; - res.version = detail::http_version_strings[static_cast(http_version_)]; + res.version = "HTTP/1.1"; // Request line and headers if (!parse_request_line(reader.ptr(), req) || !detail::read_headers(strm, req.headers)) { @@ -1766,7 +1773,8 @@ inline bool Server::process_request(Stream& strm, bool last_connection) auto ret = true; if (req.get_header_value("Connection") == "close") { - ret = false; + // ret = false; + connection_close = true; } req.set_header("REMOTE_ADDR", strm.get_remote_addr().c_str()); @@ -1823,23 +1831,20 @@ inline bool Server::is_valid() const inline bool Server::read_and_close_socket(socket_t sock) { - auto keep_alive = http_version_ == HttpVersion::v1_1; - return detail::read_and_close_socket( sock, - keep_alive, - [this](Stream& strm, bool last_connection) { - return process_request(strm, last_connection); + keep_alive_max_count_, + [this](Stream& strm, bool last_connection, bool& connection_close) { + return process_request(strm, last_connection, connection_close); }); } // HTTP client implementation inline Client::Client( - const char* host, int port, size_t timeout_sec, HttpVersion http_version) + const char* host, int port, size_t timeout_sec) : host_(host) , port_(port) , timeout_sec_(timeout_sec) - , http_version_(http_version) , host_and_port_(host_ + ":" + std::to_string(port_)) { } @@ -1884,11 +1889,12 @@ inline bool Client::read_response_line(Stream& strm, Response& res) return false; } - const static std::regex re("HTTP/1\\.[01] (\\d+?) .+\r\n"); + const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .+\r\n"); std::cmatch m; if (std::regex_match(reader.ptr(), m, re)) { - res.status = std::stoi(std::string(m[1])); + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); } return true; @@ -1913,10 +1919,9 @@ inline void Client::write_request(Stream& strm, Request& req) auto path = detail::encode_url(req.path); // Request line - strm.write_format("%s %s %s\r\n", + strm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), - path.c_str(), - detail::http_version_strings[static_cast(http_version_)]); + path.c_str()); // Headers req.set_header("Host", host_and_port_.c_str()); @@ -1929,11 +1934,10 @@ inline void Client::write_request(Stream& strm, Request& req) req.set_header("User-Agent", "cpp-httplib/0.2"); } - // TODO: if (!req.has_header("Connection") && - // (last_connection || http_version_ == detail::HttpVersion::v1_0)) { - if (!req.has_header("Connection")) { + // TODO: Support KeepAlive connection + // if (!req.has_header("Connection")) { req.set_header("Connection", "close"); - } + // } if (!req.body.empty()) { if (!req.has_header("Content-Type")) { @@ -1957,7 +1961,7 @@ inline void Client::write_request(Stream& strm, Request& req) } } -inline bool Client::process_request(Stream& strm, Request& req, Response& res) +inline bool Client::process_request(Stream& strm, Request& req, Response& res, bool& connection_close) { // Send request write_request(strm, req); @@ -1967,7 +1971,9 @@ inline bool Client::process_request(Stream& strm, Request& req, Response& res) return false; } - // TODO: Check if 'Connection' header is 'close' or HTTP version is 1.0, then close socket... + if (res.get_header_value("Connection") == "close" || res.version == "HTTP/1.0") { + connection_close = true; + } // Body if (req.method != "HEAD") { @@ -1989,9 +1995,12 @@ inline bool Client::process_request(Stream& strm, Request& req, Response& res) inline bool Client::read_and_close_socket(socket_t sock, Request& req, Response& res) { - return detail::read_and_close_socket(sock, false, [&](Stream& strm, bool /*last_connection*/) { - return process_request(strm, req, res); - }); + return detail::read_and_close_socket( + sock, + 0, + [&](Stream& strm, bool /*last_connection*/, bool& connection_close) { + return process_request(strm, req, res, connection_close); + }); } inline std::shared_ptr Client::Get(const char* path, Progress progress) @@ -2135,8 +2144,9 @@ namespace detail { template inline bool read_and_close_socket_ssl( - socket_t sock, bool keep_alive, - // TODO: OpenSSL 1.0.2 occasionally crashes... The upcoming 1.1.0 is going to be thread safe. + socket_t sock, size_t keep_alive_max_count, + // TODO: OpenSSL 1.0.2 occasionally crashes... + // The upcoming 1.1.0 is going to be thread safe. SSL_CTX* ctx, std::mutex& ctx_mutex, U SSL_connect_or_accept, V setup, T callback) @@ -2160,23 +2170,27 @@ inline bool read_and_close_socket_ssl( bool ret = false; - if (keep_alive) { - auto count = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; + if (keep_alive_max_count > 0) { + auto count = keep_alive_max_count; while (count > 0 && detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { - auto last_connection = count == 1; SSLSocketStream strm(sock, ssl); - ret = callback(strm, last_connection); - if (!ret) { + auto last_connection = count == 1; + auto connection_close = false; + + ret = callback(strm, last_connection, connection_close); + if (!ret || connection_close) { break; } + count--; } } else { SSLSocketStream strm(sock, ssl); - ret = callback(strm, true); + auto dummy_connection_close = false; + ret = callback(strm, true, dummy_connection_close); } SSL_shutdown(ssl); @@ -2233,8 +2247,7 @@ inline std::string SSLSocketStream::get_remote_addr() { } // SSL HTTP server implementation -inline SSLServer::SSLServer(const char* cert_path, const char* private_key_path, HttpVersion http_version) - : Server(http_version) +inline SSLServer::SSLServer(const char* cert_path, const char* private_key_path) { ctx_ = SSL_CTX_new(SSLv23_server_method()); @@ -2270,23 +2283,20 @@ inline bool SSLServer::is_valid() const inline bool SSLServer::read_and_close_socket(socket_t sock) { - auto keep_alive = http_version_ == HttpVersion::v1_1; - return detail::read_and_close_socket_ssl( sock, - keep_alive, + keep_alive_max_count_, ctx_, ctx_mutex_, SSL_accept, [](SSL* /*ssl*/) {}, - [this](Stream& strm, bool last_connection) { - return process_request(strm, last_connection); + [this](Stream& strm, bool last_connection, bool& connection_close) { + return process_request(strm, last_connection, connection_close); }); } // SSL HTTP client implementation -inline SSLClient::SSLClient( - const char* host, int port, size_t timeout_sec, HttpVersion http_version) - : Client(host, port, timeout_sec, http_version) +inline SSLClient::SSLClient(const char* host, int port, size_t timeout_sec) + : Client(host, port, timeout_sec) { ctx_ = SSL_CTX_new(SSLv23_client_method()); } @@ -2306,14 +2316,14 @@ inline bool SSLClient::is_valid() const inline bool SSLClient::read_and_close_socket(socket_t sock, Request& req, Response& res) { return is_valid() && detail::read_and_close_socket_ssl( - sock, false, + sock, 0, ctx_, ctx_mutex_, SSL_connect, [&](SSL* ssl) { SSL_set_tlsext_host_name(ssl, host_.c_str()); }, - [&](Stream& strm, bool /*last_connection*/) { - return process_request(strm, req, res); + [&](Stream& strm, bool /*last_connection*/, bool& connection_close) { + return process_request(strm, req, res, connection_close); }); } #endif diff --git a/util/net/http_transport_test_server.cc b/util/net/http_transport_test_server.cc index 7bc0d40a..a3bcc6a5 100644 --- a/util/net/http_transport_test_server.cc +++ b/util/net/http_transport_test_server.cc @@ -67,6 +67,8 @@ int HttpTransportTestServerMain(int argc, char* argv[]) { return 1; } + server->set_keep_alive_max_count(1); + uint16_t response_code; char response[16]; From 9a97daff395a312ae5d15f10a6aa62f483c3d6b5 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Mon, 4 Jun 2018 16:16:19 -0700 Subject: [PATCH 298/326] Add libfuzzer support Adds the build support for using libfuzzer controlled by setting `crashpad_use_libfuzzer=true`. Also adds a first fuzzer (for ElfImageReader). Currently only runs on Linux, but should work on Fuchsia too with some minor fixes (not sure yet whether the fixes required are toolchain or in our build setup). Run as: out/lin/elf_image_reader_fuzzer snapshot/elf/elf_image_reader_fuzzer_corpus/ hits an OOM pretty quickly in trying to allocate a giant buffer. Bug: crashpad:30, crashpad:196, crashpad:233 Change-Id: Idd3ca11fe00319b8b29e029d5e13b17bfd518ea0 Reviewed-on: https://chromium-review.googlesource.com/1083451 Reviewed-by: Robert Sesek Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- .gitignore | 1 + DEPS | 3 + build/BUILD.gn | 12 +++ build/BUILDCONFIG.gn | 8 ++ build/crashpad_fuzzer_test.gni | 46 +++++++++++ snapshot/BUILD.gn | 12 +++ snapshot/elf/elf_image_reader_fuzzer.cc | 75 ++++++++++++++++++ ...shpad_snapshot_test_both_dt_hash_styles.so | Bin 0 -> 8544 bytes .../elf/elf_image_reader_fuzzer_corpus/ret42 | Bin 0 -> 8264 bytes third_party/libfuzzer/BUILD.gn | 49 ++++++++++++ 10 files changed, 206 insertions(+) create mode 100644 build/crashpad_fuzzer_test.gni create mode 100644 snapshot/elf/elf_image_reader_fuzzer.cc create mode 100755 snapshot/elf/elf_image_reader_fuzzer_corpus/crashpad_snapshot_test_both_dt_hash_styles.so create mode 100755 snapshot/elf/elf_image_reader_fuzzer_corpus/ret42 create mode 100644 third_party/libfuzzer/BUILD.gn diff --git a/.gitignore b/.gitignore index d225542e..982a95e7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ /third_party/fuchsia/qemu /third_party/fuchsia/sdk /third_party/gtest/gtest +/third_party/libfuzzer /third_party/linux/.cipd /third_party/linux/clang /third_party/linux/sysroot diff --git a/DEPS b/DEPS index fdb148d7..5f96b781 100644 --- a/DEPS +++ b/DEPS @@ -30,6 +30,9 @@ deps = { 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + 'd5a36d3c51a7a270afc2c888baaaec2a6e496219', + 'crashpad/third_party/libfuzzer/src': + Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' + + 'fda403cf93ecb8792cb1d061564d89a6553ca020', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/build/BUILD.gn b/build/BUILD.gn index 5e7aed0c..0ef1127f 100644 --- a/build/BUILD.gn +++ b/build/BUILD.gn @@ -37,3 +37,15 @@ group("default_exe_manifest_win") { ] } } + +config("crashpad_fuzzer_flags") { + cflags = [ + "-fsanitize=address", + "-fsanitize-address-use-after-scope", + "-fsanitize=fuzzer", + ] + + ldflags = [ + "-fsanitize=address", + ] +} diff --git a/build/BUILDCONFIG.gn b/build/BUILDCONFIG.gn index 0e6da165..95dc2771 100644 --- a/build/BUILDCONFIG.gn +++ b/build/BUILDCONFIG.gn @@ -46,6 +46,10 @@ declare_args() { # and logging. When false, enables the release configuration, with additional # optimizations. is_debug = false + + # When true, build all code with -fsanitize=fuzzer, and enable various + # *_fuzzer targets. + crashpad_use_libfuzzer = false } _default_configs = [ @@ -53,6 +57,10 @@ _default_configs = [ "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", ] +if (crashpad_use_libfuzzer) { + _default_configs += [ "//build:crashpad_fuzzer_flags" ] +} + _default_executable_configs = _default_configs + [ "//third_party/mini_chromium/mini_chromium/build:executable", diff --git a/build/crashpad_fuzzer_test.gni b/build/crashpad_fuzzer_test.gni new file mode 100644 index 00000000..8e0eaa9f --- /dev/null +++ b/build/crashpad_fuzzer_test.gni @@ -0,0 +1,46 @@ +# Copyright 2018 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. + +import("crashpad_buildconfig.gni") +import("test.gni") + +template("fuzzer_test") { + if (crashpad_is_standalone && crashpad_use_libfuzzer) { + test(target_name) { + forward_variables_from(invoker, + [ + "cflags", + "cflags_cc", + "check_includes", + "defines", + "include_dirs", + "sources", + ]) + configs += [ "..:crashpad_config" ] + if (defined(invoker.deps)) { + deps = invoker.deps + } + deps += [ "../third_party/libfuzzer" ] + + if (!defined(invoker.cflags)) { + cflags = [] + } + cflags += [ "-fsanitize=fuzzer" ] + } + } else { + not_needed(invoker, "*") + group(target_name) { + } + } +} diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 825c9f16..ac3125ce 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -13,6 +13,7 @@ # limitations under the License. import("../build/crashpad_buildconfig.gni") +import("../build/crashpad_fuzzer_test.gni") if (crashpad_is_in_chromium) { import("//build/config/compiler/compiler.gni") @@ -238,6 +239,17 @@ if (crashpad_is_win) { } } +fuzzer_test("elf_image_reader_fuzzer") { + sources = [ + "elf/elf_image_reader_fuzzer.cc", + ] + + deps = [ + ":snapshot", + "../third_party/mini_chromium:base", + ] +} + static_library("test_support") { testonly = true diff --git a/snapshot/elf/elf_image_reader_fuzzer.cc b/snapshot/elf/elf_image_reader_fuzzer.cc new file mode 100644 index 00000000..1686650b --- /dev/null +++ b/snapshot/elf/elf_image_reader_fuzzer.cc @@ -0,0 +1,75 @@ +// Copyright 2018 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 + +#include "base/logging.h" +#include "snapshot/elf/elf_image_reader.h" +#include "util/process/process_memory.h" + +using namespace crashpad; + +class FakeProcessMemory : public ProcessMemory { + public: + FakeProcessMemory(const uint8_t* data, size_t size, VMAddress fake_base) + : data_(data), size_(size), fake_base_(fake_base) {} + + ssize_t ReadUpTo(VMAddress address, + size_t size, + void* buffer) const override { + VMAddress offset_in_data = address - fake_base_; + if (offset_in_data > size_) + return -1; + ssize_t read_size = std::min(size_ - offset_in_data, size); + memcpy(buffer, &data_[offset_in_data], read_size); + return read_size; + } + + private: + const uint8_t* data_; + size_t size_; + VMAddress fake_base_; +}; + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + // Swallow all logs to avoid spam. + logging::SetLogMessageHandler( + [](logging::LogSeverity, const char*, int, size_t, const std::string&) { + return true; + }); + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + constexpr size_t kBase = 0x10000; + FakeProcessMemory process_memory(data, size, kBase); + ProcessMemoryRange process_memory_range; + process_memory_range.Initialize(&process_memory, true, kBase, size); + + ElfImageReader reader; + if (!reader.Initialize(process_memory_range, kBase)) + return 0; + + ElfImageReader::NoteReader::Result result; + std::string note_name; + std::string note_desc; + ElfImageReader::NoteReader::NoteType note_type; + auto notes = reader.Notes(-1); + while ((result = notes->NextNote(¬e_name, ¬e_type, ¬e_desc)) == + ElfImageReader::NoteReader::Result::kSuccess) { + LOG(ERROR) << note_name << note_type << note_desc; + } + + return 0; +} diff --git a/snapshot/elf/elf_image_reader_fuzzer_corpus/crashpad_snapshot_test_both_dt_hash_styles.so b/snapshot/elf/elf_image_reader_fuzzer_corpus/crashpad_snapshot_test_both_dt_hash_styles.so new file mode 100755 index 0000000000000000000000000000000000000000..4dc7cdbe580c89810de838820baf6cbddc89d9ae GIT binary patch literal 8544 zcmeHMU2Ggz6~61W6GNQ!HiX9cLD^PGNvJY7aom`O5XX+4F||`SIH(mwPfv4Qn%(>^g=bU@)o%?(Civz=hO_7K~imHz(<`!CAq*n?O+ayDzSM{iM^!|X_ zp=DdviUPTxyw`<5l}c!U>sUumWus<^CUgdLp~=Oc6AC?MSx?2>aurLs8KD(v3aB(g zjoxxn%_RBz_qgJIdeDUxD9dtsg7kEKF8hw}xJaCm`Wm$_A?*Scx7AM4e;+*?=waWv z?kGK+SY^G=t-YlClr&2hw9&hr9_jj;@TOYM&7h6^{opsc)hDX%^8@dK=Qgn(`+@KM z)x|_a?V5j>)fwk@vcG5t*U!FlVxLg2-*+zm^Q&*gu3dUN_S?@qx#OpQ`sU}a|MT?b z{(;wSf1&Lw+vgtJ`{VAB!C$akSo$G>XtTHceqs_{K=LmU4fBE9l@J*(`4ll>_NN-) z2MABl<4?~K5N2nX@CW_EBD;xt^QLz|5N^}6iAKq)ZvcIi8W2|k^WquIvdebCB6|+<=hCHemTJpq z%X!;LW~^i;ojhxu>mdP>%{ru4vXaGfR?kG%q0CHysxQMvy~uQOt-!I#OfqxUnzXYy zHs&^mjk2|5!6}u4Fj>%GaoW=L*Nb{lmds@*l18~`Jff_0%64p0ohX;xst78iRI+53 zGt+j;Di`eOa;E55PP*(^6GbOur5r0mr6fL=OP9%xIx#%d*KZv#4jA3FJeEnlCSlY> zRV3usq#o8|!h8bN zyM1Rb<(SW~%5tSjY4@AVv#ZNDHUCHExoegeHUB&2+2!S{n*R;++(paJYyQi`PhKWo zC-c&g>ja!OFST4FYJOzZyl~6eO0M-WORjA^Q>{!I?K_{=@@HL1I5I?HYUab{`6I8dqugosr_H>0 zWE0^_N2m!)CcR#6d4Tu`yR*QK{pALkJno90S)L>bmmf`;7n^tQAqn$B^R9)KSIK5% z4LjVtYo>Ys;!`)4zI(S?ZQrRzsXeItItcqT zJu8IoBdU(Y;*+sfdb@Sb64XpS?3J>qSgZDh2g(k@_4$CLA0zx;y4M5udf?sl0QOJp zm)IY%A7anpJ(^R@C2B2G(dMW*Aj*a@0r5o9n;G)6%7RaZjxX|EQGzLwK}H%>MsKED>7QL(=y`IHj4w*+f^@ zfu61&y&1rAkn(vGRIOJSC-w5J z3gf9B58lVPJW4m?xb$zm{0220inrC`HTCkrd!TDSR*Qqw%eSjICX}ZjN;gEzZx2j< z-ls6H>v6g%hm5NzMK*Aru9v4-DD$%(f4}mNFD{R&;QiCZgZE4q58iKG9IF>|ZsKv< ztlqAI2jz9Ji6Rwc?th10mM45=^zKwY@sPSNb=EV-yZP=O9@^OXx z5AbhOCd_}1cW6Il!NNNJqTrVW$CPW-55FS$2t{aK2pK0~_5MN22fyQ+QZLqveZ+Hm zi=GHaT72VYRhI_G?FMo*5;7h*ju7G1-8ZiG5l%eWPDs4tJA{YTdsXE9ah9JmPUFWPm-(fZ5BgaV`Eg%A+}3!|&(#L_R@ykj{Olq; z-V~g7CgVZ#>MY?8hTwOo-mv%-#VVY#Gc!49B-L7=$8z#kl0!Wl7fBVZsa$cw&Qa{5 zNI@8TW>zJO`RQERNvDiMo%{EP3+WJ&WtU3!97RQ((wv$s+4;1Un#t$qNX5hHz)qd0 zj*&QX)9JE~ofyfa;wX}lFQ#U4X)8&dC^x862U*tOsbeDp*1*Z*9K^Cx%C(Cv|o1|)s z4qGIr+)$uD(&z>rxr+??63U>Nlc(7bGDZIDWebFW~#c z-|37D{c*{FE;P^=SnZ|^-wB{!kqqc7wC-@?`Z$aFah~UWiG71U&WWJ7caVrqVh*gL zp`-W?0DYV%K_3!Hv>$SyeN@hO3h+30f_93&-+zJC6O`dH=;M3}x*~$8-`{@zAA|MT z4^BA8g3h|I?jycS`SsI;ai8G0jC&*~?ptC;*M&UOaeBP^I2VI*xi?|Y0X<8FUVWUO zLE$fKfCuG?;MK?Z4;1%g=%Zfn^FDptn?dE+1@S=p7epW5uhIXOFu>o+o4!sA`wxAb z??JD4g(;7A1LpH2pK&oha1X%$nwTe$hd$`{sG!}WkNd)^96%x0NdGDo@Yq59wR52a zDk|JKN@zFi;X4$mj76Z2`$vVHqLk3@wHNe3uTha#zjmIK3VPR6bq;!@6`%9Pu@l?pi<6LRQj}Fm#3kU3ohGhB6Y4vEWDVz}P3=~waaL#F**>_B zaJNV8h!0Cnk=){90TSU41plETK;;im_;%zdAWZWTqY6!FIANj2mVrWOl3+WIH58B{ zp(ZQ9po$};5OyrGU0B(Ll^yU^6&MiXPBf=e+2VNfczauO+cCY6(~rt= zvwzk-eP%%Rgn86R;+S{Q#cP1u|MbZ}j9oANBJ`{9n}^?c<-2=t9r|67ZE;ccKQ8U0 zsd;GcC9cVX?xh5ELtoH*4D;~YglP_s$IX#cHl9vhN|MM;X2FW*Ei)5O zWyR@OZ?w}qsvp&l3$wSs&rBrq$%{c1<&^%Wx0aKn&!5Z3chmR8&mJp8ZT8JtkZ-r*UpR5@uNhQ4j!Z5 z#{C^sYogl7Ec&^6m}>JQdgK1fl4`x%JN^0vWBPB#)UD+w`(rcJpQd3LGY$8s*_b)? zT8YZ;pKjO0dyV_MB=s%{1IA4C9YW*8iZOlLdWb@KkwprnVX(AV_CGk!QIutJ`D0d` zIsSd==FlqH8`oC-#`LmrZRrWacf)vn)e4crrd@tqQ<26(zeEa;5^FHP^b&|T^I~61R0pWSV z>x5}S-n*Zt$loG-hwuR1IexBi@m)G6eB(jizWSQlSzk?%d487!$?hYppo+J?5C^E8 z^tZUJA zh2lsu84U!#S``h1UiNnfw3#i@K=>=wMxf=(Ta7^bR83!?BOhpw1X?12a5SKi9_dE| zwQ^tolFaAGPv!n%mz!mC1~zA4a|SkNU~>lk>lsilE#!a5?SS>l@zpJMr9sJ;l^s48 zk!S8x?RZCWKF$UCCckUBp#2}Kr5wvw)XNRIpP`aQyi>2L_U)`pB|x8!Kq&@unwI3R)O{6Ch!#9Zlp#MC zn7`=a!-~iAPpQ-C7=NRb$_wlz%AJ1zk($AZ+vVL9NCEn+8fBiHeO*Gy~wcfCecM`8jU(TOr zCBJ?>93eg^_N-eE4a7Hz_4}et@=o6@4JbeE^)^Jjwq9L)miQf2djy_)Q28SBqP*|0 zU1x~r@dnj+dyNJ|Rn)@q9i$VJ5RyenO1oF$2BKN5{~vsZa=H(V&nP~ZQkE}4j1ahR0P`Z_ushkYh%*#~;0>pa~v6NQ{P z7SAU5%l6oZC+ib;0l{A_y>O;j89RsHlLT70#UKkU4Vj@faY_#%r z%jc5$LMoSa2qv}Vlj%4c2we_NznHf4(VQi8D>+WTyi7j;Vz)mLO9|wyC{B zYzx%MZ#Os#)ry&iEm^ z_|Xn~D` zRaF7v0G6L$JWsGkKK%@FoD0Jqc^eRUJ*mjTJ}S!v5;*6EJ@PvszE==G^njlsd(Pn@ zBM$^>sxx=|iWZls!gC7y6_wZn=aeG)caQ%vwey(pd`Dgh+@tIP?)!JkWsm$5i2nn? z9y0Jnm;IvZ0El>D4;lD%mp$HBz^}P(82W|3Z@KJ|_X6?#1%3DU?fi(}Wp+V+40P*5 z27XWVkNbx_8h8>ND}){BpSbLiZv)*M9`gqK-%uq$7rqCPhvENnZhgqW-;-@)g+0Ej zHFin`dbU)S!T*^WcQ^$9!N$Ct^LZ!zrgJ#dqQq7t@Y?HBgIMQW+E zU;A!Q9pE_u2e1R)aoHo^3n_cV>9&XbcdD>2#E-lxtPIf)^j*tYyhjbpwLn23?D70W zGwdr7YY#e%hU$MoJvjJ1!#N}3!MyPPKp9P<8;9 GJMnK#!s$K$ literal 0 HcmV?d00001 diff --git a/third_party/libfuzzer/BUILD.gn b/third_party/libfuzzer/BUILD.gn new file mode 100644 index 00000000..ac815dad --- /dev/null +++ b/third_party/libfuzzer/BUILD.gn @@ -0,0 +1,49 @@ +# Copyright 2018 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. + +source_set("libfuzzer") { + if (crashpad_use_libfuzzer) { + sources = [ + "src/FuzzerClangCounters.cpp", + "src/FuzzerCrossOver.cpp", + "src/FuzzerDriver.cpp", + "src/FuzzerExtFunctionsDlsym.cpp", + "src/FuzzerExtFunctionsDlsymWin.cpp", + "src/FuzzerExtFunctionsWeak.cpp", + "src/FuzzerExtFunctionsWeakAlias.cpp", + "src/FuzzerExtraCounters.cpp", + "src/FuzzerIO.cpp", + "src/FuzzerIOPosix.cpp", + "src/FuzzerIOWindows.cpp", + "src/FuzzerLoop.cpp", + "src/FuzzerMain.cpp", + "src/FuzzerMerge.cpp", + "src/FuzzerMutate.cpp", + "src/FuzzerSHA1.cpp", + "src/FuzzerShmemPosix.cpp", + "src/FuzzerShmemWindows.cpp", + "src/FuzzerTracePC.cpp", + "src/FuzzerUtil.cpp", + "src/FuzzerUtilDarwin.cpp", + "src/FuzzerUtilLinux.cpp", + "src/FuzzerUtilPosix.cpp", + "src/FuzzerUtilWindows.cpp", + ] + + configs -= [ + "//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors", + "//build:crashpad_fuzzer_flags", + ] + } +} From a42b5269b4b6e6f7b3379bec1f87068eca5c8a87 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Mon, 11 Jun 2018 09:38:24 -0700 Subject: [PATCH 299/326] Add ProcessSnapshotSanitized A ProcessSnapshotSanitized enables filtering possibly sensitive information from a snapshot. WebView has different privacy constraints than Chrome and needs to avoid collecting data in annotations or from stack memory that may contain PII. This CL enables: 1. Filtering annotations by name using a whitelist. 2. Filtering for crashes which reference a particular module. 3. Redacting non-essential information from stack memory. This CL does not provide a client interface to enable sanitization. Bug: crashpad:30 Change-Id: I8944c70fdcca6d6d4b7955d983320909bf871254 Reviewed-on: https://chromium-review.googlesource.com/1070472 Commit-Queue: Joshua Peraza Reviewed-by: Scott Graham --- snapshot/BUILD.gn | 9 + snapshot/cpu_context.cc | 14 + snapshot/cpu_context.h | 3 + .../sanitized/memory_snapshot_sanitized.cc | 107 +++++++ .../sanitized/memory_snapshot_sanitized.h | 74 +++++ .../sanitized/module_snapshot_sanitized.cc | 132 +++++++++ .../sanitized/module_snapshot_sanitized.h | 75 +++++ .../sanitized/process_snapshot_sanitized.cc | 265 +++++++++++++++++ .../sanitized/process_snapshot_sanitized.h | 105 +++++++ .../process_snapshot_sanitized_test.cc | 275 ++++++++++++++++++ .../sanitized/thread_snapshot_sanitized.cc | 63 ++++ .../sanitized/thread_snapshot_sanitized.h | 59 ++++ snapshot/snapshot.gyp | 9 + snapshot/snapshot_test.gyp | 2 + util/BUILD.gn | 15 +- util/misc/range_set.cc | 55 ++++ util/misc/range_set.h | 51 ++++ util/misc/range_set_test.cc | 116 ++++++++ util/util.gyp | 2 + util/util_test.gyp | 1 + 20 files changed, 1425 insertions(+), 7 deletions(-) create mode 100644 snapshot/sanitized/memory_snapshot_sanitized.cc create mode 100644 snapshot/sanitized/memory_snapshot_sanitized.h create mode 100644 snapshot/sanitized/module_snapshot_sanitized.cc create mode 100644 snapshot/sanitized/module_snapshot_sanitized.h create mode 100644 snapshot/sanitized/process_snapshot_sanitized.cc create mode 100644 snapshot/sanitized/process_snapshot_sanitized.h create mode 100644 snapshot/sanitized/process_snapshot_sanitized_test.cc create mode 100644 snapshot/sanitized/thread_snapshot_sanitized.cc create mode 100644 snapshot/sanitized/thread_snapshot_sanitized.h create mode 100644 util/misc/range_set.cc create mode 100644 util/misc/range_set.h create mode 100644 util/misc/range_set_test.cc diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index ac3125ce..f8a055bd 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -122,6 +122,14 @@ static_library("snapshot") { "linux/system_snapshot_linux.h", "linux/thread_snapshot_linux.cc", "linux/thread_snapshot_linux.h", + "sanitized/memory_snapshot_sanitized.cc", + "sanitized/memory_snapshot_sanitized.h", + "sanitized/module_snapshot_sanitized.cc", + "sanitized/module_snapshot_sanitized.h", + "sanitized/process_snapshot_sanitized.cc", + "sanitized/process_snapshot_sanitized.h", + "sanitized/thread_snapshot_sanitized.cc", + "sanitized/thread_snapshot_sanitized.h", ] } @@ -329,6 +337,7 @@ source_set("snapshot_test") { "linux/exception_snapshot_linux_test.cc", "linux/process_reader_linux_test.cc", "linux/system_snapshot_linux_test.cc", + "sanitized/process_snapshot_sanitized_test.cc", ] } else { sources += [ "crashpad_info_client_options_test.cc" ] diff --git a/snapshot/cpu_context.cc b/snapshot/cpu_context.cc index 6ce059e7..553a1a5d 100644 --- a/snapshot/cpu_context.cc +++ b/snapshot/cpu_context.cc @@ -190,4 +190,18 @@ uint64_t CPUContext::StackPointer() const { } } +bool CPUContext::Is64Bit() const { + switch (architecture) { + case kCPUArchitectureX86_64: + case kCPUArchitectureARM64: + return true; + case kCPUArchitectureX86: + case kCPUArchitectureARM: + return false; + default: + NOTREACHED(); + return false; + } +} + } // namespace crashpad diff --git a/snapshot/cpu_context.h b/snapshot/cpu_context.h index 9cb2aec9..b104217d 100644 --- a/snapshot/cpu_context.h +++ b/snapshot/cpu_context.h @@ -323,6 +323,9 @@ struct CPUContext { //! context structure. uint64_t StackPointer() const; + //! \brief Returns `true` if this context is for a 64-bit architecture. + bool Is64Bit() const; + //! \brief The CPU architecture of a context structure. This field controls //! the expression of the union. CPUArchitecture architecture; diff --git a/snapshot/sanitized/memory_snapshot_sanitized.cc b/snapshot/sanitized/memory_snapshot_sanitized.cc new file mode 100644 index 00000000..e71629df --- /dev/null +++ b/snapshot/sanitized/memory_snapshot_sanitized.cc @@ -0,0 +1,107 @@ +// Copyright 2018 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/sanitized/memory_snapshot_sanitized.h" + +#include + +namespace crashpad { +namespace internal { + +namespace { + +class MemorySanitizer : public MemorySnapshot::Delegate { + public: + MemorySanitizer(MemorySnapshot::Delegate* delegate, + RangeSet* ranges, + VMAddress address, + bool is_64_bit) + : delegate_(delegate), + ranges_(ranges), + address_(address), + is_64_bit_(is_64_bit) {} + + ~MemorySanitizer() = default; + + bool MemorySnapshotDelegateRead(void* data, size_t size) override { + if (is_64_bit_) { + Sanitize(data, size); + } else { + Sanitize(data, size); + } + return delegate_->MemorySnapshotDelegateRead(data, size); + } + + private: + template + void Sanitize(void* data, size_t size) { + const Pointer defaced = + static_cast(MemorySnapshotSanitized::kDefaced); + + // Sanitize up to a word-aligned address. + const size_t aligned_offset = + ((address_ + sizeof(Pointer) - 1) & ~(sizeof(Pointer) - 1)) - address_; + memcpy(data, &defaced, aligned_offset); + + // Sanitize words that aren't small and don't look like pointers. + size_t word_count = (size - aligned_offset) / sizeof(Pointer); + auto words = + reinterpret_cast(static_cast(data) + aligned_offset); + for (size_t index = 0; index < word_count; ++index) { + if (words[index] > MemorySnapshotSanitized::kSmallWordMax && + !ranges_->Contains(words[index])) { + words[index] = defaced; + } + } + + // Sanitize trailing bytes beyond the word-sized items. + const size_t sanitized_bytes = + aligned_offset + word_count * sizeof(Pointer); + memcpy(static_cast(data) + sanitized_bytes, + &defaced, + size - sanitized_bytes); + } + + MemorySnapshot::Delegate* delegate_; + RangeSet* ranges_; + VMAddress address_; + bool is_64_bit_; + + DISALLOW_COPY_AND_ASSIGN(MemorySanitizer); +}; + +} // namespace + +MemorySnapshotSanitized::MemorySnapshotSanitized(const MemorySnapshot* snapshot, + RangeSet* ranges, + bool is_64_bit) + : snapshot_(snapshot), ranges_(ranges), is_64_bit_(is_64_bit) {} + +MemorySnapshotSanitized::~MemorySnapshotSanitized() = default; + +uint64_t MemorySnapshotSanitized::Address() const { + return snapshot_->Address(); +} + +size_t MemorySnapshotSanitized::Size() const { + return snapshot_->Size(); +} + +bool MemorySnapshotSanitized::Read(Delegate* delegate) const { + MemorySanitizer sanitizer(delegate, ranges_, Address(), is_64_bit_); + return snapshot_->Read(&sanitizer); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/sanitized/memory_snapshot_sanitized.h b/snapshot/sanitized/memory_snapshot_sanitized.h new file mode 100644 index 00000000..41e324f5 --- /dev/null +++ b/snapshot/sanitized/memory_snapshot_sanitized.h @@ -0,0 +1,74 @@ +// Copyright 2018 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_SANITIZED_MEMORY_SNAPSHOT_SANITIZED_H_ +#define CRASHPAD_SNAPSHOT_SANITIZED_MEMORY_SNAPSHOT_SANITIZED_H_ + +#include + +#include "snapshot/memory_snapshot.h" +#include "util/misc/range_set.h" + +namespace crashpad { +namespace internal { + +//! \brief A MemorySnapshot which wraps and filters sensitive information from +//! another MemorySnapshot. +//! +//! This class redacts all data from the wrapped MemorySnapshot unless: +//! 1. The data is pointer aligned and points into a whitelisted address range. +//! 2. The data is pointer aligned and is a small integer. +class MemorySnapshotSanitized final : public MemorySnapshot { + public: + //! \brief Redacted data is replaced with this value, casted to the + //! appropriate size for a pointer in the target process. + static constexpr uint64_t kDefaced = 0x0defaced0defaced; + + //! \brief Pointer-aligned data smaller than this value is not redacted. + static constexpr uint64_t kSmallWordMax = 4096; + + //! \brief Constructs this object. + //! + //! \param[in] snapshot The MemorySnapshot to sanitize. + //! \param[in] ranges A set of whitelisted address ranges. + //! \param[in] is_64_bit `true` if this memory is for a 64-bit process. + MemorySnapshotSanitized(const MemorySnapshot* snapshot, + RangeSet* ranges, + bool is_64_bit); + + ~MemorySnapshotSanitized() override; + + // MemorySnapshot: + + uint64_t Address() const override; + size_t Size() const override; + bool Read(Delegate* delegate) const override; + + const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const override { + return nullptr; + } + + private: + const MemorySnapshot* snapshot_; + RangeSet* ranges_; + bool is_64_bit_; + + DISALLOW_COPY_AND_ASSIGN(MemorySnapshotSanitized); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SANITIZED_MEMORY_SNAPSHOT_SANITIZED_H_ diff --git a/snapshot/sanitized/module_snapshot_sanitized.cc b/snapshot/sanitized/module_snapshot_sanitized.cc new file mode 100644 index 00000000..94cb3d00 --- /dev/null +++ b/snapshot/sanitized/module_snapshot_sanitized.cc @@ -0,0 +1,132 @@ +// Copyright 2018 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/sanitized/module_snapshot_sanitized.h" + +namespace crashpad { +namespace internal { + +namespace { + +bool KeyIsInWhitelist(const std::string& name, + const std::vector& whitelist) { + for (const auto& key : whitelist) { + if (name == key) { + return true; + } + } + return false; +} + +} // namespace + +ModuleSnapshotSanitized::ModuleSnapshotSanitized( + const ModuleSnapshot* snapshot, + const std::vector* annotations_whitelist) + : snapshot_(snapshot), annotations_whitelist_(annotations_whitelist) {} + +ModuleSnapshotSanitized::~ModuleSnapshotSanitized() = default; + +std::string ModuleSnapshotSanitized::Name() const { + return snapshot_->Name(); +} + +uint64_t ModuleSnapshotSanitized::Address() const { + return snapshot_->Address(); +} + +uint64_t ModuleSnapshotSanitized::Size() const { + return snapshot_->Size(); +} + +time_t ModuleSnapshotSanitized::Timestamp() const { + return snapshot_->Timestamp(); +} + +void ModuleSnapshotSanitized::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + snapshot_->FileVersion(version_0, version_1, version_2, version_3); +} + +void ModuleSnapshotSanitized::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + snapshot_->SourceVersion(version_0, version_1, version_2, version_3); +} + +ModuleSnapshot::ModuleType ModuleSnapshotSanitized::GetModuleType() const { + return snapshot_->GetModuleType(); +} + +void ModuleSnapshotSanitized::UUIDAndAge(crashpad::UUID* uuid, + uint32_t* age) const { + snapshot_->UUIDAndAge(uuid, age); +} + +std::string ModuleSnapshotSanitized::DebugFileName() const { + return snapshot_->DebugFileName(); +} + +std::vector ModuleSnapshotSanitized::AnnotationsVector() const { + // TODO(jperaza): If/when AnnotationsVector() begins to be used, determine + // whether and how the content should be sanitized. + DCHECK(snapshot_->AnnotationsVector().empty()); + return std::vector(); +} + +std::map +ModuleSnapshotSanitized::AnnotationsSimpleMap() const { + std::map annotations = + snapshot_->AnnotationsSimpleMap(); + if (annotations_whitelist_) { + for (auto kv = annotations.begin(); kv != annotations.end(); ++kv) { + if (!KeyIsInWhitelist(kv->first, *annotations_whitelist_)) { + annotations.erase(kv); + } + } + } + return annotations; +} + +std::vector ModuleSnapshotSanitized::AnnotationObjects() + const { + std::vector annotations = snapshot_->AnnotationObjects(); + if (annotations_whitelist_) { + std::vector whitelisted; + for (const auto& anno : annotations) { + if (KeyIsInWhitelist(anno.name, *annotations_whitelist_)) { + whitelisted.push_back(anno); + } + } + annotations.swap(whitelisted); + } + return annotations; +} + +std::set> ModuleSnapshotSanitized::ExtraMemoryRanges() + const { + DCHECK(snapshot_->ExtraMemoryRanges().empty()); + return std::set>(); +} + +std::vector +ModuleSnapshotSanitized::CustomMinidumpStreams() const { + return snapshot_->CustomMinidumpStreams(); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/sanitized/module_snapshot_sanitized.h b/snapshot/sanitized/module_snapshot_sanitized.h new file mode 100644 index 00000000..4f375ce0 --- /dev/null +++ b/snapshot/sanitized/module_snapshot_sanitized.h @@ -0,0 +1,75 @@ +// Copyright 2018 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_SANITIZED_MODULE_SNAPSHOT_SANITIZED_H_ +#define CRASHPAD_SNAPSHOT_SANITIZED_MODULE_SNAPSHOT_SANITIZED_H_ + +#include +#include + +#include "base/macros.h" +#include "snapshot/module_snapshot.h" + +namespace crashpad { +namespace internal { + +//! \brief A ModuleSnapshot which wraps and filters sensitive information from +//! another ModuleSnapshot. +class ModuleSnapshotSanitized final : public ModuleSnapshot { + public: + //! \brief Constructs this object. + //! + //! \param[in] snapshot The ModuleSnapshot to sanitize. + //! \param[in] annotations_whitelist A list of annotation names to allow to be + //! returned by AnnotationsSimpleMap() or AnnotationObjects(). If + //! `nullptr`, all annotations will be returned. + ModuleSnapshotSanitized( + const ModuleSnapshot* snapshot, + const std::vector* annotations_whitelist); + ~ModuleSnapshotSanitized() override; + + // 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 UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector AnnotationsVector() const override; + std::map AnnotationsSimpleMap() const override; + std::vector AnnotationObjects() const override; + std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; + + private: + const ModuleSnapshot* snapshot_; + const std::vector* annotations_whitelist_; + + DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotSanitized); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SANITIZED_MODULE_SNAPSHOT_SANITIZED_H_ diff --git a/snapshot/sanitized/process_snapshot_sanitized.cc b/snapshot/sanitized/process_snapshot_sanitized.cc new file mode 100644 index 00000000..76caeb5a --- /dev/null +++ b/snapshot/sanitized/process_snapshot_sanitized.cc @@ -0,0 +1,265 @@ +// Copyright 2018 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/sanitized/process_snapshot_sanitized.h" + +#include + +#include "snapshot/cpu_context.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +namespace { + +class StackReferencesAddressRange : public MemorySnapshot::Delegate { + public: + // Returns true if stack contains a pointer aligned word in the range [low, + // high). The search starts at the first pointer aligned address greater than + // stack_pointer. + bool CheckStack(const MemorySnapshot* stack, + VMAddress stack_pointer, + VMAddress low, + VMAddress high, + bool is_64_bit) { + stack_ = stack; + stack_pointer_ = stack_pointer; + low_ = low; + high_ = high; + is_64_bit_ = is_64_bit; + return stack_->Read(this); + } + + // MemorySnapshot::Delegate + bool MemorySnapshotDelegateRead(void* data, size_t size) { + return is_64_bit_ ? ScanStackForPointers(data, size) + : ScanStackForPointers(data, size); + } + + private: + template + bool ScanStackForPointers(void* data, size_t size) { + size_t sp_offset; + if (!AssignIfInRange(&sp_offset, stack_pointer_ - stack_->Address())) { + return false; + } + const size_t aligned_sp_offset = + (sp_offset + sizeof(Pointer) - 1) & ~(sizeof(Pointer) - 1); + + auto words = reinterpret_cast(static_cast(data) + + aligned_sp_offset); + size_t word_count = (size - aligned_sp_offset) / sizeof(Pointer); + for (size_t index = 0; index < word_count; ++index) { + if (words[index] >= low_ && words[index] < high_) { + return true; + } + } + + return false; + } + + VMAddress stack_pointer_; + VMAddress low_; + VMAddress high_; + const MemorySnapshot* stack_; + bool is_64_bit_; +}; + +} // namespace + +ProcessSnapshotSanitized::ProcessSnapshotSanitized() = default; + +ProcessSnapshotSanitized::~ProcessSnapshotSanitized() = default; + +bool ProcessSnapshotSanitized::Initialize( + const ProcessSnapshot* snapshot, + const std::vector* annotations_whitelist, + VMAddress target_module_address, + bool sanitize_stacks) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + snapshot_ = snapshot; + annotations_whitelist_ = annotations_whitelist; + sanitize_stacks_ = sanitize_stacks; + + if (target_module_address) { + const ExceptionSnapshot* exception = snapshot_->Exception(); + if (!exception) { + return false; + } + + const ThreadSnapshot* exc_thread = nullptr; + for (const auto thread : snapshot_->Threads()) { + if (thread->ThreadID() == exception->ThreadID()) { + exc_thread = thread; + break; + } + } + if (!exc_thread) { + return false; + } + + const ModuleSnapshot* target_module = nullptr; + for (const auto module : snapshot_->Modules()) { + if (target_module_address >= module->Address() && + target_module_address < module->Address() + module->Size()) { + target_module = module; + break; + } + } + if (!target_module) { + return false; + } + + // Only allow the snapshot if the program counter or some address on the + // stack points into the target module. + VMAddress pc = exception->Context()->InstructionPointer(); + VMAddress module_address_low = target_module->Address(); + VMAddress module_address_high = module_address_low + target_module->Size(); + if ((pc < module_address_low || pc >= module_address_high) && + !StackReferencesAddressRange().CheckStack( + exc_thread->Stack(), + exception->Context()->StackPointer(), + module_address_low, + module_address_high, + exception->Context()->Is64Bit())) { + return false; + } + } + + if (annotations_whitelist_) { + for (const auto module : snapshot_->Modules()) { + modules_.emplace_back(std::make_unique( + module, annotations_whitelist_)); + } + } + + if (sanitize_stacks_) { + for (const auto module : snapshot_->Modules()) { + address_ranges_.Insert(module->Address(), module->Size()); + } + + for (const auto thread : snapshot_->Threads()) { + address_ranges_.Insert(thread->Stack()->Address(), + thread->Stack()->Size()); + threads_.emplace_back(std::make_unique( + thread, &address_ranges_)); + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +pid_t ProcessSnapshotSanitized::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->ProcessID(); +} + +pid_t ProcessSnapshotSanitized::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->ParentProcessID(); +} + +void ProcessSnapshotSanitized::SnapshotTime(timeval* snapshot_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + snapshot_->SnapshotTime(snapshot_time); +} + +void ProcessSnapshotSanitized::ProcessStartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + snapshot_->ProcessStartTime(start_time); +} + +void ProcessSnapshotSanitized::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + snapshot_->ProcessCPUTimes(user_time, system_time); +} + +void ProcessSnapshotSanitized::ReportID(UUID* report_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + snapshot_->ReportID(report_id); +} + +void ProcessSnapshotSanitized::ClientID(UUID* client_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + snapshot_->ClientID(client_id); +} + +const std::map& +ProcessSnapshotSanitized::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->AnnotationsSimpleMap(); +} + +const SystemSnapshot* ProcessSnapshotSanitized::System() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->System(); +} + +std::vector ProcessSnapshotSanitized::Threads() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!sanitize_stacks_) { + return snapshot_->Threads(); + } + + std::vector threads; + for (const auto& thread : threads_) { + threads.push_back(thread.get()); + } + return threads; +} + +std::vector ProcessSnapshotSanitized::Modules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!annotations_whitelist_) { + return snapshot_->Modules(); + } + + std::vector modules; + for (const auto& module : modules_) { + modules.push_back(module.get()); + } + return modules; +} + +std::vector ProcessSnapshotSanitized::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->UnloadedModules(); +} + +const ExceptionSnapshot* ProcessSnapshotSanitized::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->Exception(); +} + +std::vector +ProcessSnapshotSanitized::MemoryMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->MemoryMap(); +} + +std::vector ProcessSnapshotSanitized::Handles() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->Handles(); +} + +std::vector ProcessSnapshotSanitized::ExtraMemory() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->ExtraMemory(); +} + +} // namespace crashpad diff --git a/snapshot/sanitized/process_snapshot_sanitized.h b/snapshot/sanitized/process_snapshot_sanitized.h new file mode 100644 index 00000000..f5cf5fa6 --- /dev/null +++ b/snapshot/sanitized/process_snapshot_sanitized.h @@ -0,0 +1,105 @@ +// Copyright 2018 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_SANITIZED_PROCESS_SNAPSHOT_SANITIZED_H_ +#define CRASHPAD_SNAPSHOT_SANITIZED_PROCESS_SNAPSHOT_SANITIZED_H_ + +#include +#include +#include + +#include "base/macros.h" +#include "snapshot/exception_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/sanitized/module_snapshot_sanitized.h" +#include "snapshot/sanitized/thread_snapshot_sanitized.h" +#include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" +#include "util/misc/address_types.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/range_set.h" + +namespace crashpad { + +//! \brief A ProcessSnapshot which wraps and filters sensitive information from +//! another ProcessSnapshot. +class ProcessSnapshotSanitized final : public ProcessSnapshot { + public: + ProcessSnapshotSanitized(); + ~ProcessSnapshotSanitized() override; + + //! \brief Initializes this object. + //! + //! This method must be successfully called before calling any other method on + //! this object. + //! + //! \param[in] snapshot The ProcessSnapshot to sanitize. + //! \param[in] annotations_whitelist A list of annotations names to allow to + //! be returned by AnnotationsSimpleMap() or from this object's module + //! snapshots. If `nullptr`, all annotations will be returned. + //! \param[in] target_module_address An address in the target process' + //! address space within the bounds of a module to target. If the + //! crashing thread's context and stack do not contain any pointers into + //! this module's address range, this method will return `false`. If this + //! value is 0, this method will not check the context or stack for + //! references to any particular module. + //! \param[in] sanitize_stacks If `true`, the MemorySnapshots for each + //! thread's stack will be filtered using an + //! internal::StackSnapshotSanitized. + //! \return `false` if \a snapshot does not meet sanitization requirements and + //! should be filtered entirely. Otherwise `true`. + bool Initialize(const ProcessSnapshot* snapshot, + const std::vector* annotations_whitelist, + VMAddress target_module_address, + bool sanitize_stacks); + + // 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; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector Threads() const override; + std::vector Modules() const override; + std::vector UnloadedModules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector MemoryMap() const override; + std::vector Handles() const override; + std::vector ExtraMemory() const override; + + private: + // Only used when annotations_whitelist_ != nullptr. + std::vector> modules_; + + // Only used when sanitize_stacks_ == true. + std::vector> threads_; + + RangeSet address_ranges_; + const ProcessSnapshot* snapshot_; + const std::vector* annotations_whitelist_; + bool sanitize_stacks_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotSanitized); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SANITIZED_PROCESS_SNAPSHOT_SANITIZED_H_ diff --git a/snapshot/sanitized/process_snapshot_sanitized_test.cc b/snapshot/sanitized/process_snapshot_sanitized_test.cc new file mode 100644 index 00000000..485ef87f --- /dev/null +++ b/snapshot/sanitized/process_snapshot_sanitized_test.cc @@ -0,0 +1,275 @@ +// Copyright 2018 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/sanitized/process_snapshot_sanitized.h" + +#include "base/macros.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/multiprocess_exec.h" +#include "util/file/file_io.h" +#include "util/numeric/safe_assignment.h" + +#if defined(OS_LINUX) || defined(OS_ANDROID) +#include + +#include "snapshot/linux/process_snapshot_linux.h" +#include "util/linux/direct_ptrace_connection.h" +#include "util/linux/exception_information.h" +#include "util/posix/signals.h" +#endif + +namespace crashpad { +namespace test { +namespace { + +class ExceptionGenerator { + public: + static ExceptionGenerator* Get() { + static ExceptionGenerator* instance = new ExceptionGenerator(); + return instance; + } + + bool Initialize(FileHandle in, FileHandle out) { + in_ = in; + out_ = out; + return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr); + } + + private: + ExceptionGenerator() = default; + ~ExceptionGenerator() = delete; + + static void HandleCrash(int signo, siginfo_t* siginfo, void* context) { + auto state = Get(); + + ExceptionInformation info = {}; + info.siginfo_address = FromPointerCast(siginfo); + info.context_address = FromPointerCast(context); + info.thread_id = syscall(SYS_gettid); + + auto info_addr = FromPointerCast(&info); + ASSERT_TRUE(LoggingWriteFile(state->out_, &info_addr, sizeof(info_addr))); + + CheckedReadFileAtEOF(state->in_); + Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr); + } + + FileHandle in_; + FileHandle out_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionGenerator); +}; + +constexpr char kWhitelistedAnnotationName[] = "name_of_whitelisted_anno"; +constexpr char kWhitelistedAnnotationValue[] = "some_value"; +constexpr char kNonWhitelistedAnnotationName[] = "non_whitelisted_anno"; +constexpr char kNonWhitelistedAnnotationValue[] = "private_annotation"; +constexpr char kSensitiveStackData[] = "sensitive_stack_data"; + +struct ChildTestAddresses { + VMAddress string_address; + VMAddress module_address; + VMAddress non_module_address; + VMAddress code_pointer_address; + VMAddress code_pointer_value; +}; + +void ChildTestFunction() { + FileHandle in = StdioFileHandle(StdioStream::kStandardInput); + FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); + + static StringAnnotation<32> whitelisted_annotation( + kWhitelistedAnnotationName); + whitelisted_annotation.Set(kWhitelistedAnnotationValue); + + static StringAnnotation<32> non_whitelisted_annotation( + kNonWhitelistedAnnotationName); + non_whitelisted_annotation.Set(kNonWhitelistedAnnotationValue); + + char string_data[strlen(kSensitiveStackData) + 1]; + strcpy(string_data, kSensitiveStackData); + + void (*code_pointer)(void) = ChildTestFunction; + + ChildTestAddresses addrs = {}; + addrs.string_address = FromPointerCast(string_data); + addrs.module_address = FromPointerCast(ChildTestFunction); + addrs.non_module_address = FromPointerCast(&addrs); + addrs.code_pointer_address = FromPointerCast(&code_pointer); + addrs.code_pointer_value = FromPointerCast(code_pointer); + ASSERT_TRUE(LoggingWriteFile(out, &addrs, sizeof(addrs))); + + auto gen = ExceptionGenerator::Get(); + ASSERT_TRUE(gen->Initialize(in, out)); + + __builtin_trap(); +} + +CRASHPAD_CHILD_TEST_MAIN(ChildToBeSanitized) { + ChildTestFunction(); + NOTREACHED(); + return EXIT_SUCCESS; +} + +void ExpectAnnotations(ProcessSnapshot* snapshot, bool sanitized) { + bool found_whitelisted = false; + bool found_non_whitelisted = false; + for (auto module : snapshot->Modules()) { + for (const auto& anno : module->AnnotationObjects()) { + if (anno.name == kWhitelistedAnnotationName) { + found_whitelisted = true; + } else if (anno.name == kNonWhitelistedAnnotationName) { + found_non_whitelisted = true; + } + } + } + + EXPECT_TRUE(found_whitelisted); + if (sanitized) { + EXPECT_FALSE(found_non_whitelisted); + } else { + EXPECT_TRUE(found_non_whitelisted); + } +} + +class StackSanitizationChecker : public MemorySnapshot::Delegate { + public: + StackSanitizationChecker() = default; + ~StackSanitizationChecker() = default; + + void CheckStack(const MemorySnapshot* stack, + const ChildTestAddresses& addrs, + bool is_64_bit, + bool sanitized) { + stack_ = stack; + addrs_ = addrs; + is_64_bit_ = is_64_bit; + sanitized_ = sanitized; + EXPECT_TRUE(stack_->Read(this)); + } + + // MemorySnapshot::Delegate + bool MemorySnapshotDelegateRead(void* data, size_t size) override { + size_t pointer_offset; + if (!AssignIfInRange(&pointer_offset, + addrs_.code_pointer_address - stack_->Address())) { + ADD_FAILURE(); + return false; + } + + const auto data_c = static_cast(data); + VMAddress pointer_value; + if (is_64_bit_) { + pointer_value = *reinterpret_cast(data_c + pointer_offset); + } else { + pointer_value = *reinterpret_cast(data_c + pointer_offset); + } + EXPECT_EQ(pointer_value, addrs_.code_pointer_value); + + size_t string_offset; + if (!AssignIfInRange(&string_offset, + addrs_.string_address - stack_->Address())) { + ADD_FAILURE(); + return false; + } + + auto string = data_c + string_offset; + if (sanitized_) { + EXPECT_STRNE(string, kSensitiveStackData); + } else { + EXPECT_STREQ(string, kSensitiveStackData); + } + return true; + } + + private: + const MemorySnapshot* stack_; + ChildTestAddresses addrs_; + bool is_64_bit_; + bool sanitized_; +}; + +void ExpectStackData(ProcessSnapshot* snapshot, + const ChildTestAddresses& addrs, + bool sanitized) { + const ThreadSnapshot* crasher = nullptr; + for (const auto thread : snapshot->Threads()) { + if (thread->ThreadID() == snapshot->Exception()->ThreadID()) { + crasher = thread; + break; + } + } + ASSERT_TRUE(crasher); + + const MemorySnapshot* stack = crasher->Stack(); + StackSanitizationChecker().CheckStack( + stack, addrs, crasher->Context()->Is64Bit(), sanitized); +} + +class SanitizeTest : public MultiprocessExec { + public: + SanitizeTest() : MultiprocessExec() { + SetChildTestMainFunction("ChildToBeSanitized"); + SetExpectedChildTerminationBuiltinTrap(); + } + + ~SanitizeTest() = default; + + private: + void MultiprocessParent() { + ChildTestAddresses addrs = {}; + ASSERT_TRUE( + LoggingReadFileExactly(ReadPipeHandle(), &addrs, sizeof(addrs))); + + VMAddress exception_info_addr; + ASSERT_TRUE(LoggingReadFileExactly( + ReadPipeHandle(), &exception_info_addr, sizeof(exception_info_addr))); + + DirectPtraceConnection connection; + ASSERT_TRUE(connection.Initialize(ChildProcess())); + + ProcessSnapshotLinux snapshot; + ASSERT_TRUE(snapshot.Initialize(&connection)); + ASSERT_TRUE(snapshot.InitializeException(exception_info_addr)); + + ExpectAnnotations(&snapshot, /* sanitized= */ false); + ExpectStackData(&snapshot, addrs, /* sanitized= */ false); + + std::vector whitelist; + whitelist.push_back(kWhitelistedAnnotationName); + + ProcessSnapshotSanitized sanitized; + ASSERT_TRUE(sanitized.Initialize( + &snapshot, &whitelist, addrs.module_address, true)); + + ExpectAnnotations(&sanitized, /* sanitized= */ true); + ExpectStackData(&sanitized, addrs, /* sanitized= */ true); + + ProcessSnapshotSanitized screened_snapshot; + EXPECT_FALSE(screened_snapshot.Initialize( + &snapshot, nullptr, addrs.non_module_address, false)); + } + + DISALLOW_COPY_AND_ASSIGN(SanitizeTest); +}; + +TEST(ProcessSnapshotSanitized, Sanitize) { + SanitizeTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/snapshot/sanitized/thread_snapshot_sanitized.cc b/snapshot/sanitized/thread_snapshot_sanitized.cc new file mode 100644 index 00000000..186776ea --- /dev/null +++ b/snapshot/sanitized/thread_snapshot_sanitized.cc @@ -0,0 +1,63 @@ +// Copyright 2018 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/sanitized/thread_snapshot_sanitized.h" + +#include "snapshot/cpu_context.h" + +namespace crashpad { +namespace internal { + +ThreadSnapshotSanitized::ThreadSnapshotSanitized(const ThreadSnapshot* snapshot, + RangeSet* ranges) + : ThreadSnapshot(), + snapshot_(snapshot), + stack_(snapshot_->Stack(), ranges, snapshot_->Context()->Is64Bit()) {} + +ThreadSnapshotSanitized::~ThreadSnapshotSanitized() = default; + +const CPUContext* ThreadSnapshotSanitized::Context() const { + return snapshot_->Context(); +} + +const MemorySnapshot* ThreadSnapshotSanitized::Stack() const { + return &stack_; +} + +uint64_t ThreadSnapshotSanitized::ThreadID() const { + return snapshot_->ThreadID(); +} + +int ThreadSnapshotSanitized::SuspendCount() const { + return snapshot_->SuspendCount(); +} + +int ThreadSnapshotSanitized::Priority() const { + return snapshot_->Priority(); +} + +uint64_t ThreadSnapshotSanitized::ThreadSpecificDataAddress() const { + return snapshot_->ThreadSpecificDataAddress(); +} + +std::vector ThreadSnapshotSanitized::ExtraMemory() + const { + // TODO(jperaza): If/when ExtraMemory() is used, decide whether and how it + // should be sanitized. + DCHECK(snapshot_->ExtraMemory().empty()); + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/sanitized/thread_snapshot_sanitized.h b/snapshot/sanitized/thread_snapshot_sanitized.h new file mode 100644 index 00000000..081627a6 --- /dev/null +++ b/snapshot/sanitized/thread_snapshot_sanitized.h @@ -0,0 +1,59 @@ +// Copyright 2018 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_SANITIZED_THREAD_SNAPSHOT_SANITIZED_H_ +#define CRASHPAD_SNAPSHOT_SANITIZED_THREAD_SNAPSHOT_SANITIZED_H_ + +#include "snapshot/thread_snapshot.h" + +#include "snapshot/sanitized/memory_snapshot_sanitized.h" +#include "util/misc/range_set.h" + +namespace crashpad { +namespace internal { + +//! \brief A ThreadSnapshot which wraps and filters sensitive information from +//! another ThreadSnapshot. +class ThreadSnapshotSanitized final : public ThreadSnapshot { + public: + //! \brief Constructs this object. + //! + //! \param[in] snapshot The ThreadSnapshot to sanitize. + //! \param[in] ranges A set of address ranges with which to sanitize this + //! thread's stacks. \see internal::MemorySnapshotSanitized. + ThreadSnapshotSanitized(const ThreadSnapshot* snapshot, RangeSet* ranges); + + ~ThreadSnapshotSanitized() override; + + // ThreadSnapshot: + + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector ExtraMemory() const override; + + private: + const ThreadSnapshot* snapshot_; + MemorySnapshotSanitized stack_; + + DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotSanitized); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SANITIZED_THREAD_SNAPSHOT_SANITIZED_H_ diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index b199841b..3aad7bae 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -123,6 +123,14 @@ 'posix/timezone.cc', 'posix/timezone.h', 'process_snapshot.h', + 'sanitized/memory_snapshot_sanitized.cc', + 'sanitized/memory_snapshot_sanitized.h', + 'sanitized/module_snapshot_sanitized.cc', + 'sanitized/module_snapshot_sanitized.h', + 'sanitized/process_snapshot_sanitized.cc', + 'sanitized/process_snapshot_sanitized.h', + 'sanitized/thread_snapshot_sanitized.cc', + 'sanitized/thread_snapshot_sanitized.h', 'snapshot_constants.h', 'system_snapshot.h', 'thread_snapshot.h', @@ -176,6 +184,7 @@ 'sources/': [ ['exclude', '^elf/'], ['exclude', '^crashpad_types/'], + ['exclude', '^sanitized/'], ], }], ['target_arch!="ia32" and target_arch!="x64"', { diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index 6f0fc90e..c2ec97d3 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -92,6 +92,7 @@ 'mac/system_snapshot_mac_test.cc', 'minidump/process_snapshot_minidump_test.cc', 'posix/timezone_test.cc', + 'sanitized/process_snapshot_sanitized_test.cc', 'win/cpu_context_win_test.cc', 'win/exception_snapshot_win_test.cc', 'win/extra_memory_ranges_test.cc', @@ -145,6 +146,7 @@ 'sources/': [ ['exclude', '^elf/'], ['exclude', '^crashpad_types/'], + ['exclude', '^sanitized/'], ], }], ], diff --git a/util/BUILD.gn b/util/BUILD.gn index 514f4838..abb55d5f 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -104,6 +104,8 @@ static_library("util") { "misc/pdb_structures.h", "misc/random_string.cc", "misc/random_string.h", + "misc/range_set.cc", + "misc/range_set.h", "misc/reinterpret_bytes.cc", "misc/reinterpret_bytes.h", "misc/scoped_forbid_return.cc", @@ -256,9 +258,7 @@ static_library("util") { defines = [ "CRASHPAD_USE_BORINGSSL" ] if (crashpad_is_in_fuchsia) { - deps += [ - "//third_party/boringssl" - ] + deps += [ "//third_party/boringssl" ] } else { libs = [ "crypto", @@ -490,13 +490,13 @@ if (!crashpad_is_android) { } if (use_boringssl_for_http_transport_socket) { - data_deps = [ ":generate_test_server_key" ] + data_deps = [ + ":generate_test_server_key", + ] defines = [ "CRASHPAD_USE_BORINGSSL" ] if (crashpad_is_in_fuchsia) { - deps += [ - "//third_party/boringssl" - ] + deps += [ "//third_party/boringssl" ] } else { libs = [ "crypto", @@ -526,6 +526,7 @@ source_set("util_test") { "misc/initialization_state_test.cc", "misc/paths_test.cc", "misc/random_string_test.cc", + "misc/range_set_test.cc", "misc/reinterpret_bytes_test.cc", "misc/scoped_forbid_return_test.cc", "misc/time_test.cc", diff --git a/util/misc/range_set.cc b/util/misc/range_set.cc new file mode 100644 index 00000000..5a7040fe --- /dev/null +++ b/util/misc/range_set.cc @@ -0,0 +1,55 @@ +// Copyright 2018 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 "util/misc/range_set.h" + +#include + +namespace crashpad { + +RangeSet::RangeSet() = default; + +RangeSet::~RangeSet() = default; + +void RangeSet::Insert(VMAddress base, VMSize size) { + if (!size) { + return; + } + + VMAddress last = base + size - 1; + + auto overlapping_range = ranges_.lower_bound(base); +#define OVERLAPPING_RANGES_BASE overlapping_range->second +#define OVERLAPPING_RANGES_LAST overlapping_range->first + while (overlapping_range != ranges_.end() && + OVERLAPPING_RANGES_BASE <= last) { + base = std::min(base, OVERLAPPING_RANGES_BASE); + last = std::max(last, OVERLAPPING_RANGES_LAST); + auto tmp = overlapping_range; + ++overlapping_range; + ranges_.erase(tmp); + } +#undef OVERLAPPING_RANGES_BASE +#undef OVERLAPPING_RANGES_LAST + + ranges_[last] = base; +} + +bool RangeSet::Contains(VMAddress address) const { + auto range_above_address = ranges_.lower_bound(address); + return range_above_address != ranges_.end() && + range_above_address->second <= address; +} + +} // namespace crashpad diff --git a/util/misc/range_set.h b/util/misc/range_set.h new file mode 100644 index 00000000..573705ec --- /dev/null +++ b/util/misc/range_set.h @@ -0,0 +1,51 @@ +// Copyright 2018 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_UTIL_MISC_RANGE_SET_H_ +#define CRASHPAD_UTIL_MISC_RANGE_SET_H_ + +#include + +#include "base/macros.h" +#include "util/misc/address_types.h" + +namespace crashpad { + +//! \brief A set of VMAddress ranges. +class RangeSet { + public: + RangeSet(); + ~RangeSet(); + + //! \brief Inserts a range into the set. + //! + //! \param[in] base The low address of the range. + //! \param[in] size The size of the range. + void Insert(VMAddress base, VMSize size); + + //! \brief Returns `true` if \a address falls within a range in this set. + bool Contains(VMAddress address) const; + + private: + // Keys are the highest address in the range. Values are the base address of + // the range. Overlapping ranges are merged on insertion. Adjacent ranges may + // be merged. + std::map ranges_; + + DISALLOW_COPY_AND_ASSIGN(RangeSet); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_RANGE_SET_H_ diff --git a/util/misc/range_set_test.cc b/util/misc/range_set_test.cc new file mode 100644 index 00000000..d5a3cd14 --- /dev/null +++ b/util/misc/range_set_test.cc @@ -0,0 +1,116 @@ +// Copyright 2018 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 "util/misc/range_set.h" + +#include + +#include + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "util/misc/address_types.h" +#include "util/misc/from_pointer_cast.h" + +namespace crashpad { +namespace test { +namespace { + +void ExpectRangeIsContained(const RangeSet& ranges, + VMAddress base, + VMSize size) { + for (VMAddress addr = base; addr < base + size; ++addr) { + SCOPED_TRACE(base::StringPrintf("0x%" PRIx64 " in range 0x%" PRIx64 + ":0x%" PRIx64, + addr, + base, + base + size)); + EXPECT_TRUE(ranges.Contains(addr)); + } +} + +TEST(RangeSet, Basic) { + RangeSet ranges; + auto base = FromPointerCast(&ranges); + VMSize size = sizeof(ranges); + ranges.Insert(base, size); + ExpectRangeIsContained(ranges, base, size); + EXPECT_FALSE(ranges.Contains(base - 1)); + EXPECT_FALSE(ranges.Contains(base + size)); +} + +TEST(RangeSet, ZeroSizedRange) { + RangeSet ranges; + auto addr = FromPointerCast(&ranges); + ranges.Insert(addr, 0); + EXPECT_FALSE(ranges.Contains(addr)); +} + +TEST(RangeSet, DuplicateRanges) { + RangeSet ranges; + auto base = FromPointerCast(&ranges); + VMSize size = sizeof(ranges); + ranges.Insert(base, size); + ranges.Insert(base, size); + ExpectRangeIsContained(ranges, base, size); +} + +TEST(RangeSet, OverlappingRanges) { + RangeSet ranges; + ranges.Insert(37, 16); + ranges.Insert(9, 9); + ranges.Insert(17, 42); + + EXPECT_TRUE(ranges.Contains(9)); + EXPECT_TRUE(ranges.Contains(17)); + EXPECT_TRUE(ranges.Contains(36)); + EXPECT_TRUE(ranges.Contains(37)); + EXPECT_TRUE(ranges.Contains(52)); + EXPECT_TRUE(ranges.Contains(58)); +} + +TEST(RangeSet, SubRangeInLargeRange) { + constexpr size_t kBufferSize = 2 << 22; + auto buf = std::make_unique(kBufferSize); + + RangeSet ranges; + auto addr = FromPointerCast(buf.get()); + + ranges.Insert(addr, kBufferSize); + EXPECT_TRUE(ranges.Contains(addr)); + EXPECT_TRUE(ranges.Contains(addr + kBufferSize - 1)); + + ranges.Insert(addr, kBufferSize / 2); + EXPECT_TRUE(ranges.Contains(addr)); + EXPECT_TRUE(ranges.Contains(addr + kBufferSize / 2 - 1)); + EXPECT_TRUE(ranges.Contains(addr + kBufferSize - 1)); +} + +TEST(RangeSet, LargeOverlappingRanges) { + constexpr size_t kBufferSize = 2 << 23; + auto buf = std::make_unique(kBufferSize); + + RangeSet ranges; + auto addr = FromPointerCast(buf.get()); + + ranges.Insert(addr, 3 * kBufferSize / 4); + ranges.Insert(addr + kBufferSize / 4, 3 * kBufferSize / 4); + EXPECT_TRUE(ranges.Contains(addr)); + EXPECT_TRUE(ranges.Contains(addr + kBufferSize - 1)); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/util/util.gyp b/util/util.gyp index 5590b1f3..4cbc13e1 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -153,6 +153,8 @@ 'misc/pdb_structures.h', 'misc/random_string.cc', 'misc/random_string.h', + 'misc/range_set.cc', + 'misc/range_set.h', 'misc/reinterpret_bytes.cc', 'misc/reinterpret_bytes.h', 'misc/scoped_forbid_return.cc', diff --git a/util/util_test.gyp b/util/util_test.gyp index d28fe9ee..ff3559e3 100644 --- a/util/util_test.gyp +++ b/util/util_test.gyp @@ -79,6 +79,7 @@ 'misc/paths_test.cc', 'misc/scoped_forbid_return_test.cc', 'misc/random_string_test.cc', + 'misc/range_set_test.cc', 'misc/reinterpret_bytes_test.cc', 'misc/time_test.cc', 'misc/uuid_test.cc', From d1e6a2130da4a6f4005a7729ad12420ce6c9a486 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 12 Jun 2018 08:30:36 -0700 Subject: [PATCH 300/326] android/linux: add a client interface to control sanitization Sanitization is controlled by a SanitizationInformation struct to be read from the client's memory. The address of this struct is either passed in a ClientInformation when the client requests a crash dump, or as a flag to the handler --sanitization_information. Bug: crashpad:30 Change-Id: I2744f8fb85b4fea7362b2b88faa4bef1da74e36b Reviewed-on: https://chromium-review.googlesource.com/1083143 Commit-Queue: Joshua Peraza Reviewed-by: Scott Graham --- client/crashpad_client_linux_test.cc | 49 +++++++++---- handler/handler_main.cc | 35 ++++++++-- .../linux/crash_report_exception_handler.cc | 61 +++++++++++++--- .../linux/crash_report_exception_handler.h | 7 +- handler/linux/exception_handler_server.cc | 7 +- handler/linux/exception_handler_server.h | 15 ++-- .../linux/exception_handler_server_test.cc | 8 +-- snapshot/BUILD.gn | 3 + .../sanitized/sanitization_information.cc | 61 ++++++++++++++++ snapshot/sanitized/sanitization_information.h | 68 ++++++++++++++++++ .../sanitization_information_test.cc | 70 +++++++++++++++++++ snapshot/snapshot.gyp | 2 + snapshot/snapshot_test.gyp | 1 + util/BUILD.gn | 1 + util/linux/exception_handler_protocol.cc | 25 +++++++ util/linux/exception_handler_protocol.h | 12 +++- util/misc/metrics.h | 6 ++ util/util.gyp | 1 + 18 files changed, 385 insertions(+), 47 deletions(-) create mode 100644 snapshot/sanitized/sanitization_information.cc create mode 100644 snapshot/sanitized/sanitization_information.h create mode 100644 snapshot/sanitized/sanitization_information_test.cc create mode 100644 util/linux/exception_handler_protocol.cc diff --git a/client/crashpad_client_linux_test.cc b/client/crashpad_client_linux_test.cc index bacfcae3..4c5bf4de 100644 --- a/client/crashpad_client_linux_test.cc +++ b/client/crashpad_client_linux_test.cc @@ -28,6 +28,7 @@ #include "gtest/gtest.h" #include "snapshot/annotation_snapshot.h" #include "snapshot/minidump/process_snapshot_minidump.h" +#include "snapshot/sanitized/sanitization_information.h" #include "test/multiprocess.h" #include "test/multiprocess_exec.h" #include "test/scoped_temp_dir.h" @@ -210,7 +211,9 @@ class StartHandlerForClientTest { StartHandlerForClientTest() = default; ~StartHandlerForClientTest() = default; - bool Initialize() { + bool Initialize(bool sanitize) { + sanitize_ = sanitize; + int socks[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) != 0) { PLOG(ERROR) << "socketpair"; @@ -253,19 +256,23 @@ class StartHandlerForClientTest { ASSERT_TRUE(database); std::vector reports; - ASSERT_EQ(database->GetPendingReports(&reports), - CrashReportDatabase::kNoError); - EXPECT_EQ(reports.size(), 1u); - - reports.clear(); ASSERT_EQ(database->GetCompletedReports(&reports), CrashReportDatabase::kNoError); EXPECT_EQ(reports.size(), 0u); + + reports.clear(); + ASSERT_EQ(database->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + if (sanitize_) { + EXPECT_EQ(reports.size(), 0u); + } else { + EXPECT_EQ(reports.size(), 1u); + } } bool InstallHandler() { auto signal_handler = SandboxedHandler::Get(); - return signal_handler->Initialize(client_sock_.get()); + return signal_handler->Initialize(client_sock_.get(), sanitize_); } private: @@ -278,8 +285,9 @@ class StartHandlerForClientTest { return instance; } - bool Initialize(FileHandle client_sock) { + bool Initialize(FileHandle client_sock, bool sanitize) { client_sock_ = client_sock; + sanitize_ = sanitize; return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr); } @@ -302,11 +310,20 @@ class StartHandlerForClientTest { context); exception_information.thread_id = syscall(SYS_gettid); - ClientInformation info = {}; + ClientInformation info; info.exception_information_address = FromPointerCast( &exception_information); + SanitizationInformation sanitization_info = {}; + if (state->sanitize_) { + info.sanitization_information_address = + FromPointerCast(&sanitization_info); + // Target a non-module address to prevent a crash dump. + sanitization_info.target_module_address = + FromPointerCast(&sanitization_info); + } + ExceptionHandlerClient handler_client(state->client_sock_); CHECK_EQ(handler_client.RequestCrashDump(info), 0); @@ -314,6 +331,7 @@ class StartHandlerForClientTest { } FileHandle client_sock_; + bool sanitize_; DISALLOW_COPY_AND_ASSIGN(SandboxedHandler); }; @@ -321,6 +339,7 @@ class StartHandlerForClientTest { ScopedTempDir temp_dir_; ScopedFileHandle client_sock_; ScopedFileHandle server_sock_; + bool sanitize_; DISALLOW_COPY_AND_ASSIGN(StartHandlerForClientTest); }; @@ -331,9 +350,9 @@ class StartHandlerForChildTest : public Multiprocess { StartHandlerForChildTest() = default; ~StartHandlerForChildTest() = default; - bool Initialize() { + bool Initialize(bool sanitize) { SetExpectedChildTerminationBuiltinTrap(); - return test_state_.Initialize(); + return test_state_.Initialize(sanitize); } private: @@ -361,7 +380,13 @@ class StartHandlerForChildTest : public Multiprocess { TEST(CrashpadClient, StartHandlerForChild) { StartHandlerForChildTest test; - ASSERT_TRUE(test.Initialize()); + ASSERT_TRUE(test.Initialize(/* sanitize= */ false)); + test.Run(); +} + +TEST(CrashpadClient, SanitizedChild) { + StartHandlerForChildTest test; + ASSERT_TRUE(test.Initialize(/* sanitize= */ true)); test.Run(); } diff --git a/handler/handler_main.cc b/handler/handler_main.cc index 901ee221..230feb41 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -144,6 +144,8 @@ void Usage(const base::FilePath& me) { " --trace-parent-with-exception=EXCEPTION_INFORMATION_ADDRESS\n" " request a dump for the handler's parent process\n" " --initial-client-fd=FD a socket connected to a client.\n" +" --sanitization_information=SANITIZATION_INFORMATION_ADDRESS\n" +" the address of a SanitizationInformation struct.\n" #endif // OS_LINUX || OS_ANDROID " --url=URL send crash reports to this Breakpad server URL,\n" " only if uploads are enabled for the database\n" @@ -167,6 +169,7 @@ struct Options { #elif defined(OS_LINUX) || defined(OS_ANDROID) VMAddress exception_information_address; int initial_client_fd; + VMAddress sanitization_information_address; #elif defined(OS_WIN) std::string pipe_name; InitialClientData initial_client_data; @@ -535,6 +538,7 @@ int HandlerMain(int argc, #if defined(OS_LINUX) || defined(OS_ANDROID) kOptionTraceParentWithException, kOptionInitialClientFD, + kOptionSanitizationInformation, #endif kOptionURL, @@ -590,6 +594,10 @@ int HandlerMain(int argc, nullptr, kOptionTraceParentWithException}, {"initial-client-fd", required_argument, nullptr, kOptionInitialClientFD}, + {"sanitization-information", + required_argument, + nullptr, + kOptionSanitizationInformation}, #endif // OS_LINUX || OS_ANDROID {"url", required_argument, nullptr, kOptionURL}, {"help", no_argument, nullptr, kOptionHelp}, @@ -608,6 +616,7 @@ int HandlerMain(int argc, #if defined(OS_LINUX) || defined(OS_ANDROID) options.exception_information_address = 0; options.initial_client_fd = kInvalidFileHandle; + options.sanitization_information_address = 0; #endif int opt; @@ -714,6 +723,15 @@ int HandlerMain(int argc, } break; } + case kOptionSanitizationInformation: { + if (!StringToNumber(optarg, + &options.sanitization_information_address)) { + ToolSupport::UsageHint(me, + "failed to parse --sanitization-information"); + return ExitFailure(); + } + break; + } #endif // OS_LINUX || OS_ANDROID case kOptionURL: { options.url = optarg; @@ -762,9 +780,15 @@ int HandlerMain(int argc, #elif defined(OS_LINUX) || defined(OS_ANDROID) if (!options.exception_information_address && options.initial_client_fd == kInvalidFileHandle) { + ToolSupport::UsageHint( + me, "--trace-parent-with-exception or --initial_client_fd is required"); + return ExitFailure(); + } + if (options.sanitization_information_address && + !options.exception_information_address) { ToolSupport::UsageHint( me, - "--exception_information_address or --initial_client_fd is required"); + "--sanitization_information requires --trace-parent-with-exception"); return ExitFailure(); } #endif // OS_MACOSX @@ -844,9 +868,12 @@ int HandlerMain(int argc, #if defined(OS_LINUX) || defined(OS_ANDROID) if (options.exception_information_address) { - return exception_handler.HandleException(getppid(), - options.exception_information_address) ? - EXIT_SUCCESS : ExitFailure(); + ClientInformation info; + info.exception_information_address = options.exception_information_address; + info.sanitization_information_address = + options.sanitization_information_address; + return exception_handler.HandleException(getppid(), info) ? EXIT_SUCCESS + : ExitFailure(); } #endif // OS_LINUX || OS_ANDROID diff --git a/handler/linux/crash_report_exception_handler.cc b/handler/linux/crash_report_exception_handler.cc index 2f38f2c0..101c49f5 100644 --- a/handler/linux/crash_report_exception_handler.cc +++ b/handler/linux/crash_report_exception_handler.cc @@ -21,6 +21,8 @@ #include "minidump/minidump_file_writer.h" #include "snapshot/crashpad_info_client_options.h" #include "snapshot/linux/process_snapshot_linux.h" +#include "snapshot/sanitized/process_snapshot_sanitized.h" +#include "snapshot/sanitized/sanitization_information.h" #include "util/linux/direct_ptrace_connection.h" #include "util/linux/ptrace_client.h" #include "util/misc/metrics.h" @@ -43,7 +45,7 @@ CrashReportExceptionHandler::~CrashReportExceptionHandler() = default; bool CrashReportExceptionHandler::HandleException( pid_t client_process_id, - VMAddress exception_info_address) { + const ClientInformation& info) { Metrics::ExceptionEncountered(); DirectPtraceConnection connection; @@ -53,12 +55,12 @@ bool CrashReportExceptionHandler::HandleException( return false; } - return HandleExceptionWithConnection(&connection, exception_info_address); + return HandleExceptionWithConnection(&connection, info); } bool CrashReportExceptionHandler::HandleExceptionWithBroker( pid_t client_process_id, - VMAddress exception_info_address, + const ClientInformation& info, int broker_sock) { Metrics::ExceptionEncountered(); @@ -69,19 +71,20 @@ bool CrashReportExceptionHandler::HandleExceptionWithBroker( return false; } - return HandleExceptionWithConnection(&client, exception_info_address); + return HandleExceptionWithConnection(&client, info); } bool CrashReportExceptionHandler::HandleExceptionWithConnection( PtraceConnection* connection, - VMAddress exception_info_address) { + const ClientInformation& info) { ProcessSnapshotLinux process_snapshot; if (!process_snapshot.Initialize(connection)) { Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed); return false; } - if (!process_snapshot.InitializeException(exception_info_address)) { + if (!process_snapshot.InitializeException( + info.exception_information_address)) { Metrics::ExceptionCaptureResult( Metrics::CaptureResult::kExceptionInitializationFailed); return false; @@ -116,10 +119,50 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection( process_snapshot.SetReportID(new_report->ReportID()); + ProcessSnapshot* snapshot = nullptr; + ProcessSnapshotSanitized sanitized; + std::vector whitelist; + if (info.sanitization_information_address) { + SanitizationInformation sanitization_info; + ProcessMemoryRange range; + if (!range.Initialize(connection->Memory(), connection->Is64Bit()) || + !range.Read(info.sanitization_information_address, + sizeof(sanitization_info), + &sanitization_info)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kSanitizationInitializationFailed); + return false; + } + + if (sanitization_info.annotations_whitelist_address && + !ReadAnnotationsWhitelist( + range, + sanitization_info.annotations_whitelist_address, + &whitelist)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kSanitizationInitializationFailed); + return false; + } + + if (!sanitized.Initialize(&process_snapshot, + sanitization_info.annotations_whitelist_address + ? &whitelist + : nullptr, + sanitization_info.target_module_address, + sanitization_info.sanitize_stacks)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kSkippedDueToSanitization); + return true; + } + + snapshot = &sanitized; + } else { + snapshot = &process_snapshot; + } + MinidumpFileWriter minidump; - minidump.InitializeFromSnapshot(&process_snapshot); - AddUserExtensionStreams( - user_stream_data_sources_, &process_snapshot, &minidump); + minidump.InitializeFromSnapshot(snapshot); + AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump); if (!minidump.WriteEverything(new_report->Writer())) { LOG(ERROR) << "WriteEverything failed"; diff --git a/handler/linux/crash_report_exception_handler.h b/handler/linux/crash_report_exception_handler.h index ca14f81a..24d5995a 100644 --- a/handler/linux/crash_report_exception_handler.h +++ b/handler/linux/crash_report_exception_handler.h @@ -23,6 +23,7 @@ #include "handler/crash_report_upload_thread.h" #include "handler/linux/exception_handler_server.h" #include "handler/user_stream_data_source.h" +#include "util/linux/exception_handler_protocol.h" #include "util/linux/ptrace_connection.h" #include "util/misc/address_types.h" @@ -63,15 +64,15 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { // ExceptionHandlerServer::Delegate: bool HandleException(pid_t client_process_id, - VMAddress exception_info_address) override; + const ClientInformation& info) override; bool HandleExceptionWithBroker(pid_t client_process_id, - VMAddress exception_info_address, + const ClientInformation& info, int broker_sock) override; private: bool HandleExceptionWithConnection(PtraceConnection* connection, - VMAddress exception_info_address); + const ClientInformation& info); CrashReportDatabase* database_; // weak CrashReportUploadThread* upload_thread_; // weak diff --git a/handler/linux/exception_handler_server.cc b/handler/linux/exception_handler_server.cc index 447093dc..748bf6e9 100644 --- a/handler/linux/exception_handler_server.cc +++ b/handler/linux/exception_handler_server.cc @@ -446,15 +446,12 @@ bool ExceptionHandlerServer::HandleCrashDumpRequest( ServerToClientMessage::kTypeCrashDumpFailed); case PtraceStrategyDecider::Strategy::kDirectPtrace: - delegate_->HandleException(client_process_id, - client_info.exception_information_address); + delegate_->HandleException(client_process_id, client_info); break; case PtraceStrategyDecider::Strategy::kUseBroker: delegate_->HandleExceptionWithBroker( - client_process_id, - client_info.exception_information_address, - client_sock); + client_process_id, client_info, client_sock); break; } diff --git a/handler/linux/exception_handler_server.h b/handler/linux/exception_handler_server.h index 1c02a441..5f491826 100644 --- a/handler/linux/exception_handler_server.h +++ b/handler/linux/exception_handler_server.h @@ -71,24 +71,21 @@ class ExceptionHandlerServer { //! \brief Called on receipt of a crash dump request from a client. //! //! \param[in] client_process_id The process ID of the crashing client. - //! \param[in] exception_information_address The address in the client's - //! address space of an ExceptionInformation struct. + //! \param[in] info Information on the client. //! \return `true` on success. `false` on failure with a message logged. virtual bool HandleException(pid_t client_process_id, - VMAddress exception_information_address) = 0; + const ClientInformation& info) = 0; //! \brief Called on the receipt of a crash dump request from a client for a //! crash that should be mediated by a PtraceBroker. //! //! \param[in] client_process_id The process ID of the crashing client. - //! \param[in] exception_information_address The address in the client's - //! address space of an ExceptionInformation struct. + //! \param[in] info Information on the client. //! \param[in] broker_sock A socket connected to the PtraceBroker. //! \return `true` on success. `false` on failure with a message logged. - virtual bool HandleExceptionWithBroker( - pid_t client_process_id, - VMAddress exception_information_address, - int broker_sock) = 0; + virtual bool HandleExceptionWithBroker(pid_t client_process_id, + const ClientInformation& info, + int broker_sock) = 0; protected: ~Delegate() {} diff --git a/handler/linux/exception_handler_server_test.cc b/handler/linux/exception_handler_server_test.cc index 247ac8c6..eda14d31 100644 --- a/handler/linux/exception_handler_server_test.cc +++ b/handler/linux/exception_handler_server_test.cc @@ -101,25 +101,25 @@ class TestDelegate : public ExceptionHandlerServer::Delegate { } bool HandleException(pid_t client_process_id, - VMAddress exception_information_address) override { + const ClientInformation& info) override { DirectPtraceConnection connection; bool connected = connection.Initialize(client_process_id); EXPECT_TRUE(connected); - last_exception_address_ = exception_information_address; + last_exception_address_ = info.exception_information_address; last_client_ = client_process_id; sem_.Signal(); return connected; } bool HandleExceptionWithBroker(pid_t client_process_id, - VMAddress exception_information_address, + const ClientInformation& info, int broker_sock) override { PtraceClient client; bool connected = client.Initialize(broker_sock, client_process_id); EXPECT_TRUE(connected); - last_exception_address_ = exception_information_address, + last_exception_address_ = info.exception_information_address, last_client_ = client_process_id; sem_.Signal(); return connected; diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index f8a055bd..6bafabb3 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -128,6 +128,8 @@ static_library("snapshot") { "sanitized/module_snapshot_sanitized.h", "sanitized/process_snapshot_sanitized.cc", "sanitized/process_snapshot_sanitized.h", + "sanitized/sanitization_information.cc", + "sanitized/sanitization_information.h", "sanitized/thread_snapshot_sanitized.cc", "sanitized/thread_snapshot_sanitized.h", ] @@ -338,6 +340,7 @@ source_set("snapshot_test") { "linux/process_reader_linux_test.cc", "linux/system_snapshot_linux_test.cc", "sanitized/process_snapshot_sanitized_test.cc", + "sanitized/sanitization_information_test.cc", ] } else { sources += [ "crashpad_info_client_options_test.cc" ] diff --git a/snapshot/sanitized/sanitization_information.cc b/snapshot/sanitized/sanitization_information.cc new file mode 100644 index 00000000..f7be9b08 --- /dev/null +++ b/snapshot/sanitized/sanitization_information.cc @@ -0,0 +1,61 @@ +// Copyright 2018 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/sanitized/sanitization_information.h" + +#include "client/annotation.h" + +namespace crashpad { + +namespace { + +template +bool ReadWhitelist(const ProcessMemoryRange& memory, + VMAddress whitelist_address, + std::vector* whitelist) { + if (!whitelist_address) { + return true; + } + + std::vector local_whitelist; + Pointer name_address; + while (memory.Read(whitelist_address, sizeof(name_address), &name_address)) { + if (!name_address) { + whitelist->swap(local_whitelist); + return true; + } + + std::string name; + if (!memory.ReadCStringSizeLimited( + name_address, Annotation::kNameMaxLength, &name)) { + return false; + } + local_whitelist.push_back(name); + whitelist_address += sizeof(Pointer); + } + + return false; +} + +} // namespace + +bool ReadAnnotationsWhitelist(const ProcessMemoryRange& memory, + VMAddress whitelist_address, + std::vector* whitelist) { + return memory.Is64Bit() + ? ReadWhitelist(memory, whitelist_address, whitelist) + : ReadWhitelist(memory, whitelist_address, whitelist); +} + +} // namespace crashpad diff --git a/snapshot/sanitized/sanitization_information.h b/snapshot/sanitized/sanitization_information.h new file mode 100644 index 00000000..6a6d9089 --- /dev/null +++ b/snapshot/sanitized/sanitization_information.h @@ -0,0 +1,68 @@ +// Copyright 2018 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_SANITIZED_SANITIZATION_INFORMATION_H_ +#define CRASHPAD_SNAPSHOT_SANITIZED_SANITIZATION_INFORMATION_H_ + +#include + +#include +#include + +#include "util/misc/address_types.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +#pragma pack(push, 1) + +//! \brief Struture containing information about how snapshots should be +//! sanitized. +//! +//! \see ProcessSnapshotSanitized +struct SanitizationInformation { + //! \brief The address in the client process' address space of a nullptr + //! terminated array of NUL-terminated strings. The string values are the + //! names of whitelisted annotations. This value is 0 if there is no + //! whitelist and all annotations are allowed. + VMAddress annotations_whitelist_address; + + //! \brief An address in the client process' address space within a module to + //! target. When a target module is used, crash dumps are discarded unless + //! the crashing thread's program counter or pointer-aligned values on the + //! crashing thread's stack point into the target module. This value is 0 + //! if there is no target module. + VMAddress target_module_address; + + //! \brief Non-zero if stacks should be sanitized for possible PII. + uint8_t sanitize_stacks; +}; + +#pragma pack(pop) + +//! \brief Reads an annotations whitelist from another process. +//! +//! \param[in] memory A memory reader for the target process. +//! \param[in] whitelist_address The address in the target process' address +//! space of a nullptr terminated array of NUL-terminated strings. +//! \param[out] whitelist The whitelist read, valid only if this function +//! returns `true`. +//! \return `true` on success, `false` on failure with a message logged. +bool ReadAnnotationsWhitelist(const ProcessMemoryRange& memory, + VMAddress whitelist_address, + std::vector* whitelist); + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SANITIZED_SANITIZATION_INFORMATION_H_ diff --git a/snapshot/sanitized/sanitization_information_test.cc b/snapshot/sanitized/sanitization_information_test.cc new file mode 100644 index 00000000..a7bf8265 --- /dev/null +++ b/snapshot/sanitized/sanitization_information_test.cc @@ -0,0 +1,70 @@ +// Copyright 2018 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/sanitized/sanitization_information.h" + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "util/misc/from_pointer_cast.h" +#include "util/process/process_memory_linux.h" + +namespace crashpad { +namespace test { +namespace { + +class WhitelistTest : public testing::Test { + public: + void SetUp() override { + ASSERT_TRUE(memory_.Initialize(getpid())); +#if defined(ARCH_CPU_64_BITS) + ASSERT_TRUE(range_.Initialize(&memory_, true)); +#else + ASSERT_TRUE(range_.Initialize(&memory_, false)); +#endif + } + + protected: + bool ReadWhitelist(const char* const* address) { + return ReadAnnotationsWhitelist( + range_, FromPointerCast(address), &whitelist_); + } + + ProcessMemoryLinux memory_; + ProcessMemoryRange range_; + std::vector whitelist_; +}; + +const char* const kEmptyWhitelist[] = {nullptr}; + +TEST_F(WhitelistTest, EmptyWhitelist) { + ASSERT_TRUE(ReadWhitelist(kEmptyWhitelist)); + EXPECT_EQ(whitelist_, std::vector()); +} + +const char* const kNonEmptyWhitelist[] = {"string1", + "another_string", + "", + nullptr}; + +TEST_F(WhitelistTest, NonEmptyWhitelist) { + ASSERT_TRUE(ReadWhitelist(kNonEmptyWhitelist)); + ASSERT_EQ(whitelist_.size(), arraysize(kNonEmptyWhitelist) - 1); + for (size_t index = 0; index < arraysize(kNonEmptyWhitelist) - 1; ++index) { + EXPECT_EQ(whitelist_[index], kNonEmptyWhitelist[index]); + } +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index 3aad7bae..a78d21f9 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -129,6 +129,8 @@ 'sanitized/module_snapshot_sanitized.h', 'sanitized/process_snapshot_sanitized.cc', 'sanitized/process_snapshot_sanitized.h', + 'sanitized/sanitization_information.cc', + 'sanitized/sanitization_information.h', 'sanitized/thread_snapshot_sanitized.cc', 'sanitized/thread_snapshot_sanitized.h', 'snapshot_constants.h', diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index c2ec97d3..fc746f2c 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -93,6 +93,7 @@ 'minidump/process_snapshot_minidump_test.cc', 'posix/timezone_test.cc', 'sanitized/process_snapshot_sanitized_test.cc', + 'sanitized/sanitization_information_test.cc', 'win/cpu_context_win_test.cc', 'win/exception_snapshot_win_test.cc', 'win/extra_memory_ranges_test.cc', diff --git a/util/BUILD.gn b/util/BUILD.gn index abb55d5f..03220c9e 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -281,6 +281,7 @@ static_library("util") { "linux/direct_ptrace_connection.h", "linux/exception_handler_client.cc", "linux/exception_handler_client.h", + "linux/exception_handler_protocol.cc", "linux/exception_handler_protocol.h", "linux/exception_information.h", "linux/memory_map.cc", diff --git a/util/linux/exception_handler_protocol.cc b/util/linux/exception_handler_protocol.cc new file mode 100644 index 00000000..3feca698 --- /dev/null +++ b/util/linux/exception_handler_protocol.cc @@ -0,0 +1,25 @@ +// Copyright 2018 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 "util/linux/exception_handler_protocol.h" + +namespace crashpad { + +ClientInformation::ClientInformation() + : exception_information_address(0), sanitization_information_address(0) {} + +ClientToServerMessage::ClientToServerMessage() + : version(kVersion), type(kCrashDumpRequest), client_info() {} + +} // namespace crashpad diff --git a/util/linux/exception_handler_protocol.h b/util/linux/exception_handler_protocol.h index 3f71abf7..4ba1b5d0 100644 --- a/util/linux/exception_handler_protocol.h +++ b/util/linux/exception_handler_protocol.h @@ -35,17 +35,27 @@ enum Bool : char { kBoolFalse, kBoolTrue }; //! \brief Information about a client registered with an ExceptionHandlerServer. struct ClientInformation { + //! \brief Constructs this object. + ClientInformation(); + //! \brief The address in the client's address space of an //! ExceptionInformation struct. VMAddress exception_information_address; + + //! \brief The address in the client's address space of a + //! SanitizationInformation struct, or 0 if there is no such struct. + VMAddress sanitization_information_address; }; //! \brief The message passed from client to server. struct ClientToServerMessage { static constexpr int32_t kVersion = 1; + //! \brief Constructs this object. + ClientToServerMessage(); + //! \brief Indicates what message version is being used. - int32_t version = kVersion; + int32_t version; enum Type : uint32_t { //! \brief Used to request a crash dump for the sending client. diff --git a/util/misc/metrics.h b/util/misc/metrics.h index 54ccb843..3e9337a9 100644 --- a/util/misc/metrics.h +++ b/util/misc/metrics.h @@ -129,6 +129,12 @@ class Metrics { //! This value is only used on Linux/Android. kBrokeredPtraceFailed = 9, + //! \brief Sanitization was requested but could not be initialized. + kSanitizationInitializationFailed = 10, + + //! \brief Sanitization caused this crash dump to be skipped. + kSkippedDueToSanitization = 11, + //! \brief The number of values in this enumeration; not a valid value. kMaxValue }; diff --git a/util/util.gyp b/util/util.gyp index 4cbc13e1..1ffb752c 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -60,6 +60,7 @@ 'linux/direct_ptrace_connection.h', 'linux/exception_handler_client.cc', 'linux/exception_handler_client.h', + 'linux/exception_handler_protocol.cc', 'linux/exception_handler_protocol.h', 'linux/exception_information.h', 'linux/memory_map.cc', From bff3594594abe4fbba8a30e4ee9c2c2b69e93bb6 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 12 Jun 2018 09:32:52 -0700 Subject: [PATCH 301/326] fuchsia: Update includes for new fdio header location Also includes a gtest roll, which includes a change in gtest to do the same thing. This also removes the link against launchpad which is no longer necessary, and will be removed from the SDK soon. Bug: crashpad:196, chromium:848028, chromium:850757 Change-Id: Ica8632a6157b585d6b44073e05bf7aa43253e305 Reviewed-on: https://chromium-review.googlesource.com/1096353 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- DEPS | 2 +- client/crashpad_client_fuchsia.cc | 2 +- test/multiprocess_exec_fuchsia.cc | 4 ++-- third_party/gtest/BUILD.gn | 6 ------ tools/run_with_crashpad.cc | 2 +- 5 files changed, 5 insertions(+), 11 deletions(-) diff --git a/DEPS b/DEPS index 5f96b781..d9231e13 100644 --- a/DEPS +++ b/DEPS @@ -23,7 +23,7 @@ deps = { '6fe4a3251488f7af86d64fc25cf442e817cf6133', 'crashpad/third_party/gtest/gtest': Var('chromium_git') + '/external/github.com/google/googletest@' + - '145d05750b15324899473340c8dd5af50d125d33', + 'c091b0469ab4c04ee9411ef770f32360945f4c53', 'crashpad/third_party/gyp/gyp': Var('chromium_git') + '/external/gyp@' + '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', diff --git a/client/crashpad_client_fuchsia.cc b/client/crashpad_client_fuchsia.cc index 0d4b8727..26781094 100644 --- a/client/crashpad_client_fuchsia.cc +++ b/client/crashpad_client_fuchsia.cc @@ -14,7 +14,7 @@ #include "client/crashpad_client.h" -#include +#include #include #include diff --git a/test/multiprocess_exec_fuchsia.cc b/test/multiprocess_exec_fuchsia.cc index 20f3ab65..95436b47 100644 --- a/test/multiprocess_exec_fuchsia.cc +++ b/test/multiprocess_exec_fuchsia.cc @@ -14,8 +14,8 @@ #include "test/multiprocess_exec.h" -#include -#include +#include +#include #include #include #include diff --git a/third_party/gtest/BUILD.gn b/third_party/gtest/BUILD.gn index f9108281..db5f4a56 100644 --- a/third_party/gtest/BUILD.gn +++ b/third_party/gtest/BUILD.gn @@ -46,9 +46,6 @@ if (crashpad_is_in_chromium) { visibility = [ ":*" ] include_dirs = [ "gtest/googletest" ] defines = [ "GUNIT_NO_GOOGLE3=1" ] - if (crashpad_is_fuchsia) { - libs = [ "launchpad" ] - } } config("gtest_public_config") { @@ -265,9 +262,6 @@ if (crashpad_is_in_chromium) { config("gmock_private_config") { visibility = [ ":*" ] include_dirs = [ "gtest/googlemock" ] - if (crashpad_is_fuchsia) { - libs = [ "launchpad" ] - } } config("gmock_public_config") { diff --git a/tools/run_with_crashpad.cc b/tools/run_with_crashpad.cc index f26dd2bc..5ef0edb6 100644 --- a/tools/run_with_crashpad.cc +++ b/tools/run_with_crashpad.cc @@ -32,7 +32,7 @@ #include "util/string/split_string.h" #if defined(OS_FUCHSIA) -#include +#include #include #include From 7c6d2334a93ce5ae9d376db9f8f577a51682338f Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 12 Jun 2018 10:13:38 -0700 Subject: [PATCH 302/326] Don't fail minidump write if memory snapshot read fails On Windows (and probably elsewhere) it's possible that something else on the system changes the memory map between when a memory snapshot range is added to the minidump, and when the process's memory is actually read from the target and written to the .dmp file. As a result, failing the Read() should not result in aborting the minidump's write, which it previously would have. Bug: crashpad:234 Change-Id: Ib24e255a34fa2e1758621d3955ebc7a0f96166e2 Reviewed-on: https://chromium-review.googlesource.com/1096452 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- minidump/minidump_memory_writer.cc | 12 +++++- minidump/minidump_memory_writer_test.cc | 41 +++++++++++++++++++ .../test/minidump_memory_writer_test_util.cc | 4 ++ .../test/minidump_memory_writer_test_util.h | 2 + snapshot/test/test_memory_snapshot.cc | 6 ++- snapshot/test/test_memory_snapshot.h | 3 ++ 6 files changed, 66 insertions(+), 2 deletions(-) diff --git a/minidump/minidump_memory_writer.cc b/minidump/minidump_memory_writer.cc index 0e5f0861..868c356e 100644 --- a/minidump/minidump_memory_writer.cc +++ b/minidump/minidump_memory_writer.cc @@ -52,7 +52,17 @@ bool SnapshotMinidumpMemoryWriter::WriteObject( file_writer); // This will result in MemorySnapshotDelegateRead() being called. - return memory_snapshot_->Read(this); + if (!memory_snapshot_->Read(this)) { + // If the Read() fails (perhaps because the process' memory map has changed + // since it the range was captured), write an empty block of memory. It + // would be nice to instead not include this memory, but at this point in + // the writing process, it would be difficult to amend the minidump's + // structure. See https://crashpad.chromium.org/234 for background. + std::vector empty(memory_snapshot_->Size(), 0xfe); + MemorySnapshotDelegateRead(empty.data(), empty.size()); + } + + return true; } const MINIDUMP_MEMORY_DESCRIPTOR* diff --git a/minidump/minidump_memory_writer_test.cc b/minidump/minidump_memory_writer_test.cc index acff9c29..60b7fa8a 100644 --- a/minidump/minidump_memory_writer_test.cc +++ b/minidump/minidump_memory_writer_test.cc @@ -188,6 +188,47 @@ TEST(MinidumpMemoryWriter, TwoMemoryRegions) { } } +TEST(MinidumpMemoryWriter, RegionReadFails) { + MinidumpFileWriter minidump_file_writer; + auto memory_list_writer = std::make_unique(); + + constexpr uint64_t kBaseAddress = 0xfedcba9876543210; + constexpr size_t kSize = 0x1000; + constexpr uint8_t kValue = 'm'; + + auto memory_writer = + std::make_unique(kBaseAddress, kSize, kValue); + + // Make the read of that memory fail. + memory_writer->SetShouldFailRead(true); + + memory_list_writer->AddMemory(std::move(memory_writer)); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + MINIDUMP_MEMORY_DESCRIPTOR expected; + expected.StartOfMemoryRange = kBaseAddress; + expected.Memory.DataSize = kSize; + expected.Memory.Rva = + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MEMORY_LIST) + + memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR); + ExpectMinidumpMemoryDescriptorAndContents( + &expected, + &memory_list->MemoryRanges[0], + string_file.string(), + 0xfe, // Not kValue ('m'), but the value that the implementation inserts + // if memory is unreadable. + true); +} + class TestMemoryStream final : public internal::MinidumpStreamWriter { public: TestMemoryStream(uint64_t base_address, size_t size, uint8_t value) diff --git a/minidump/test/minidump_memory_writer_test_util.cc b/minidump/test/minidump_memory_writer_test_util.cc index 03a0e18a..f198abeb 100644 --- a/minidump/test/minidump_memory_writer_test_util.cc +++ b/minidump/test/minidump_memory_writer_test_util.cc @@ -31,6 +31,10 @@ TestMinidumpMemoryWriter::TestMinidumpMemoryWriter(uint64_t base_address, TestMinidumpMemoryWriter::~TestMinidumpMemoryWriter() { } +void TestMinidumpMemoryWriter::SetShouldFailRead(bool should_fail) { + test_snapshot_.SetShouldFailRead(should_fail); +} + void ExpectMinidumpMemoryDescriptor( const MINIDUMP_MEMORY_DESCRIPTOR* expected, const MINIDUMP_MEMORY_DESCRIPTOR* observed) { diff --git a/minidump/test/minidump_memory_writer_test_util.h b/minidump/test/minidump_memory_writer_test_util.h index db198909..0890cd0e 100644 --- a/minidump/test/minidump_memory_writer_test_util.h +++ b/minidump/test/minidump_memory_writer_test_util.h @@ -40,6 +40,8 @@ class TestMinidumpMemoryWriter final : public SnapshotMinidumpMemoryWriter { TestMinidumpMemoryWriter(uint64_t base_address, size_t size, uint8_t value); ~TestMinidumpMemoryWriter(); + void SetShouldFailRead(bool should_fail); + private: TestMemorySnapshot test_snapshot_; diff --git a/snapshot/test/test_memory_snapshot.cc b/snapshot/test/test_memory_snapshot.cc index a3e9ebd9..95aba406 100644 --- a/snapshot/test/test_memory_snapshot.cc +++ b/snapshot/test/test_memory_snapshot.cc @@ -21,7 +21,7 @@ namespace crashpad { namespace test { TestMemorySnapshot::TestMemorySnapshot() - : address_(0), size_(0), value_('\0') { + : address_(0), size_(0), value_('\0'), should_fail_(false) { } TestMemorySnapshot::~TestMemorySnapshot() { @@ -36,6 +36,10 @@ size_t TestMemorySnapshot::Size() const { } bool TestMemorySnapshot::Read(Delegate* delegate) const { + if (should_fail_) { + return false; + } + if (size_ == 0) { return delegate->MemorySnapshotDelegateRead(nullptr, size_); } diff --git a/snapshot/test/test_memory_snapshot.h b/snapshot/test/test_memory_snapshot.h index ae17ee48..011e6c6f 100644 --- a/snapshot/test/test_memory_snapshot.h +++ b/snapshot/test/test_memory_snapshot.h @@ -40,6 +40,8 @@ class TestMemorySnapshot final : public MemorySnapshot { //! called. This value will be repeated Size() times. void SetValue(char value) { value_ = value; } + void SetShouldFailRead(bool should_fail) { should_fail_ = true; } + // MemorySnapshot: uint64_t Address() const override; @@ -52,6 +54,7 @@ class TestMemorySnapshot final : public MemorySnapshot { uint64_t address_; size_t size_; char value_; + bool should_fail_; DISALLOW_COPY_AND_ASSIGN(TestMemorySnapshot); }; From edc8084c3bdcb8dd2064fb1987fee10c0752e8d9 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 12 Jun 2018 16:30:50 -0700 Subject: [PATCH 303/326] Roll mini_chromium to d5523a7 [fuchsia] Update to zx_cprng_draw_new Bug: crashpad:196 Change-Id: Ia6f3d19045b10ee02d241e65f0aa437c53a47e55 Reviewed-on: https://chromium-review.googlesource.com/1098227 Reviewed-by: Scott Graham Commit-Queue: Scott Graham --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index d9231e13..fbbd899d 100644 --- a/DEPS +++ b/DEPS @@ -29,7 +29,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'd5a36d3c51a7a270afc2c888baaaec2a6e496219', + 'd5523a7033c013dd2ae73fa29428f7b70dc0b3a0', 'crashpad/third_party/libfuzzer/src': Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' + 'fda403cf93ecb8792cb1d061564d89a6553ca020', From fa47143c8c62dfff60620f19bb992834549b844d Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Wed, 13 Jun 2018 08:33:32 -0700 Subject: [PATCH 304/326] [fuchsia] Move to zx_take_startup_handle This function is just a renamed version of zx_get_startup_handle to clarify the transfer of ownership. Change-Id: Ic83cc592df3a571faebd788b3403ccfebd9ff3b8 Reviewed-on: https://chromium-review.googlesource.com/1099054 Commit-Queue: Scott Graham Reviewed-by: Scott Graham --- handler/handler_main.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/handler/handler_main.cc b/handler/handler_main.cc index 230feb41..412ee112 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -939,18 +939,18 @@ int HandlerMain(int argc, } #elif defined(OS_FUCHSIA) // These handles are logically "moved" into these variables when retrieved by - // zx_get_startup_handle(). Both are given to ExceptionHandlerServer which + // zx_take_startup_handle(). Both are given to ExceptionHandlerServer which // owns them in this process. There is currently no "connect-later" mode on // Fuchsia, all the binding must be done by the client before starting // crashpad_handler. - base::ScopedZxHandle root_job(zx_get_startup_handle(PA_HND(PA_USER0, 0))); + base::ScopedZxHandle root_job(zx_take_startup_handle(PA_HND(PA_USER0, 0))); if (!root_job.is_valid()) { LOG(ERROR) << "no process handle passed in startup handle 0"; return EXIT_FAILURE; } base::ScopedZxHandle exception_port( - zx_get_startup_handle(PA_HND(PA_USER0, 1))); + zx_take_startup_handle(PA_HND(PA_USER0, 1))); if (!exception_port.is_valid()) { LOG(ERROR) << "no exception port handle passed in startup handle 1"; return EXIT_FAILURE; From 85565c545cb11cfd5814e1f286874e5c96dc7f34 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 13 Jun 2018 10:15:19 -0700 Subject: [PATCH 305/326] Doc update for GN build Explain the GN build setup for Windows, Mac, Linux, and Fuchsia, and keep a separate section for the Android gyp build. Bug: crashpad:235 Change-Id: Ifaabba430ab0f04bac5a4669523308040e55f05b Reviewed-on: https://chromium-review.googlesource.com/1099255 Commit-Queue: Scott Graham Reviewed-by: Joshua Peraza --- doc/developing.md | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/doc/developing.md b/doc/developing.md index c8bb430d..84d1cf76 100644 --- a/doc/developing.md +++ b/doc/developing.md @@ -86,28 +86,25 @@ $ gclient sync ## Building -Crashpad uses [GYP](https://gyp.gsrc.io/) to generate -[Ninja](https://ninja-build.org/) build files. The build is described by `.gyp` -files throughout the Crashpad source code tree. The -[`build/gyp_crashpad.py`](https://chromium.googlesource.com/crashpad/crashpad/+/master/build/gyp_crashpad.py) -script runs GYP properly for Crashpad, and is also called when you run `fetch -crashpad`, `gclient sync`, or `gclient runhooks`. +### Windows, Mac, Linux, Fuchsia -The Ninja build files and build output are in the `out` directory. Both debug- -and release-mode configurations are available. The examples below show the debug -configuration. To build and test the release configuration, substitute `Release` -for `Debug`. On Windows, four configurations are available: `Debug` and -`Release` produce 32-bit x86 executables, and `Debug_x64` and `Release_x64` -produce x86_64 executables. +On Windows, Mac, Linux, and Fuchsia Crashpad uses +[GN](https://gn.googlesource.com/gn) to generate +[Ninja](https://ninja-build.org/) build files. For example, ``` $ cd ~/crashpad/crashpad -$ ninja -C out/Debug +$ gn gen out/Default +$ ninja -C out/Default ``` -Ninja is part of the +You can then use `gn args out/Default` or edit `out/Default/args.gn` to +configure the build, for example things like `is_debug=true` or +`target_cpu="x86"`. + +GN and Ninja are part of the [depot_tools](https://www.chromium.org/developers/how-tos/depottools). There’s -no need to install it separately. +no need to install them separately. ### Android From 0a665e3c81a1aec148db59ebcfaa436637772f53 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 13 Jun 2018 14:23:58 -0700 Subject: [PATCH 306/326] Suppress output when generating test server key openssl outputs some useless junk when generating the test key; swallow that. ''' Generating a 2048 bit RSA private key ...............................................+++ ...........................................+++ writing new private key to 'crashpad_util_test_key.pem' ----- ''' Bug: crashpad:196 Change-Id: I0bdfb4f29931ef58d0c51c5e5488d3b5aeb798f0 Reviewed-on: https://chromium-review.googlesource.com/1099960 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- util/net/generate_test_server_key.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/util/net/generate_test_server_key.py b/util/net/generate_test_server_key.py index 5f67bb87..6e6340e3 100755 --- a/util/net/generate_test_server_key.py +++ b/util/net/generate_test_server_key.py @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os import subprocess # GN requires a Python script for actions, so this just wraps the openssl @@ -23,4 +24,5 @@ key = 'crashpad_util_test_key.pem' cert = 'crashpad_util_test_cert.pem' subprocess.check_call( ['openssl', 'req', '-x509', '-nodes', '-subj', '/CN=localhost', - '-days', '365', '-newkey', 'rsa:2048', '-keyout', key, '-out', cert]) + '-days', '365', '-newkey', 'rsa:2048', '-keyout', key, '-out', cert], + stderr=open(os.devnull, 'w')) From 2771ebf805aed81c95bf1d8dc9ee404b603df025 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Wed, 13 Jun 2018 20:14:43 -0700 Subject: [PATCH 307/326] fuchsia: Package test cert and key when running in Fuchsia tree Bug: crashpad:196 Change-Id: I18f7686a9b5127143501c2b21663d80aae3d1f54 Reviewed-on: https://chromium-review.googlesource.com/1100494 Commit-Queue: Scott Graham Reviewed-by: Scott Graham --- BUILD.gn | 12 ++++++++++++ test/test_paths.cc | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/BUILD.gn b/BUILD.gn index e09f72dd..065a5e1f 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -45,6 +45,7 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { "snapshot:crashpad_snapshot_test_module_large", "snapshot:crashpad_snapshot_test_module_small", "test:crashpad_test_test_multiprocess_exec_test_child", + "util:generate_test_server_key", "util:http_transport_test_server", ] @@ -60,6 +61,17 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { name = "http_transport_test_server" dest = "crashpad_test_data/http_transport_test_server" }, + + # These aren't actually tests, but that seems to be the only way to + # convince package() to get them from the output directory. + { + name = "crashpad_util_test_cert.pem" + dest = "crashpad_test_data/crashpad_util_test_cert.pem" + }, + { + name = "crashpad_util_test_key.pem" + dest = "crashpad_test_data/crashpad_util_test_key.pem" + }, ] loadable_modules = [ diff --git a/test/test_paths.cc b/test/test_paths.cc index 7bfc5de0..50165e43 100644 --- a/test/test_paths.cc +++ b/test/test_paths.cc @@ -232,6 +232,14 @@ base::FilePath TestPaths::BuildArtifact( break; case FileType::kCertificate: +#if defined(CRASHPAD_IS_IN_FUCHSIA) + // When running in the Fuchsia tree, the .pem files are packaged as assets + // into the test data folder. This will need to be rationalized when + // things are actually run from a package. + // https://crashpad.chromium.org/bug/196. + directory = + base::FilePath(FILE_PATH_LITERAL("/system/test/crashpad_test_data")); +#endif extension = FILE_PATH_LITERAL(".pem"); break; } From 639cba075c8d9e9635674b456e75a7390bc495d9 Mon Sep 17 00:00:00 2001 From: Brett Wilson Date: Wed, 13 Jun 2018 16:38:28 -0700 Subject: [PATCH 308/326] Use new Fuchsia kernel API to suspend threads. We are transitioning to a token-based API and will be removing the old one. Changes to use a thread state wait rather than reading the registers in a loop to determine when the thread is actually suspended. Change-Id: I4b015bb0fc74b15177304a62be6c1d9a59b45c80 Reviewed-on: https://chromium-review.googlesource.com/1100170 Commit-Queue: Brett Wilson Reviewed-by: Scott Graham --- util/fuchsia/scoped_task_suspend.cc | 84 ++++++++--------------------- util/fuchsia/scoped_task_suspend.h | 18 ++++--- 2 files changed, 33 insertions(+), 69 deletions(-) diff --git a/util/fuchsia/scoped_task_suspend.cc b/util/fuchsia/scoped_task_suspend.cc index c5871963..ca0fd564 100644 --- a/util/fuchsia/scoped_task_suspend.cc +++ b/util/fuchsia/scoped_task_suspend.cc @@ -40,80 +40,42 @@ zx_obj_type_t GetHandleType(zx_handle_t handle) { return basic.type; } -enum class SuspensionResult { - FailedSuspendCall, - FailedToSuspendInTimelyFashion, - Succeeded, -}; - -SuspensionResult SuspendThread(zx_handle_t thread) { - zx_status_t status = zx_task_suspend(thread); - ZX_LOG_IF(ERROR, status != ZX_OK, status) << "zx_task_suspend"; - if (status != ZX_OK) - return SuspensionResult::FailedSuspendCall; - // zx_task_suspend() suspends the thread "sometime soon", but it's hard to - // use when it's not guaranteed to be suspended after return. Try reading the - // thread state until the registers are retrievable, which means that the - // thread is actually suspended. Don't wait forever in case the suspend - // failed for whatever reason, but try a few times. - for (int i = 0; i < 5; ++i) { - zx_thread_state_general_regs_t regs; - status = zx_thread_read_state( - thread, ZX_THREAD_STATE_GENERAL_REGS, ®s, sizeof(regs)); - if (status == ZX_OK) { - return SuspensionResult::Succeeded; - } - zx_nanosleep(zx_deadline_after(ZX_MSEC(10))); +// Returns the suspend token of the suspended thread. This function attempts +// to wait a short time for the thread to actually suspend before returning +// but this is not guaranteed. +base::ScopedZxHandle SuspendThread(zx_handle_t thread) { + zx_handle_t token = ZX_HANDLE_INVALID; + zx_status_t status = zx_task_suspend_token(thread, &token); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_task_suspend"; + base::ScopedZxHandle(); } - LOG(ERROR) << "thread failed to suspend"; - return SuspensionResult::FailedToSuspendInTimelyFashion; -} -bool ResumeThread(zx_handle_t thread) { - zx_status_t status = zx_task_resume(thread, 0); - ZX_LOG_IF(ERROR, status != ZX_OK, status) << "zx_task_resume"; - return status == ZX_OK; + zx_signals_t observed = 0u; + if (zx_object_wait_one(thread, ZX_THREAD_SUSPENDED, + zx_deadline_after(ZX_MSEC(50)), &observed) != ZX_OK) { + LOG(ERROR) << "thread failed to suspend"; + } + return base::ScopedZxHandle(token); } } // namespace -ScopedTaskSuspend::ScopedTaskSuspend(zx_handle_t task) : task_(task) { - DCHECK_NE(task_, zx_process_self()); - DCHECK_NE(task_, zx_thread_self()); +ScopedTaskSuspend::ScopedTaskSuspend(zx_handle_t task) { + DCHECK_NE(task, zx_process_self()); + DCHECK_NE(task, zx_thread_self()); - zx_obj_type_t type = GetHandleType(task_); + zx_obj_type_t type = GetHandleType(task); if (type == ZX_OBJ_TYPE_THREAD) { - // Note that task_ is only marked invalid if the zx_task_suspend() call - // completely fails, otherwise the suspension might just not have taken - // effect yet, so avoid leaving it suspended forever by still resuming on - // destruction. - if (SuspendThread(task_) == SuspensionResult::FailedSuspendCall) { - task_ = ZX_HANDLE_INVALID; - } + suspend_tokens_.push_back(SuspendThread(task)); } else if (type == ZX_OBJ_TYPE_PROCESS) { - for (const auto& thread : GetChildHandles(task_, ZX_INFO_PROCESS_THREADS)) { - SuspendThread(thread.get()); - } + for (const auto& thread : GetChildHandles(task, ZX_INFO_PROCESS_THREADS)) + suspend_tokens_.push_back(SuspendThread(thread.get())); } else { LOG(ERROR) << "unexpected handle type"; - task_ = ZX_HANDLE_INVALID; } } -ScopedTaskSuspend::~ScopedTaskSuspend() { - if (task_ != ZX_HANDLE_INVALID) { - zx_obj_type_t type = GetHandleType(task_); - if (type == ZX_OBJ_TYPE_THREAD) { - ResumeThread(task_); - } else if (type == ZX_OBJ_TYPE_PROCESS) { - for (const auto& thread : - GetChildHandles(task_, ZX_INFO_PROCESS_THREADS)) { - ResumeThread(thread.get()); - } - } else { - LOG(ERROR) << "unexpected handle type"; - } - } -} +ScopedTaskSuspend::~ScopedTaskSuspend() = default; } // namespace crashpad diff --git a/util/fuchsia/scoped_task_suspend.h b/util/fuchsia/scoped_task_suspend.h index d3790c8c..f817364d 100644 --- a/util/fuchsia/scoped_task_suspend.h +++ b/util/fuchsia/scoped_task_suspend.h @@ -17,20 +17,21 @@ #include +#include + +#include "base/fuchsia/scoped_zx_handle.h" #include "base/macros.h" namespace crashpad { //! \brief Manages the suspension of another task. //! -//! Currently, suspends and resumes are not counted on Fuchsia, so while this -//! class attempts to manage suspension of a task, if another caller or process -//! is simultaneously suspending or resuming this task, the results may not be -//! as expected. +//! The underlying API only supports suspending threads (despite its name) not +//! entire tasks. As a result, it's possible some threads may not be correctly +//! suspended/resumed as their creation might race enumeration. //! -//! Additionally, the underlying API only supports suspending threads (despite -//! its name) not entire tasks. As a result, it's possible some threads may not -//! be correctly suspended/resumed as their creation might race enumeration. +//! Additionally, suspending a thread is asynchronous and may take an +//! arbitrary amount of time. //! //! Because of these limitations, this class is limited to being a best-effort, //! and correct suspension/resumption cannot be relied upon. @@ -43,7 +44,8 @@ class ScopedTaskSuspend { ~ScopedTaskSuspend(); private: - zx_handle_t task_; // weak + // Could be one (for a thread) or many (for every process in a thread). + std::vector suspend_tokens_; DISALLOW_COPY_AND_ASSIGN(ScopedTaskSuspend); }; From c0a0d70a2b7a5d973828e6079386cb2a852e5b1c Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 20 Jun 2018 09:36:57 -0700 Subject: [PATCH 309/326] Increase max annotations size Clank's JavaExceptionReporter attaches up to 5 * 4096 bytes of Java exceptions to minidumps. Bug: crashpad:30 Change-Id: I3b6b63b1f1b893225d6f340b03a65edf105ed0be Reviewed-on: https://chromium-review.googlesource.com/1108337 Reviewed-by: Robert Sesek Commit-Queue: Joshua Peraza --- client/annotation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/annotation.h b/client/annotation.h index c7d92d80..46cde62c 100644 --- a/client/annotation.h +++ b/client/annotation.h @@ -72,7 +72,7 @@ class Annotation { static constexpr size_t kNameMaxLength = 64; //! \brief The maximum size of an annotation’s value, in bytes. - static constexpr size_t kValueMaxSize = 2048; + static constexpr size_t kValueMaxSize = 5 * 4096; //! \brief The type used for \a SetSize(). using ValueSizeType = uint32_t; From fec77b759378aeea611bd05f77343b241708398e Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 21 Jun 2018 10:37:32 -0700 Subject: [PATCH 310/326] win: Add optional cipd pull of Windows toolchain package Requires https://chromium-review.googlesource.com/c/chromium/mini_chromium/+/1110347 to get used. Bug: crashpad:236 Change-Id: I0e6178b68959e3995ace65d5109380b3ce1d8158 Reviewed-on: https://chromium-review.googlesource.com/1110346 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- DEPS | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/DEPS b/DEPS index fbbd899d..29474219 100644 --- a/DEPS +++ b/DEPS @@ -14,7 +14,8 @@ vars = { 'chromium_git': 'https://chromium.googlesource.com', - 'pull_linux_clang': False + 'pull_linux_clang': False, + 'pull_win_toolchain': False } deps = { @@ -231,6 +232,23 @@ hooks = [ '-log-level', 'info', ], }, + { + 'name': 'toolchain_win', + 'pattern': '.', + # This package is only updated when the solution in .gclient includes an + # entry like: + # "custom_vars": { "pull_win_toolchain": True } + # This is because the contained bits are not redistributable. + 'condition': 'checkout_win and pull_win_toolchain', + 'action': [ + 'cipd', + 'install', + 'chrome_internal/third_party/sdk/windows', + 'uploaded:2018-06-13', + '-root', 'crashpad/third_party/win', + '-log-level', 'info', + ], + }, { 'name': 'gyp', 'pattern': '\.gypi?$', From b0de1cff1b6f018927c95c452f59496b0b2b6538 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 21 Jun 2018 11:49:31 -0700 Subject: [PATCH 311/326] win: Move cipd toolchain pull to subdirectory of third_party/win In case we want more things under third_party/win, having a toolchain subdir makes more sense. Bug: crashpad:236 Change-Id: Iefb54117c286d79f96ffa7beca9e3e3aab3f208e Reviewed-on: https://chromium-review.googlesource.com/1110489 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 29474219..c4886301 100644 --- a/DEPS +++ b/DEPS @@ -245,7 +245,7 @@ hooks = [ 'install', 'chrome_internal/third_party/sdk/windows', 'uploaded:2018-06-13', - '-root', 'crashpad/third_party/win', + '-root', 'crashpad/third_party/win/toolchain', '-log-level', 'info', ], }, From d5ab4e21c6b7ed6572feace6819bf7ea37f877b5 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 21 Jun 2018 12:49:17 -0700 Subject: [PATCH 312/326] Roll mini_chromium to 793e94e2 Only one change: 793e94e2 win: Use cipd win toolchain package when available Bug: crashpad:236 Change-Id: I6482aebb785dd0dc2db022d1b3b44014ea7e952f Reviewed-on: https://chromium-review.googlesource.com/1110664 Reviewed-by: Joshua Peraza Commit-Queue: Scott Graham --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index c4886301..dbdc3a49 100644 --- a/DEPS +++ b/DEPS @@ -30,7 +30,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'd5523a7033c013dd2ae73fa29428f7b70dc0b3a0', + '793e94e2c652831af2d25bb5288b04e59048c62d', 'crashpad/third_party/libfuzzer/src': Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' + 'fda403cf93ecb8792cb1d061564d89a6553ca020', From 17dff19b216ba59ee5704c8ba49cccda1be3102c Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Thu, 21 Jun 2018 15:32:54 -0700 Subject: [PATCH 313/326] fuchsia: Re-enable the trybots in compile-only config; enable arm64 https://chromium-review.googlesource.com/c/chromium/tools/build/+/1111016 makes the bots compile-only which is better than not having them on at all, until we have a reliable way to run the tests. Additionally, flip on the arm64 builders. Bug: crashpad:219 Change-Id: If61fe9d3e569996119c09499599d197070b8c358 Reviewed-on: https://chromium-review.googlesource.com/1111020 Reviewed-by: Joshua Peraza --- infra/config/cq.cfg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg index 64213fc3..b6bf0434 100644 --- a/infra/config/cq.cfg +++ b/infra/config/cq.cfg @@ -39,10 +39,10 @@ verifiers { builders { name: "crashpad_try_win_rel" } builders { name: "crashpad_try_linux_dbg" } builders { name: "crashpad_try_linux_rel" } - # https://bugs.chromium.org/p/crashpad/issues/detail?id=219 QEMU runs are - # flaking; remove from CQ while being investigated. - #builders { name: "crashpad_try_fuchsia_x64_dbg" } - #builders { name: "crashpad_try_fuchsia_x64_rel" } + builders { name: "crashpad_try_fuchsia_arm64_dbg" } + builders { name: "crashpad_try_fuchsia_arm64_rel" } + builders { name: "crashpad_try_fuchsia_x64_dbg" } + builders { name: "crashpad_try_fuchsia_x64_rel" } # https://crbug.com/743139 - disabled until we can move these to swarming, # at which point we can just remove them. #builders { name: "crashpad_try_win_x86_dbg" } From 4b05be4265c0ffacfce26d7db7644ffbf9037696 Mon Sep 17 00:00:00 2001 From: Jose Dapena Paz Date: Tue, 26 Jun 2018 11:47:15 +0200 Subject: [PATCH 314/326] linux: fix ARM snapshot context traits build. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On GCC with libstdc++, ContextTraits fail to build because of the missing declaration of offsetof (should include cstddef) and for aliasing a type with the same name overriding previous declaration. Change-Id: Ic497238122bcb430f14f9234644c483a8e27e3b6 Reviewed-on: https://chromium-review.googlesource.com/1114606 Reviewed-by: Robert Sesek Commit-Queue: José Dapena Paz --- snapshot/linux/exception_snapshot_linux.cc | 4 ++-- snapshot/linux/signal_context.h | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/snapshot/linux/exception_snapshot_linux.cc b/snapshot/linux/exception_snapshot_linux.cc index 6e2a9275..c57e072e 100644 --- a/snapshot/linux/exception_snapshot_linux.cc +++ b/snapshot/linux/exception_snapshot_linux.cc @@ -130,7 +130,7 @@ bool ExceptionSnapshotLinux::ReadContext( LinuxVMAddress gprs_address = context_address + offsetof(UContext, mcontext32) + - offsetof(MContext32, gprs); + offsetof(ContextTraits32::MContext32, gprs); SignalThreadContext32 thread_context; if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) { @@ -207,7 +207,7 @@ bool ExceptionSnapshotLinux::ReadContext( LinuxVMAddress gprs_address = context_address + offsetof(UContext, mcontext64) + - offsetof(MContext64, gprs); + offsetof(ContextTraits64::MContext64, gprs); ThreadContext::t64_t thread_context; if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) { diff --git a/snapshot/linux/signal_context.h b/snapshot/linux/signal_context.h index f9d2cb9d..98844151 100644 --- a/snapshot/linux/signal_context.h +++ b/snapshot/linux/signal_context.h @@ -19,6 +19,7 @@ #include #include +#include #include #include "build/build_config.h" @@ -244,7 +245,7 @@ struct SignalThreadContext32 { using SignalThreadContext64 = ThreadContext::t64_t; -struct MContext32 { +struct MContext32Data { uint32_t trap_no; uint32_t error_code; uint32_t oldmask; @@ -252,19 +253,19 @@ struct MContext32 { uint32_t fault_address; }; -struct MContext64 { +struct MContext64Data { uint64_t fault_address; SignalThreadContext64 gprs; }; struct ContextTraits32 : public Traits32 { - using MContext32 = MContext32; + using MContext32 = MContext32Data; using MContext64 = Nothing; }; struct ContextTraits64 : public Traits64 { using MContext32 = Nothing; - using MContext64 = MContext64; + using MContext64 = MContext64Data; }; template From b1692957029dd9076ebf93b3bcab40d67cdcd132 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 27 Jun 2018 11:46:03 -0700 Subject: [PATCH 315/326] linux: include missing ucontext header Change-Id: Icac41faf501bba63b3bcd0cdc41e8fa26ca3509c Reviewed-on: https://chromium-review.googlesource.com/1117371 Reviewed-by: Scott Graham Commit-Queue: Joshua Peraza --- snapshot/linux/signal_context.h | 1 + 1 file changed, 1 insertion(+) diff --git a/snapshot/linux/signal_context.h b/snapshot/linux/signal_context.h index 98844151..63202abe 100644 --- a/snapshot/linux/signal_context.h +++ b/snapshot/linux/signal_context.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include From eb7d8a4cde17c3c5527ec9a3b7106949063e388c Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 27 Jun 2018 14:07:25 -0700 Subject: [PATCH 316/326] Alias user regs structs for glibc ARM64 pre 2.20 Change-Id: Ie8e4ce8df34c4d14e874884db50d748d03af9592 Reviewed-on: https://chromium-review.googlesource.com/1117644 Reviewed-by: Scott Graham Commit-Queue: Joshua Peraza --- compat/BUILD.gn | 1 + compat/compat.gyp | 1 + compat/linux/sys/user.h | 28 ++++++++++++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 compat/linux/sys/user.h diff --git a/compat/BUILD.gn b/compat/BUILD.gn index 8db7b4de..2bfe074f 100644 --- a/compat/BUILD.gn +++ b/compat/BUILD.gn @@ -69,6 +69,7 @@ compat_target("compat") { sources += [ "linux/signal.h", "linux/sys/ptrace.h", + "linux/sys/user.h", ] } diff --git a/compat/compat.gyp b/compat/compat.gyp index c5fe350f..2c5eeff3 100644 --- a/compat/compat.gyp +++ b/compat/compat.gyp @@ -36,6 +36,7 @@ 'android/sys/user.h', 'linux/signal.h', 'linux/sys/ptrace.h', + 'linux/sys/user.h', 'mac/AvailabilityMacros.h', 'mac/kern/exc_resource.h', 'mac/mach/i386/thread_state.h', diff --git a/compat/linux/sys/user.h b/compat/linux/sys/user.h new file mode 100644 index 00000000..197c1dd1 --- /dev/null +++ b/compat/linux/sys/user.h @@ -0,0 +1,28 @@ +// Copyright 2018 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_COMPAT_LINUX_SYS_USER_H_ +#define CRASHPAD_COMPAT_LINUX_SYS_USER_H_ + +#include_next + +#include + +// glibc for 64-bit ARM uses different names for these structs prior to 2.20. +#if defined(__arm64__) && defined(__GLIBC__) && !__GLIBC_PREREQ(2, 20) +using user_regs_struct = user_pt_regs; +using user_fpsimd_struct = user_fpsimd_state; +#endif + +#endif // CRASHPAD_COMPAT_LINUX_SYS_USER_H_ From aabc485b646b813cd75ccf3d73d4435b97352e6b Mon Sep 17 00:00:00 2001 From: Nick Maniscalco Date: Mon, 2 Jul 2018 13:22:13 -0700 Subject: [PATCH 317/326] fuchsia: Use ZX_TIME_INFINITE instead of UINT64_MAX In the future, Zircon's time types will change from unsigned to signed. Use ZX_TIME_INFINITE instead of UINT64_MAX when zx_nanosleep'ing. See related Zircon bug ZX-2100. Change-Id: I5eb139280c27ca817e1a489f04c860563c9b677c Reviewed-on: https://chromium-review.googlesource.com/1123221 Commit-Queue: Nick Maniscalco Reviewed-by: Joshua Peraza --- snapshot/fuchsia/process_reader_fuchsia_test.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/snapshot/fuchsia/process_reader_fuchsia_test.cc b/snapshot/fuchsia/process_reader_fuchsia_test.cc index 63b59e83..546d8290 100644 --- a/snapshot/fuchsia/process_reader_fuchsia_test.cc +++ b/snapshot/fuchsia/process_reader_fuchsia_test.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include "gtest/gtest.h" #include "test/multiprocess_exec.h" @@ -103,7 +104,7 @@ void* SignalAndSleep(void* arg) { zx_port_packet_t packet = {}; packet.type = ZX_PKT_TYPE_USER; zx_port_queue(*reinterpret_cast(arg), &packet); - zx_nanosleep(UINT64_MAX); + zx_nanosleep(ZX_TIME_INFINITE); return nullptr; } From 038ba6eea2b2acd79084d5a979126ac8c952bee4 Mon Sep 17 00:00:00 2001 From: Charly Delay Date: Tue, 3 Jul 2018 15:01:12 -0400 Subject: [PATCH 318/326] compat.gyp: use type=none for header-only target On Linux and macOS, compat.gyp:crashpad_compat is a header-only target, which should be declared type=none. This CL also adds the missing non_mac/ include_dirs for non-macOS targets. Bug: crashpad: Change-Id: I7bef32e8f6bdcb86f51118a1bb1d3b52d05120d1 Reviewed-on: https://chromium-review.googlesource.com/1124773 Reviewed-by: Joshua Peraza Commit-Queue: Joshua Peraza --- compat/compat.gyp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/compat/compat.gyp b/compat/compat.gyp index 2c5eeff3..1229ee0c 100644 --- a/compat/compat.gyp +++ b/compat/compat.gyp @@ -19,7 +19,6 @@ 'targets': [ { 'target_name': 'crashpad_compat', - 'type': 'static_library', 'sources': [ 'android/dlfcn_internal.cc', 'android/dlfcn_internal.h', @@ -63,6 +62,7 @@ ], 'conditions': [ ['OS=="mac"', { + 'type': 'none', 'include_dirs': [ 'mac', ], @@ -71,8 +71,18 @@ 'mac', ], }, + }, { + 'include_dirs': [ + 'non_mac', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + 'non_mac', + ], + }, }], ['OS=="win"', { + 'type': 'static_library', 'include_dirs': [ 'win', ], @@ -95,6 +105,7 @@ }, }], ['OS=="android"', { + 'type': 'static_library', 'include_dirs': [ 'android', 'linux', @@ -112,6 +123,7 @@ }, }], ['OS=="linux"', { + 'type': 'none', 'include_dirs': [ 'linux', ], From 3072b4059fbef261975bd5fc7363226b109b21fd Mon Sep 17 00:00:00 2001 From: Charly Delay Date: Fri, 6 Jul 2018 13:10:46 -0400 Subject: [PATCH 319/326] zlib.gyp: support the "external" dependency mode This CL implements support for the external model for Crashpad's zlib dependency, in which the dependencies and Crashpad are checked out as sibling in the same directory. Bug: crashpad: Change-Id: I0ca640e0be9b6a4fd8379026dfc8eb061b40badf Reviewed-on: https://chromium-review.googlesource.com/1124778 Reviewed-by: Joshua Peraza Commit-Queue: Joshua Peraza --- third_party/zlib/zlib.gyp | 45 +++++++++++++++++++++++--------- third_party/zlib/zlib_crashpad.h | 5 ++-- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/third_party/zlib/zlib.gyp b/third_party/zlib/zlib.gyp index b6a6bc91..df26cc23 100644 --- a/third_party/zlib/zlib.gyp +++ b/third_party/zlib/zlib.gyp @@ -13,18 +13,28 @@ # limitations under the License. { - 'variables': { - 'conditions': [ - # Use the system zlib by default where available, as it is on most - # platforms. Windows does not have a system zlib, so use “embedded” which - # directs the build to use the source code in the zlib subdirectory. - ['OS!="win"', { - 'zlib_source%': 'system', - }, { - 'zlib_source%': 'embedded', - }], - ], - }, + 'includes': [ + '../../build/crashpad_dependencies.gypi', + ], + 'conditions': [ + ['1==1', { # Defer processing until crashpad_dependencies is set + 'variables': { + 'conditions': [ + ['crashpad_dependencies=="external"', { + 'zlib_source%': 'external', + }, 'OS!="win"', { + # Use the system zlib by default where available, as it is on most + # platforms. Windows does not have a system zlib, so use “embedded” + # which directs the build to use the source code in the zlib + # subdirectory. + 'zlib_source%': 'system', + }, { + 'zlib_source%': 'embedded', + }], + ], + }, + }], + ], 'targets': [ { 'target_name': 'zlib', @@ -141,6 +151,17 @@ }], ], }], + ['zlib_source=="external"', { + 'type': 'none', + 'direct_dependent_settings': { + 'defines': [ + 'CRASHPAD_ZLIB_SOURCE_EXTERNAL', + ], + }, + 'dependencies': [ + '../../../../zlib/zlib.gyp:zlib', + ], + }], ], }, ], diff --git a/third_party/zlib/zlib_crashpad.h b/third_party/zlib/zlib_crashpad.h index d3a23861..fd497a85 100644 --- a/third_party/zlib/zlib_crashpad.h +++ b/third_party/zlib/zlib_crashpad.h @@ -19,9 +19,8 @@ // available at any other location in the source tree. It will #include the // proper depending on how the build has been configured. -#if defined(CRASHPAD_ZLIB_SOURCE_EXTERNAL) -#include "third_party/zlib/zlib.h" -#elif defined(CRASHPAD_ZLIB_SOURCE_SYSTEM) +#if defined(CRASHPAD_ZLIB_SOURCE_SYSTEM) || \ + defined(CRASHPAD_ZLIB_SOURCE_EXTERNAL) #include #elif defined(CRASHPAD_ZLIB_SOURCE_EMBEDDED) #include "third_party/zlib/zlib/zlib.h" From cb41ba7471a02113447f621027bc08a75b35be11 Mon Sep 17 00:00:00 2001 From: Djordje Golubovic Date: Tue, 10 Jul 2018 11:17:22 +0200 Subject: [PATCH 320/326] Added MIPS support to crashpad. Modified gyp/gn files to support MIPS targets. Implemented thread_info, cpu_context, signal context classes for MIPS target. Addressed MIPS specific signal ordering. Added "MIPS Technologies, Inc." to AUTHORS file. Bug: crashpad:232 Change-Id: Ibfc221ba54088e95f984b9dc6be5fd52f86abcc2 Reviewed-on: https://chromium-review.googlesource.com/1064594 Commit-Queue: Joshua Peraza Reviewed-by: Joshua Peraza --- AUTHORS | 1 + compat/linux/sys/ptrace.h | 7 + minidump/minidump_context.h | 132 +++++++++++++++++ minidump/minidump_context_writer.cc | 111 ++++++++++++++ minidump/minidump_context_writer.h | 80 ++++++++++ minidump/minidump_context_writer_test.cc | 30 ++++ minidump/minidump_misc_info_writer.cc | 4 + minidump/test/minidump_context_test_util.cc | 139 ++++++++++++++++++ minidump/test/minidump_context_test_util.h | 9 ++ snapshot/BUILD.gn | 6 +- snapshot/capture_memory.cc | 4 + snapshot/cpu_architecture.h | 8 +- snapshot/cpu_context.cc | 2 + snapshot/cpu_context.h | 48 ++++++ snapshot/linux/cpu_context_linux.h | 36 +++++ snapshot/linux/exception_snapshot_linux.cc | 55 +++++++ snapshot/linux/exception_snapshot_linux.h | 3 + .../linux/exception_snapshot_linux_test.cc | 30 ++++ snapshot/linux/process_reader_linux.cc | 3 + snapshot/linux/signal_context.h | 121 +++++++++++++++ snapshot/linux/system_snapshot_linux.cc | 12 ++ snapshot/linux/thread_snapshot_linux.cc | 16 ++ snapshot/linux/thread_snapshot_linux.h | 3 + snapshot/snapshot_test.gyp | 23 ++- snapshot/test/test_cpu_context.cc | 72 +++++++++ snapshot/test/test_cpu_context.h | 2 + test/linux/get_tls.cc | 14 ++ test/multiprocess_posix.cc | 2 +- util/linux/auxiliary_vector_test.cc | 9 +- util/linux/ptracer.cc | 127 +++++++++++++++- util/linux/thread_info.h | 43 ++++++ util/misc/capture_context.h | 1 + util/misc/capture_context_linux.S | 91 +++++++++++- util/misc/capture_context_test.cc | 2 +- util/misc/capture_context_test_util_linux.cc | 6 + util/posix/symbolic_constants_posix.cc | 34 +++++ util/posix/symbolic_constants_posix_test.cc | 2 + 37 files changed, 1271 insertions(+), 17 deletions(-) diff --git a/AUTHORS b/AUTHORS index 7c40c4fe..8dcac323 100644 --- a/AUTHORS +++ b/AUTHORS @@ -11,3 +11,4 @@ Intel Corporation Opera Software ASA Vewd Software AS LG Electronics, Inc. +MIPS Technologies, Inc. diff --git a/compat/linux/sys/ptrace.h b/compat/linux/sys/ptrace.h index e5c95c7c..fa894381 100644 --- a/compat/linux/sys/ptrace.h +++ b/compat/linux/sys/ptrace.h @@ -30,6 +30,13 @@ static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = static_cast<__ptrace_request>(22); #define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA +#elif defined(__mips__) +static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = + static_cast<__ptrace_request>(25); +#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA +static constexpr __ptrace_request PTRACE_GET_THREAD_AREA_3264 = + static_cast<__ptrace_request>(0xc4); +#define PTRACE_GET_THREAD_AREA_3264 PTRACE_GET_THREAD_AREA_3264 #endif #endif // !PTRACE_GET_THREAD_AREA && !PT_GET_THREAD_AREA && defined(__GLIBC__) diff --git a/minidump/minidump_context.h b/minidump/minidump_context.h index a7328cad..6f51b5a4 100644 --- a/minidump/minidump_context.h +++ b/minidump/minidump_context.h @@ -432,6 +432,138 @@ struct MinidumpContextARM64 { uint128_struct fpsimd[32]; }; +//! \brief 32bit MIPS-specifc flags for MinidumpContextMIPS::context_flags. +//! Based on minidump_cpu_mips.h from breakpad +enum MinidumpContextMIPSFlags : uint32_t { + //! \brief Identifies the context structure as MIPSEL. + kMinidumpContextMIPS = 0x00040000, + + //! \brief Indicates the validity of integer registers. + //! + //! Registers `0`-`31`, `mdhi`, `mdlo`, `epc`, `badvaddr`, `status` and + //! `cause` are valid. + kMinidumpContextMIPSInteger = kMinidumpContextMIPS | 0x00000002, + + //! \brief Indicates the validity of floating point registers. + //! + //! Floating point registers `0`-`31`, `fpcsr` and `fir` are valid + kMinidumpContextMIPSFloatingPoint = kMinidumpContextMIPS | 0x00000004, + + //! \brief Indicates the validity of DSP registers. + //! + //! Registers `hi0`-`hi2`, `lo0`-`lo2` and `dsp_control` are valid + kMinidumpContextMIPSDSP = kMinidumpContextMIPS | 0x00000008, + + //! \brief Indicates the validity of all registers. + kMinidumpContextMIPSAll = kMinidumpContextMIPSInteger | + kMinidumpContextMIPSFloatingPoint | + kMinidumpContextMIPSDSP, +}; + +//! \brief A 32bit MIPS CPU context (register state) carried in a minidump file. +struct MinidumpContextMIPS { + uint32_t context_flags; + + //! \brief This padding field is included for breakpad compatibility. + uint32_t _pad0; + //! \brief General purpose registers `0`-`31`. + uint64_t regs[32]; + + //! \brief Multiply/divide result. + uint64_t mdhi, mdlo; + + //! \brief DSP registers. + uint32_t hi[3]; + uint32_t lo[3]; + uint32_t dsp_control; + //! \brief This padding field is included for breakpad compatibility. + uint32_t _pad1; + + // \brief cp0 registers. + uint64_t epc; + uint64_t badvaddr; + uint32_t status; + uint32_t cause; + + //! \brief FPU registers. + union { + struct { + float _fp_fregs; + uint32_t _fp_pad; + } fregs[32]; + double dregs[32]; + } fpregs; + + //! \brief FPU status register. + uint32_t fpcsr; + //! \brief FPU implementation register. + uint32_t fir; +}; + +//! \brief 64bit MIPS-specifc flags for MinidumpContextMIPS64::context_flags. +//! Based on minidump_cpu_mips.h from breakpad +enum MinidumpContextMIPS64Flags : uint32_t { + //! \brief Identifies the context structure as MIPS64EL. + kMinidumpContextMIPS64 = 0x00080000, + + //! \brief Indicates the validity of integer registers. + //! + //! Registers `0`-`31`, `mdhi`, `mdlo`, `epc`, `badvaddr`, `status` and + //! `cause` are valid. + kMinidumpContextMIPS64Integer = kMinidumpContextMIPS64 | 0x00000002, + + //! \brief Indicates the validity of floating point registers. + //! + //! Floating point registers `0`-`31`, `fpcsr` and `fir` are valid + kMinidumpContextMIPS64FloatingPoint = kMinidumpContextMIPS64 | 0x00000004, + + //! \brief Indicates the validity of DSP registers. + //! + //! Registers `hi0`-`hi2`, `lo0`-`lo2` and `dsp_control` are valid. + kMinidumpContextMIPS64DSP = kMinidumpContextMIPS64 | 0x00000008, + + //! \brief Indicates the validity of all registers. + kMinidumpContextMIPS64All = kMinidumpContextMIPS64Integer | + kMinidumpContextMIPS64FloatingPoint | + kMinidumpContextMIPS64DSP, +}; + +//! \brief A 32bit MIPS CPU context (register state) carried in a minidump file. +struct MinidumpContextMIPS64 { + uint64_t context_flags; + + //! \brief General purpose registers. + uint64_t regs[32]; + + //! \brief Multiply/divide result. + uint64_t mdhi, mdlo; + + //! \brief DSP registers. + uint64_t hi[3]; + uint64_t lo[3]; + uint64_t dsp_control; + + //! \brief cp0 registers. + uint64_t epc; + uint64_t badvaddr; + uint64_t status; + uint64_t cause; + + //! \brief FPU registers. + union { + struct { + float _fp_fregs; + uint32_t _fp_pad; + } fregs[32]; + double dregs[32]; + } fpregs; + + //! \brief FPU status register. + uint64_t fpcsr; + //! \brief FPU implementation register. + uint64_t fir; +}; + } // namespace crashpad #endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_ diff --git a/minidump/minidump_context_writer.cc b/minidump/minidump_context_writer.cc index 2fa2e53e..20adbf3f 100644 --- a/minidump/minidump_context_writer.cc +++ b/minidump/minidump_context_writer.cc @@ -87,6 +87,20 @@ MinidumpContextWriter::CreateFromSnapshot(const CPUContext* context_snapshot) { break; } + case kCPUArchitectureMIPSEL: { + context = std::make_unique(); + reinterpret_cast(context.get()) + ->InitializeFromSnapshot(context_snapshot->mipsel); + break; + } + + case kCPUArchitectureMIPS64EL: { + context = std::make_unique(); + reinterpret_cast(context.get()) + ->InitializeFromSnapshot(context_snapshot->mips64); + break; + } + default: { LOG(ERROR) << "unknown context architecture " << context_snapshot->architecture; @@ -339,4 +353,101 @@ size_t MinidumpContextARM64Writer::ContextSize() const { return sizeof(context_); } +MinidumpContextMIPSWriter::MinidumpContextMIPSWriter() + : MinidumpContextWriter(), context_() { + context_.context_flags = kMinidumpContextMIPS; +} + +MinidumpContextMIPSWriter::~MinidumpContextMIPSWriter() = default; + +void MinidumpContextMIPSWriter::InitializeFromSnapshot( + const CPUContextMIPS* context_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(context_.context_flags, kMinidumpContextMIPS); + + context_.context_flags = kMinidumpContextMIPSAll; + + static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs), + "GPRs size mismatch"); + memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); + context_.mdhi = context_snapshot->mdhi; + context_.mdlo = context_snapshot->mdlo; + context_.epc = context_snapshot->cp0_epc; + context_.badvaddr = context_snapshot->cp0_badvaddr; + context_.status = context_snapshot->cp0_status; + context_.cause = context_snapshot->cp0_cause; + + static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs), + "FPRs size mismatch"); + memcpy(&context_.fpregs, &context_snapshot->fpregs, sizeof(context_.fpregs)); + context_.fpcsr = context_snapshot->fpcsr; + context_.fir = context_snapshot->fir; + + for (size_t index = 0; index < 3; ++index) { + context_.hi[index] = context_snapshot->hi[index]; + context_.lo[index] = context_snapshot->lo[index]; + } + context_.dsp_control = context_snapshot->dsp_control; +} + +bool MinidumpContextMIPSWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + return file_writer->Write(&context_, sizeof(context_)); +} + +size_t MinidumpContextMIPSWriter::ContextSize() const { + DCHECK_GE(state(), kStateFrozen); + return sizeof(context_); +} + +MinidumpContextMIPS64Writer::MinidumpContextMIPS64Writer() + : MinidumpContextWriter(), context_() { + context_.context_flags = kMinidumpContextMIPS64; +} + +MinidumpContextMIPS64Writer::~MinidumpContextMIPS64Writer() = default; + +void MinidumpContextMIPS64Writer::InitializeFromSnapshot( + const CPUContextMIPS64* context_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(context_.context_flags, kMinidumpContextMIPS64); + + context_.context_flags = kMinidumpContextMIPS64All; + + static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs), + "GPRs size mismatch"); + memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); + context_.mdhi = context_snapshot->mdhi; + context_.mdlo = context_snapshot->mdlo; + context_.epc = context_snapshot->cp0_epc; + context_.badvaddr = context_snapshot->cp0_badvaddr; + context_.status = context_snapshot->cp0_status; + context_.cause = context_snapshot->cp0_cause; + + static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs), + "FPRs size mismatch"); + memcpy(context_.fpregs.dregs, + context_snapshot->fpregs.dregs, + sizeof(context_.fpregs.dregs)); + context_.fpcsr = context_snapshot->fpcsr; + context_.fir = context_snapshot->fir; + + for (size_t index = 0; index < 3; ++index) { + context_.hi[index] = context_snapshot->hi[index]; + context_.lo[index] = context_snapshot->lo[index]; + } + context_.dsp_control = context_snapshot->dsp_control; +} + +bool MinidumpContextMIPS64Writer::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + return file_writer->Write(&context_, sizeof(context_)); +} + +size_t MinidumpContextMIPS64Writer::ContextSize() const { + DCHECK_GE(state(), kStateFrozen); + return sizeof(context_); +} + } // namespace crashpad diff --git a/minidump/minidump_context_writer.h b/minidump/minidump_context_writer.h index fb3b1513..d4ab936e 100644 --- a/minidump/minidump_context_writer.h +++ b/minidump/minidump_context_writer.h @@ -235,6 +235,86 @@ class MinidumpContextARM64Writer final : public MinidumpContextWriter { DISALLOW_COPY_AND_ASSIGN(MinidumpContextARM64Writer); }; +//! \brief The writer for a MinidumpContextMIPS structure in a minidump file. +class MinidumpContextMIPSWriter final : public MinidumpContextWriter { + public: + MinidumpContextMIPSWriter(); + ~MinidumpContextMIPSWriter() override; + + //! \brief Initializes the MinidumpContextMIPS based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutation of context() may be done before + //! calling this method, and it is not normally necessary to alter + //! context() after calling this method. + void InitializeFromSnapshot(const CPUContextMIPS* context_snapshot); + + //! \brief Returns a pointer to the context structure that this object will + //! write. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. The context structure + //! must only be modified while this object is in the #kStateMutable + //! state. + MinidumpContextMIPS* context() { return &context_; } + + protected: + // MinidumpWritable: + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpContextWriter: + size_t ContextSize() const override; + + private: + MinidumpContextMIPS context_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpContextMIPSWriter); +}; + +//! \brief The writer for a MinidumpContextMIPS64 structure in a minidump file. +class MinidumpContextMIPS64Writer final : public MinidumpContextWriter { + public: + MinidumpContextMIPS64Writer(); + ~MinidumpContextMIPS64Writer() override; + + //! \brief Initializes the MinidumpContextMIPS based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutation of context() may be done before + //! calling this method, and it is not normally necessary to alter + //! context() after calling this method. + void InitializeFromSnapshot(const CPUContextMIPS64* context_snapshot); + + //! \brief Returns a pointer to the context structure that this object will + //! write. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. The context structure + //! must only be modified while this object is in the #kStateMutable + //! state. + MinidumpContextMIPS64* context() { return &context_; } + + protected: + // MinidumpWritable: + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpContextWriter: + size_t ContextSize() const override; + + private: + MinidumpContextMIPS64 context_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpContextMIPS64Writer); +}; + } // namespace crashpad #endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_ diff --git a/minidump/minidump_context_writer_test.cc b/minidump/minidump_context_writer_test.cc index 0122683c..3216a906 100644 --- a/minidump/minidump_context_writer_test.cc +++ b/minidump/minidump_context_writer_test.cc @@ -183,6 +183,36 @@ TEST(MinidumpContextWriter, ARM64_FromSnapshot) { context, ExpectMinidumpContextARM64, kSeed); } +TEST(MinidumpContextWriter, MIPS_Zeros) { + EmptyContextTest( + ExpectMinidumpContextMIPS); +} + +TEST(MinidumpContextWriter, MIPS64_Zeros) { + EmptyContextTest( + ExpectMinidumpContextMIPS64); +} + +TEST(MinidumpContextWriter, MIPS_FromSnapshot) { + constexpr uint32_t kSeed = 32; + CPUContextMIPS context_mips; + CPUContext context; + context.mipsel = &context_mips; + InitializeCPUContextMIPS(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextMIPS, kSeed); +} + +TEST(MinidumpContextWriter, MIPS64_FromSnapshot) { + constexpr uint32_t kSeed = 64; + CPUContextMIPS64 context_mips; + CPUContext context; + context.mips64 = &context_mips; + InitializeCPUContextMIPS64(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextMIPS64, kSeed); +} + } // namespace } // namespace test } // namespace crashpad diff --git a/minidump/minidump_misc_info_writer.cc b/minidump/minidump_misc_info_writer.cc index c1e24892..d83ed237 100644 --- a/minidump/minidump_misc_info_writer.cc +++ b/minidump/minidump_misc_info_writer.cc @@ -122,6 +122,10 @@ std::string MinidumpMiscInfoDebugBuildString() { static constexpr char kCPU[] = "arm"; #elif defined(ARCH_CPU_ARM64) static constexpr char kCPU[] = "arm64"; +#elif defined(ARCH_CPU_MIPSEL) + static constexpr char kCPU[] = "mips"; +#elif defined(ARCH_CPU_MIPS64EL) + static constexpr char kCPU[] = "mips64"; #else #error define kCPU for this CPU #endif diff --git a/minidump/test/minidump_context_test_util.cc b/minidump/test/minidump_context_test_util.cc index 0dc3a971..28f94106 100644 --- a/minidump/test/minidump_context_test_util.cc +++ b/minidump/test/minidump_context_test_util.cc @@ -195,6 +195,80 @@ void InitializeMinidumpContextARM64(MinidumpContextARM64* context, context->fpcr = value++; } +void InitializeMinidumpContextMIPS(MinidumpContextMIPS* context, + uint32_t seed) { + if (seed == 0) { + memset(context, 0, sizeof(*context)); + context->context_flags = kMinidumpContextMIPS; + return; + } + + context->context_flags = kMinidumpContextMIPSAll; + + uint32_t value = seed; + + for (size_t index = 0; index < arraysize(context->regs); ++index) { + context->regs[index] = value++; + } + + context->mdlo = value++; + context->mdhi = value++; + context->epc = value++; + context->badvaddr = value++; + context->status = value++; + context->cause = value++; + + for (size_t index = 0; index < arraysize(context->fpregs.fregs); ++index) { + context->fpregs.fregs[index]._fp_fregs = static_cast(value++); + } + + context->fpcsr = value++; + context->fir = value++; + + for (size_t index = 0; index < 3; ++index) { + context->hi[index] = value++; + context->lo[index] = value++; + } + + context->dsp_control = value++; +} + +void InitializeMinidumpContextMIPS64(MinidumpContextMIPS64* context, + uint32_t seed) { + if (seed == 0) { + memset(context, 0, sizeof(*context)); + context->context_flags = kMinidumpContextMIPS64; + return; + } + + context->context_flags = kMinidumpContextMIPS64All; + + uint64_t value = seed; + + for (size_t index = 0; index < arraysize(context->regs); ++index) { + context->regs[index] = value++; + } + + context->mdlo = value++; + context->mdhi = value++; + context->epc = value++; + context->badvaddr = value++; + context->status = value++; + context->cause = value++; + + for (size_t index = 0; index < arraysize(context->fpregs.dregs); ++index) { + context->fpregs.dregs[index] = static_cast(value++); + } + context->fpcsr = value++; + context->fir = value++; + + for (size_t index = 0; index < 3; ++index) { + context->hi[index] = value++; + context->lo[index] = value++; + } + context->dsp_control = value++; +} + namespace { // Using gtest assertions, compares |expected| to |observed|. This is @@ -453,5 +527,70 @@ void ExpectMinidumpContextARM64(uint32_t expect_seed, } } +void ExpectMinidumpContextMIPS(uint32_t expect_seed, + const MinidumpContextMIPS* observed, + bool snapshot) { + MinidumpContextMIPS expected; + InitializeMinidumpContextMIPS(&expected, expect_seed); + + EXPECT_EQ(observed->context_flags, expected.context_flags); + + for (size_t index = 0; index < arraysize(expected.regs); ++index) { + EXPECT_EQ(observed->regs[index], expected.regs[index]); + } + + EXPECT_EQ(observed->mdlo, expected.mdlo); + EXPECT_EQ(observed->mdhi, expected.mdhi); + EXPECT_EQ(observed->epc, expected.epc); + EXPECT_EQ(observed->badvaddr, expected.badvaddr); + EXPECT_EQ(observed->status, expected.status); + EXPECT_EQ(observed->cause, expected.cause); + + for (size_t index = 0; index < arraysize(expected.fpregs.fregs); ++index) { + EXPECT_EQ(observed->fpregs.fregs[index]._fp_fregs, + expected.fpregs.fregs[index]._fp_fregs); + } + EXPECT_EQ(observed->fpcsr, expected.fpcsr); + EXPECT_EQ(observed->fir, expected.fir); + + for (size_t index = 0; index < 3; ++index) { + EXPECT_EQ(observed->hi[index], expected.hi[index]); + EXPECT_EQ(observed->lo[index], expected.lo[index]); + } + EXPECT_EQ(observed->dsp_control, expected.dsp_control); +} + +void ExpectMinidumpContextMIPS64(uint32_t expect_seed, + const MinidumpContextMIPS64* observed, + bool snapshot) { + MinidumpContextMIPS64 expected; + InitializeMinidumpContextMIPS64(&expected, expect_seed); + + EXPECT_EQ(observed->context_flags, expected.context_flags); + + for (size_t index = 0; index < arraysize(expected.regs); ++index) { + EXPECT_EQ(observed->regs[index], expected.regs[index]); + } + + EXPECT_EQ(observed->mdlo, expected.mdlo); + EXPECT_EQ(observed->mdhi, expected.mdhi); + EXPECT_EQ(observed->epc, expected.epc); + EXPECT_EQ(observed->badvaddr, expected.badvaddr); + EXPECT_EQ(observed->status, expected.status); + EXPECT_EQ(observed->cause, expected.cause); + + for (size_t index = 0; index < arraysize(expected.fpregs.dregs); ++index) { + EXPECT_EQ(observed->fpregs.dregs[index], expected.fpregs.dregs[index]); + } + EXPECT_EQ(observed->fpcsr, expected.fpcsr); + EXPECT_EQ(observed->fir, expected.fir); + + for (size_t index = 0; index < 3; ++index) { + EXPECT_EQ(observed->hi[index], expected.hi[index]); + EXPECT_EQ(observed->lo[index], expected.lo[index]); + } + EXPECT_EQ(observed->dsp_control, expected.dsp_control); +} + } // namespace test } // namespace crashpad diff --git a/minidump/test/minidump_context_test_util.h b/minidump/test/minidump_context_test_util.h index 64f79cde..080e04a0 100644 --- a/minidump/test/minidump_context_test_util.h +++ b/minidump/test/minidump_context_test_util.h @@ -44,6 +44,9 @@ void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context, void InitializeMinidumpContextARM(MinidumpContextARM* context, uint32_t seed); void InitializeMinidumpContextARM64(MinidumpContextARM64* context, uint32_t seed); +void InitializeMinidumpContextMIPS(MinidumpContextMIPS* context, uint32_t seed); +void InitializeMinidumpContextMIPS64(MinidumpContextMIPS* context, + uint32_t seed); //! \} //! \brief Verifies, via gtest assertions, that a context structure contains @@ -76,6 +79,12 @@ void ExpectMinidumpContextARM(uint32_t expect_seed, void ExpectMinidumpContextARM64(uint32_t expect_seed, const MinidumpContextARM64* observed, bool snapshot); +void ExpectMinidumpContextMIPS(uint32_t expect_seed, + const MinidumpContextMIPS* observed, + bool snapshot); +void ExpectMinidumpContextMIPS64(uint32_t expect_seed, + const MinidumpContextMIPS64* observed, + bool snapshot); //! \} } // namespace test diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 6bafabb3..134df8b1 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -416,7 +416,8 @@ source_set("snapshot_test") { libs = [ "dl" ] } - if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + if ((crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) && + target_cpu != "mipsel" && target_cpu != "mips64el") { data_deps += [ ":crashpad_snapshot_test_both_dt_hash_styles" ] } @@ -477,7 +478,8 @@ crashpad_loadable_module("crashpad_snapshot_test_module_small") { deps += [ "../third_party/mini_chromium:base" ] } -if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { +if ((crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) && + target_cpu != "mipsel" && target_cpu != "mips64el") { crashpad_loadable_module("crashpad_snapshot_test_both_dt_hash_styles") { testonly = true sources = [ diff --git a/snapshot/capture_memory.cc b/snapshot/capture_memory.cc index 4327fbda..98f400e8 100644 --- a/snapshot/capture_memory.cc +++ b/snapshot/capture_memory.cc @@ -106,6 +106,10 @@ void CaptureMemory::PointedToByContext(const CPUContext& context, MaybeCaptureMemoryAround(delegate, context.arm->regs[i]); } } +#elif defined(ARCH_CPU_MIPS_FAMILY) + for (size_t i = 0; i < arraysize(context.mipsel->regs); ++i) { + MaybeCaptureMemoryAround(delegate, context.mipsel->regs[i]); + } #else #error Port. #endif diff --git a/snapshot/cpu_architecture.h b/snapshot/cpu_architecture.h index 6116d4a1..811a7209 100644 --- a/snapshot/cpu_architecture.h +++ b/snapshot/cpu_architecture.h @@ -37,7 +37,13 @@ enum CPUArchitecture { kCPUArchitectureARM, //! \brief 64-bit ARM. - kCPUArchitectureARM64 + kCPUArchitectureARM64, + + //! \brief 32-bit MIPSEL. + kCPUArchitectureMIPSEL, + + //! \brief 64-bit MIPSEL. + kCPUArchitectureMIPS64EL }; } // namespace crashpad diff --git a/snapshot/cpu_context.cc b/snapshot/cpu_context.cc index 553a1a5d..4d7c1e5a 100644 --- a/snapshot/cpu_context.cc +++ b/snapshot/cpu_context.cc @@ -194,9 +194,11 @@ bool CPUContext::Is64Bit() const { switch (architecture) { case kCPUArchitectureX86_64: case kCPUArchitectureARM64: + case kCPUArchitectureMIPS64EL: return true; case kCPUArchitectureX86: case kCPUArchitectureARM: + case kCPUArchitectureMIPSEL: return false; default: NOTREACHED(); diff --git a/snapshot/cpu_context.h b/snapshot/cpu_context.h index b104217d..4dde9436 100644 --- a/snapshot/cpu_context.h +++ b/snapshot/cpu_context.h @@ -306,6 +306,52 @@ struct CPUContextARM64 { uint32_t fpcr; }; +//! \brief A context structure carrying MIPS CPU state. +struct CPUContextMIPS { + uint64_t regs[32]; + uint32_t mdlo; + uint32_t mdhi; + uint32_t cp0_epc; + uint32_t cp0_badvaddr; + uint32_t cp0_status; + uint32_t cp0_cause; + uint32_t hi[3]; + uint32_t lo[3]; + uint32_t dsp_control; + union { + double dregs[32]; + struct { + float _fp_fregs; + uint32_t _fp_pad; + } fregs[32]; + } fpregs; + uint32_t fpcsr; + uint32_t fir; +}; + +//! \brief A context structure carrying MIPS64 CPU state. +struct CPUContextMIPS64 { + uint64_t regs[32]; + uint64_t mdlo; + uint64_t mdhi; + uint64_t cp0_epc; + uint64_t cp0_badvaddr; + uint64_t cp0_status; + uint64_t cp0_cause; + uint64_t hi[3]; + uint64_t lo[3]; + uint64_t dsp_control; + union { + double dregs[32]; + struct { + float _fp_fregs; + uint32_t _fp_pad; + } fregs[32]; + } fpregs; + uint64_t fpcsr; + uint64_t fir; +}; + //! \brief A context structure capable of carrying the context of any supported //! CPU architecture. struct CPUContext { @@ -334,6 +380,8 @@ struct CPUContext { CPUContextX86_64* x86_64; CPUContextARM* arm; CPUContextARM64* arm64; + CPUContextMIPS* mipsel; + CPUContextMIPS64* mips64; }; }; diff --git a/snapshot/linux/cpu_context_linux.h b/snapshot/linux/cpu_context_linux.h index 4ca679a9..37fbc432 100644 --- a/snapshot/linux/cpu_context_linux.h +++ b/snapshot/linux/cpu_context_linux.h @@ -138,6 +138,42 @@ void InitializeCPUContextARM64_OnlyFPSIMD( #endif // ARCH_CPU_ARM_FAMILY || DOXYGEN +#if defined(ARCH_CPU_MIPS_FAMILY) || DOXYGEN + +//! \brief Initializes a CPUContextMIPS structure from native context +//! structures on Linux. +//! +//! This function has template specializations for MIPSEL and MIPS64EL +//! architecture contexts, using ContextTraits32 or ContextTraits64 as template +//! parameter, respectively. +//! +//! \param[in] thread_context The native thread context. +//! \param[in] float_context The native float context. +//! \param[out] context The CPUContextMIPS structure to initialize. +template +void InitializeCPUContextMIPS( + const typename Traits::SignalThreadContext& thread_context, + const typename Traits::SignalFloatContext& float_context, + typename Traits::CPUContext* context) { + static_assert(sizeof(context->regs) == sizeof(thread_context.regs), + "registers size mismatch"); + static_assert(sizeof(context->fpregs) == sizeof(float_context.fpregs), + "fp registers size mismatch"); + memcpy(&context->regs, &thread_context.regs, sizeof(context->regs)); + context->mdlo = thread_context.lo; + context->mdhi = thread_context.hi; + context->cp0_epc = thread_context.cp0_epc; + context->cp0_badvaddr = thread_context.cp0_badvaddr; + context->cp0_status = thread_context.cp0_status; + context->cp0_cause = thread_context.cp0_cause; + + memcpy(&context->fpregs, &float_context.fpregs, sizeof(context->fpregs)); + context->fpcsr = float_context.fpcsr; + context->fir = float_context.fpu_id; +}; + +#endif // ARCH_CPU_MIPS_FAMILY || DOXYGEN + } // namespace internal } // namespace crashpad diff --git a/snapshot/linux/exception_snapshot_linux.cc b/snapshot/linux/exception_snapshot_linux.cc index c57e072e..4256f942 100644 --- a/snapshot/linux/exception_snapshot_linux.cc +++ b/snapshot/linux/exception_snapshot_linux.cc @@ -268,6 +268,61 @@ bool ExceptionSnapshotLinux::ReadContext( } while (true); } +#elif defined(ARCH_CPU_MIPS_FAMILY) + +template +static bool ReadContext(ProcessReaderLinux* reader, + LinuxVMAddress context_address, + typename Traits::CPUContext* dest_context) { + ProcessMemory* memory = reader->Memory(); + + LinuxVMAddress gregs_address = context_address + + offsetof(UContext, mcontext) + + offsetof(typename Traits::MContext, gregs); + + typename Traits::SignalThreadContext thread_context; + if (!memory->Read(gregs_address, sizeof(thread_context), &thread_context)) { + LOG(ERROR) << "Couldn't read gregs"; + return false; + } + + LinuxVMAddress fpregs_address = context_address + + offsetof(UContext, mcontext) + + offsetof(typename Traits::MContext, fpregs); + + typename Traits::SignalFloatContext fp_context; + if (!memory->Read(fpregs_address, sizeof(fp_context), &fp_context)) { + LOG(ERROR) << "Couldn't read fpregs"; + return false; + } + + InitializeCPUContextMIPS(thread_context, fp_context, dest_context); + + return true; +} + +template <> +bool ExceptionSnapshotLinux::ReadContext( + ProcessReaderLinux* reader, + LinuxVMAddress context_address) { + context_.architecture = kCPUArchitectureMIPSEL; + context_.mipsel = &context_union_.mipsel; + + return internal::ReadContext( + reader, context_address, context_.mipsel); +} + +template <> +bool ExceptionSnapshotLinux::ReadContext( + ProcessReaderLinux* reader, + LinuxVMAddress context_address) { + context_.architecture = kCPUArchitectureMIPS64EL; + context_.mips64 = &context_union_.mips64; + + return internal::ReadContext( + reader, context_address, context_.mips64); +} + #endif // ARCH_CPU_X86_FAMILY bool ExceptionSnapshotLinux::Initialize(ProcessReaderLinux* process_reader, diff --git a/snapshot/linux/exception_snapshot_linux.h b/snapshot/linux/exception_snapshot_linux.h index 0dcead7b..ea0cd210 100644 --- a/snapshot/linux/exception_snapshot_linux.h +++ b/snapshot/linux/exception_snapshot_linux.h @@ -81,6 +81,9 @@ class ExceptionSnapshotLinux final : public ExceptionSnapshot { #elif defined(ARCH_CPU_ARM_FAMILY) CPUContextARM arm; CPUContextARM64 arm64; +#elif defined(ARCH_CPU_MIPS_FAMILY) + CPUContextMIPS mipsel; + CPUContextMIPS64 mips64; #endif } context_union_; CPUContext context_; diff --git a/snapshot/linux/exception_snapshot_linux_test.cc b/snapshot/linux/exception_snapshot_linux_test.cc index 5a0c6ae1..df9ad9e9 100644 --- a/snapshot/linux/exception_snapshot_linux_test.cc +++ b/snapshot/linux/exception_snapshot_linux_test.cc @@ -266,6 +266,36 @@ void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { sizeof(actual.arm64->fpsimd)), 0); } +#elif defined(ARCH_CPU_MIPS_FAMILY) +using NativeCPUContext = ucontext_t; + +void InitializeContext(NativeCPUContext* context) { + for (size_t reg = 0; reg < arraysize(context->uc_mcontext.gregs); ++reg) { + context->uc_mcontext.gregs[reg] = reg; + } + memset(&context->uc_mcontext.fpregs, 44, sizeof(context->uc_mcontext.fpregs)); +} + +void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { +#if defined(ARCH_CPU_MIPSEL) + EXPECT_EQ(actual.architecture, kCPUArchitectureMIPSEL); +#define CPU_ARCH_NAME mipsel +#elif defined(ARCH_CPU_MIPS64EL) + EXPECT_EQ(actual.architecture, kCPUArchitectureMIPS64EL); +#define CPU_ARCH_NAME mips64 +#endif + + for (size_t reg = 0; reg < arraysize(expected.uc_mcontext.gregs); ++reg) { + EXPECT_EQ(actual.CPU_ARCH_NAME->regs[reg], expected.uc_mcontext.gregs[reg]); + } + + EXPECT_EQ(memcmp(&actual.CPU_ARCH_NAME->fpregs, + &expected.uc_mcontext.fpregs, + sizeof(actual.CPU_ARCH_NAME->fpregs)), + 0); +#undef CPU_ARCH_NAME +} + #else #error Port. #endif diff --git a/snapshot/linux/process_reader_linux.cc b/snapshot/linux/process_reader_linux.cc index 9a5b092f..038d5cef 100644 --- a/snapshot/linux/process_reader_linux.cc +++ b/snapshot/linux/process_reader_linux.cc @@ -100,6 +100,9 @@ void ProcessReaderLinux::Thread::InitializeStack(ProcessReaderLinux* reader) { #elif defined(ARCH_CPU_ARM_FAMILY) stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.sp : thread_info.thread_context.t32.sp; +#elif defined(ARCH_CPU_MIPS_FAMILY) + stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.regs[29] + : thread_info.thread_context.t32.regs[29]; #else #error Port. #endif diff --git a/snapshot/linux/signal_context.h b/snapshot/linux/signal_context.h index 63202abe..11002468 100644 --- a/snapshot/linux/signal_context.h +++ b/snapshot/linux/signal_context.h @@ -41,8 +41,14 @@ union Sigval { template struct Siginfo { int32_t signo; +#ifdef ARCH_CPU_MIPS_FAMILY + // Attribute order for signo_t defined in kernel is different for MIPS. + int32_t code; + int32_t err; +#else int32_t err; int32_t code; +#endif typename Traits::UInteger32_64Only padding; union { @@ -301,6 +307,121 @@ static_assert(offsetof(UContext, reserved) == "reserved space offset mismtach"); #endif +#elif defined(ARCH_CPU_MIPS_FAMILY) + +struct MContext32 { + uint32_t regmask; + uint32_t status; + uint64_t pc; + uint64_t gregs[32]; + struct { + float _fp_fregs; + unsigned int _fp_pad; + } fpregs[32]; + uint32_t fp_owned; + uint32_t fpc_csr; + uint32_t fpc_eir; + uint32_t used_math; + uint32_t dsp; + uint64_t mdhi; + uint64_t mdlo; + uint32_t hi1; + uint32_t lo1; + uint32_t hi2; + uint32_t lo2; + uint32_t hi3; + uint32_t lo3; +}; + +struct MContext64 { + uint64_t gregs[32]; + double fpregs[32]; + uint64_t mdhi; + uint64_t hi1; + uint64_t hi2; + uint64_t hi3; + uint64_t mdlo; + uint64_t lo1; + uint64_t lo2; + uint64_t lo3; + uint64_t pc; + uint32_t fpc_csr; + uint32_t used_math; + uint32_t dsp; + uint32_t __glibc_reserved1; +}; + +struct SignalThreadContext32 { + uint64_t regs[32]; + uint32_t lo; + uint32_t hi; + uint32_t cp0_epc; + uint32_t cp0_badvaddr; + uint32_t cp0_status; + uint32_t cp0_cause; + + SignalThreadContext32() {} + explicit SignalThreadContext32( + const struct ThreadContext::t32_t& thread_context) { + for (size_t reg = 0; reg < 32; ++reg) { + regs[reg] = thread_context.regs[reg]; + } + lo = thread_context.lo; + hi = thread_context.hi; + cp0_epc = thread_context.cp0_epc; + cp0_badvaddr = thread_context.cp0_badvaddr; + cp0_status = thread_context.cp0_status; + cp0_cause = thread_context.cp0_cause; + } +}; + +struct ContextTraits32 : public Traits32 { + using MContext = MContext32; + using SignalThreadContext = SignalThreadContext32; + using SignalFloatContext = FloatContext::f32_t; + using CPUContext = CPUContextMIPS; +}; + +struct ContextTraits64 : public Traits64 { + using MContext = MContext64; + using SignalThreadContext = ThreadContext::t64_t; + using SignalFloatContext = FloatContext::f64_t; + using CPUContext = CPUContextMIPS64; +}; + +template +struct UContext { + typename Traits::ULong flags; + typename Traits::Address link; + SignalStack stack; + typename Traits::ULong_32Only alignment_padding_; + typename Traits::MContext mcontext; + Sigset sigmask; +}; + +#if defined(ARCH_CPU_MIPSEL) +static_assert(offsetof(UContext, mcontext) == + offsetof(ucontext_t, uc_mcontext), + "context offset mismatch"); +static_assert(offsetof(UContext, mcontext.gregs) == + offsetof(ucontext_t, uc_mcontext.gregs), + "context offset mismatch"); +static_assert(offsetof(UContext, mcontext.fpregs) == + offsetof(ucontext_t, uc_mcontext.fpregs), + "context offset mismatch"); + +#elif defined(ARCH_CPU_MIPS64EL) +static_assert(offsetof(UContext, mcontext) == + offsetof(ucontext_t, uc_mcontext), + "context offset mismtach"); +static_assert(offsetof(UContext, mcontext.gregs) == + offsetof(ucontext_t, uc_mcontext.gregs), + "context offset mismatch"); +static_assert(offsetof(UContext, mcontext.fpregs) == + offsetof(ucontext_t, uc_mcontext.fpregs), + "context offset mismatch"); +#endif + #else #error Port. #endif // ARCH_CPU_X86_FAMILY diff --git a/snapshot/linux/system_snapshot_linux.cc b/snapshot/linux/system_snapshot_linux.cc index 4c392888..8564d3d4 100644 --- a/snapshot/linux/system_snapshot_linux.cc +++ b/snapshot/linux/system_snapshot_linux.cc @@ -200,6 +200,9 @@ CPUArchitecture SystemSnapshotLinux::GetCPUArchitecture() const { #elif defined(ARCH_CPU_ARM_FAMILY) return process_reader_->Is64Bit() ? kCPUArchitectureARM64 : kCPUArchitectureARM; +#elif defined(ARCH_CPU_MIPS_FAMILY) + return process_reader_->Is64Bit() ? kCPUArchitectureMIPS64EL + : kCPUArchitectureMIPSEL; #else #error port to your architecture #endif @@ -212,6 +215,9 @@ uint32_t SystemSnapshotLinux::CPURevision() const { #elif defined(ARCH_CPU_ARM_FAMILY) // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30 return 0; +#elif defined(ARCH_CPU_MIPS_FAMILY) + // Not implementable on MIPS + return 0; #else #error port to your architecture #endif @@ -229,6 +235,9 @@ std::string SystemSnapshotLinux::CPUVendor() const { #elif defined(ARCH_CPU_ARM_FAMILY) // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30 return std::string(); +#elif defined(ARCH_CPU_MIPS_FAMILY) + // Not implementable on MIPS + return std::string(); #else #error port to your architecture #endif @@ -359,6 +368,9 @@ bool SystemSnapshotLinux::NXEnabled() const { #elif defined(ARCH_CPU_ARM_FAMILY) // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30 return false; +#elif defined(ARCH_CPU_MIPS_FAMILY) + // Not implementable on MIPS + return false; #else #error Port. #endif // ARCH_CPU_X86_FAMILY diff --git a/snapshot/linux/thread_snapshot_linux.cc b/snapshot/linux/thread_snapshot_linux.cc index 084ed73a..8ffbfe8b 100644 --- a/snapshot/linux/thread_snapshot_linux.cc +++ b/snapshot/linux/thread_snapshot_linux.cc @@ -69,6 +69,22 @@ bool ThreadSnapshotLinux::Initialize(ProcessReaderLinux* process_reader, thread.thread_info.float_context.f32, context_.arm); } +#elif defined(ARCH_CPU_MIPS_FAMILY) + if (process_reader->Is64Bit()) { + context_.architecture = kCPUArchitectureMIPS64EL; + context_.mips64 = &context_union_.mips64; + InitializeCPUContextMIPS( + thread.thread_info.thread_context.t64, + thread.thread_info.float_context.f64, + context_.mips64); + } else { + context_.architecture = kCPUArchitectureMIPSEL; + context_.mipsel = &context_union_.mipsel; + InitializeCPUContextMIPS( + SignalThreadContext32(thread.thread_info.thread_context.t32), + thread.thread_info.float_context.f32, + context_.mipsel); + } #else #error Port. #endif diff --git a/snapshot/linux/thread_snapshot_linux.h b/snapshot/linux/thread_snapshot_linux.h index 8fc7e17e..17e471f3 100644 --- a/snapshot/linux/thread_snapshot_linux.h +++ b/snapshot/linux/thread_snapshot_linux.h @@ -65,6 +65,9 @@ class ThreadSnapshotLinux final : public ThreadSnapshot { #elif defined(ARCH_CPU_ARM_FAMILY) CPUContextARM arm; CPUContextARM64 arm64; +#elif defined(ARCH_CPU_MIPS_FAMILY) + CPUContextMIPS mipsel; + CPUContextMIPS64 mips64; #else #error Port. #endif // ARCH_CPU_X86_FAMILY diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index fc746f2c..b795d92f 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -52,7 +52,6 @@ 'target_name': 'crashpad_snapshot_test', 'type': 'executable', 'dependencies': [ - 'crashpad_snapshot_test_both_dt_hash_styles', 'crashpad_snapshot_test_lib', 'crashpad_snapshot_test_module', 'crashpad_snapshot_test_module_large', @@ -104,6 +103,10 @@ 'win/system_snapshot_win_test.cc', ], 'conditions': [ + # .gnu.hash is incompatible with the MIPS ABI + ['target_arch!="mips"', { + 'dependencies': ['crashpad_snapshot_test_both_dt_hash_styles'] + }], ['OS=="mac"', { 'dependencies': [ 'crashpad_snapshot_test_module_crashy_initializer', @@ -228,13 +231,17 @@ { 'target_name': 'crashpad_snapshot_test_both_dt_hash_styles', 'type': 'executable', - 'sources': [ - 'hash_types_test.cc', - ], - - 'ldflags': [ - # This makes `ld` emit both .hash and .gnu.hash sections. - '-Wl,--hash-style=both', + 'conditions': [ + # .gnu.hash is incompatible with the MIPS ABI + ['target_arch!="mips"', { + 'sources': [ + 'hash_types_test.cc', + ], + 'ldflags': [ + # This makes `ld` emit both .hash and .gnu.hash sections. + '-Wl,--hash-style=both', + ]}, + ] ], }, ], diff --git a/snapshot/test/test_cpu_context.cc b/snapshot/test/test_cpu_context.cc index 09a5e799..1e785af8 100644 --- a/snapshot/test/test_cpu_context.cc +++ b/snapshot/test/test_cpu_context.cc @@ -220,5 +220,77 @@ void InitializeCPUContextARM64(CPUContext* context, uint32_t seed) { arm64->fpcr = value++; } +void InitializeCPUContextMIPS(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureMIPSEL; + CPUContextMIPS* mipsel = context->mipsel; + + if (seed == 0) { + memset(mipsel, 0, sizeof(*mipsel)); + return; + } + + uint32_t value = seed; + + for (size_t index = 0; index < arraysize(mipsel->regs); ++index) { + mipsel->regs[index] = value++; + } + + mipsel->mdlo = value++; + mipsel->mdhi = value++; + mipsel->cp0_epc = value++; + mipsel->cp0_badvaddr = value++; + mipsel->cp0_status = value++; + mipsel->cp0_cause = value++; + + for (size_t index = 0; index < arraysize(mipsel->fpregs.fregs); ++index) { + mipsel->fpregs.fregs[index]._fp_fregs = static_cast(value++); + } + + mipsel->fpcsr = value++; + mipsel->fir = value++; + + for (size_t index = 0; index < 3; ++index) { + mipsel->hi[index] = value++; + mipsel->lo[index] = value++; + } + mipsel->dsp_control = value++; +} + +void InitializeCPUContextMIPS64(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureMIPS64EL; + CPUContextMIPS64* mips64 = context->mips64; + + if (seed == 0) { + memset(mips64, 0, sizeof(*mips64)); + return; + } + + uint64_t value = seed; + + for (size_t index = 0; index < arraysize(mips64->regs); ++index) { + mips64->regs[index] = value++; + } + + mips64->mdlo = value++; + mips64->mdhi = value++; + mips64->cp0_epc = value++; + mips64->cp0_badvaddr = value++; + mips64->cp0_status = value++; + mips64->cp0_cause = value++; + + for (size_t index = 0; index < arraysize(mips64->fpregs.dregs); ++index) { + mips64->fpregs.dregs[index] = static_cast(value++); + } + + mips64->fpcsr = value++; + mips64->fir = value++; + + for (size_t index = 0; index < 3; ++index) { + mips64->hi[index] = value++; + mips64->lo[index] = value++; + } + mips64->dsp_control = value++; +} + } // namespace test } // namespace crashpad diff --git a/snapshot/test/test_cpu_context.h b/snapshot/test/test_cpu_context.h index 0b4b5376..ca1b6b7b 100644 --- a/snapshot/test/test_cpu_context.h +++ b/snapshot/test/test_cpu_context.h @@ -61,6 +61,8 @@ void InitializeCPUContextX86(CPUContext* context, uint32_t seed); void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed); void InitializeCPUContextARM(CPUContext* context, uint32_t seed); void InitializeCPUContextARM64(CPUContext* context, uint32_t seed); +void InitializeCPUContextMIPS(CPUContext* context, uint32_t seed); +void InitializeCPUContextMIPS64(CPUContext* context, uint32_t seed); //! \} } // namespace test diff --git a/test/linux/get_tls.cc b/test/linux/get_tls.cc index 47d041e4..452724de 100644 --- a/test/linux/get_tls.cc +++ b/test/linux/get_tls.cc @@ -35,6 +35,20 @@ LinuxVMAddress GetTLS() { tls = tls_32; #elif defined(ARCH_CPU_X86_64) asm("movq %%fs:0x0, %0" : "=r"(tls)); +#elif defined(ARCH_CPU_MIPSEL) + uint32_t tls_32; + asm("rdhwr $3,$29\n\t" + "move %0,$3\n\t" + : "=r"(tls_32) + : + : "$3"); + tls = tls_32; +#elif defined(ARCH_CPU_MIPS64EL) + asm("rdhwr $3,$29\n\t" + "move %0,$3\n\t" + : "=r"(tls) + : + : "$3"); #else #error Port. #endif // ARCH_CPU_ARMEL diff --git a/test/multiprocess_posix.cc b/test/multiprocess_posix.cc index 96b8ad26..cdb5385e 100644 --- a/test/multiprocess_posix.cc +++ b/test/multiprocess_posix.cc @@ -158,7 +158,7 @@ void Multiprocess::SetExpectedChildTermination(TerminationReason reason, } void Multiprocess::SetExpectedChildTerminationBuiltinTrap() { -#if defined(ARCH_CPU_ARM64) +#if defined(ARCH_CPU_ARM64) || defined(ARCH_CPU_MIPS_FAMILY) SetExpectedChildTermination(kTerminationSignal, SIGTRAP); #else SetExpectedChildTermination(kTerminationSignal, SIGILL); diff --git a/util/linux/auxiliary_vector_test.cc b/util/linux/auxiliary_vector_test.cc index 3a69ead1..add2d1e2 100644 --- a/util/linux/auxiliary_vector_test.cc +++ b/util/linux/auxiliary_vector_test.cc @@ -37,7 +37,12 @@ // TODO(jperaza): This symbol isn't defined when building in chromium for // Android. There may be another symbol to use. extern "C" { -extern void _start(); +#if defined(ARCH_CPU_MIPS_FAMILY) +#define START_SYMBOL __start +#else +#define START_SYMBOL _start +#endif +extern void START_SYMBOL(); } // extern "C" #endif @@ -70,7 +75,7 @@ void TestAgainstCloneOrSelf(pid_t pid) { #if !defined(OS_ANDROID) LinuxVMAddress entry_addr; ASSERT_TRUE(aux.GetValue(AT_ENTRY, &entry_addr)); - EXPECT_EQ(entry_addr, FromPointerCast(_start)); + EXPECT_EQ(entry_addr, FromPointerCast(START_SYMBOL)); #endif uid_t uid; diff --git a/util/linux/ptracer.cc b/util/linux/ptracer.cc index 63bee1e0..c6c92299 100644 --- a/util/linux/ptracer.cc +++ b/util/linux/ptracer.cc @@ -269,6 +269,131 @@ bool GetThreadArea64(pid_t tid, } return true; } +#elif defined(ARCH_CPU_MIPS_FAMILY) +// PTRACE_GETREGSET, introduced in Linux 2.6.34 (2225a122ae26), requires kernel +// support enabled by HAVE_ARCH_TRACEHOOK. This has been set for x86 (including +// x86_64) since Linux 2.6.28 (99bbc4b1e677a), but for MIPS only since +// Linux 3.13 (c0ff3c53d4f99). Older Linux kernels support PTRACE_GETREGS, +// and PTRACE_GETFPREGS instead, which don't allow checking the size of data +// copied. Also, PTRACE_GETREGS assumes register size of 64 bits even for 32 bit +// MIPS CPU (contrary to PTRACE_GETREGSET behavior), so we need buffer +// structure here. + +bool GetGeneralPurposeRegistersLegacy(pid_t tid, + ThreadContext* context, + bool can_log) { + ThreadContext context_buffer; + if (ptrace(PTRACE_GETREGS, tid, nullptr, &context_buffer.t64) != 0) { + PLOG_IF(ERROR, can_log) << "ptrace"; + return false; + } +// Bitness of target process can't be determined through ptrace here, so we +// assume target process has the same as current process, making cross-bit +// ptrace unsupported on MIPS for kernels older than 3.13 +#if defined(ARCH_CPU_MIPSEL) +#define THREAD_CONTEXT_FIELD t32 +#elif defined(ARCH_CPU_MIPS64EL) +#define THREAD_CONTEXT_FIELD t64 +#endif + for (size_t reg = 0; reg < 32; ++reg) { + context->THREAD_CONTEXT_FIELD.regs[reg] = context_buffer.t64.regs[reg]; + } + context->THREAD_CONTEXT_FIELD.lo = context_buffer.t64.lo; + context->THREAD_CONTEXT_FIELD.hi = context_buffer.t64.hi; + context->THREAD_CONTEXT_FIELD.cp0_epc = context_buffer.t64.cp0_epc; + context->THREAD_CONTEXT_FIELD.cp0_badvaddr = context_buffer.t64.cp0_badvaddr; + context->THREAD_CONTEXT_FIELD.cp0_status = context_buffer.t64.cp0_status; + context->THREAD_CONTEXT_FIELD.cp0_cause = context_buffer.t64.cp0_cause; +#undef THREAD_CONTEXT_FIELD + return true; +} + +bool GetFloatingPointRegistersLegacy(pid_t tid, + FloatContext* context, + bool can_log) { + if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) { + PLOG_IF(ERROR, can_log) << "ptrace"; + return false; + } + return true; +} + +bool GetFloatingPointRegisters32(pid_t tid, + FloatContext* context, + bool can_log) { + iovec iov; + iov.iov_base = &context->f32.fpregs; + iov.iov_len = sizeof(context->f32.fpregs); + if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) { + switch (errno) { + case EINVAL: + // fp may not be present + break; + case EIO: + return GetFloatingPointRegistersLegacy(tid, context, can_log); + default: + PLOG_IF(ERROR, can_log) << "ptrace"; + return false; + } + } + return true; +} + +bool GetFloatingPointRegisters64(pid_t tid, + FloatContext* context, + bool can_log) { + iovec iov; + iov.iov_base = &context->f64.fpregs; + iov.iov_len = sizeof(context->f64.fpregs); + if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f64.fpregs) != 0) { + switch (errno) { + case EINVAL: + // fp may not be present + break; + case EIO: + return GetFloatingPointRegistersLegacy(tid, context, can_log); + default: + PLOG_IF(ERROR, can_log) << "ptrace"; + return false; + } + } + return true; +} + +bool GetThreadArea32(pid_t tid, + const ThreadContext& context, + LinuxVMAddress* address, + bool can_log) { +#if defined(ARCH_CPU_MIPSEL) + void* result; + if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &result) != 0) { + PLOG_IF(ERROR, can_log) << "ptrace"; + return false; + } + *address = FromPointerCast(result); + return true; +#else + return false; +#endif +} + +bool GetThreadArea64(pid_t tid, + const ThreadContext& context, + LinuxVMAddress* address, + bool can_log) { + void* result; +#if defined(ARCH_CPU_MIPSEL) + if (ptrace(PTRACE_GET_THREAD_AREA_3264, tid, nullptr, &result) != 0) { +#else + if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &result) != 0) { +#endif + PLOG_IF(ERROR, can_log) << "ptrace"; + return false; + } + *address = FromPointerCast(result); + return true; +} + #else #error Port. #endif // ARCH_CPU_X86_FAMILY @@ -283,7 +408,7 @@ size_t GetGeneralPurposeRegistersAndLength(pid_t tid, PTRACE_GETREGSET, tid, reinterpret_cast(NT_PRSTATUS), &iov) != 0) { switch (errno) { -#if defined(ARCH_CPU_ARMEL) +#if defined(ARCH_CPU_ARMEL) || defined(ARCH_CPU_MIPS_FAMILY) case EIO: return GetGeneralPurposeRegistersLegacy(tid, context, can_log) ? sizeof(context->t32) diff --git a/util/linux/thread_info.h b/util/linux/thread_info.h index 91d0082f..5b55c24a 100644 --- a/util/linux/thread_info.h +++ b/util/linux/thread_info.h @@ -67,6 +67,18 @@ union ThreadContext { uint32_t pc; uint32_t cpsr; uint32_t orig_r0; +#elif defined(ARCH_CPU_MIPS_FAMILY) + // Reflects output format of static int gpr32_get(), defined in + // arch/mips/kernel/ptrace.c in kernel source + uint32_t padding0_[6]; + uint32_t regs[32]; + uint32_t lo; + uint32_t hi; + uint32_t cp0_epc; + uint32_t cp0_badvaddr; + uint32_t cp0_status; + uint32_t cp0_cause; + uint32_t padding1_; #else #error Port. #endif // ARCH_CPU_X86_FAMILY @@ -110,6 +122,16 @@ union ThreadContext { uint64_t sp; uint64_t pc; uint64_t pstate; +#elif defined(ARCH_CPU_MIPS_FAMILY) + // Reflects output format of static int gpr64_get(), defined in + // arch/mips/kernel/ptrace.c in kernel source + uint64_t regs[32]; + uint64_t lo; + uint64_t hi; + uint64_t cp0_epc; + uint64_t cp0_badvaddr; + uint64_t cp0_status; + uint64_t cp0_cause; #else #error Port. #endif // ARCH_CPU_X86_FAMILY @@ -119,15 +141,19 @@ union ThreadContext { using NativeThreadContext = user_regs_struct; #elif defined(ARCH_CPU_ARMEL) using NativeThreadContext = user_regs; +#elif defined(ARCH_CPU_MIPS_FAMILY) +// No appropriate NativeThreadsContext type available for MIPS #else #error Port. #endif // ARCH_CPU_X86_FAMILY || ARCH_CPU_ARM64 +#if !defined(ARCH_CPU_MIPS_FAMILY) #if defined(ARCH_CPU_32_BITS) static_assert(sizeof(t32_t) == sizeof(NativeThreadContext), "Size mismatch"); #else // ARCH_CPU_64_BITS static_assert(sizeof(t64_t) == sizeof(NativeThreadContext), "Size mismatch"); #endif // ARCH_CPU_32_BITS +#endif // !ARCH_CPU_MIPS_FAMILY }; static_assert(std::is_standard_layout::value, "Not standard layout"); @@ -183,6 +209,15 @@ union FloatContext { bool have_fpregs; bool have_vfp; +#elif defined(ARCH_CPU_MIPS_FAMILY) + // Reflects data format filled by ptrace_getfpregs() in + // arch/mips/kernel/ptrace.c + struct { + float _fp_fregs; + unsigned int _fp_pad; + } fpregs[32]; + uint32_t fpcsr; + uint32_t fpu_id; #else #error Port. #endif // ARCH_CPU_X86_FAMILY @@ -211,6 +246,12 @@ union FloatContext { uint32_t fpsr; uint32_t fpcr; uint8_t padding[8]; +#elif defined(ARCH_CPU_MIPS_FAMILY) + // Reflects data format filled by ptrace_getfpregs() in + // arch/mips/kernel/ptrace.c + double fpregs[32]; + uint32_t fpcsr; + uint32_t fpu_id; #else #error Port. #endif // ARCH_CPU_X86_FAMILY @@ -237,6 +278,8 @@ union FloatContext { #endif #elif defined(ARCH_CPU_ARM64) static_assert(sizeof(f64) == sizeof(user_fpsimd_struct), "Size mismatch"); +#elif defined(ARCH_CPU_MIPS_FAMILY) +// No appropriate floating point context native type for available MIPS. #else #error Port. #endif // ARCH_CPU_X86 diff --git a/util/misc/capture_context.h b/util/misc/capture_context.h index 73b80580..541589df 100644 --- a/util/misc/capture_context.h +++ b/util/misc/capture_context.h @@ -65,6 +65,7 @@ using NativeCPUContext = ucontext_t; //! Win | x86_64 | `%%rcx` //! macOS/Linux/Fuchsia | x86_64 | `%%rdi` //! Linux | ARM/ARM64 | `r0`/`x0` +//! Linux | MIPS/MIPS64 | `$a0` //! //! Additionally, the value `LR` on ARM/ARM64 will be the return address of //! this function. diff --git a/util/misc/capture_context_linux.S b/util/misc/capture_context_linux.S index b8d6238b..42999a90 100644 --- a/util/misc/capture_context_linux.S +++ b/util/misc/capture_context_linux.S @@ -28,7 +28,7 @@ .globl CAPTURECONTEXT_SYMBOL2 #if defined(__i386__) || defined(__x86_64__) .balign 16, 0x90 -#elif defined(__arm__) || defined(__aarch64__) +#elif defined(__arm__) || defined(__aarch64__) || defined(__mips__) .balign 4, 0x0 #endif @@ -331,5 +331,94 @@ CAPTURECONTEXT_SYMBOL2: // TODO(jperaza): save floating-point registers. ret +#elif defined(__mips__) + .set noat +#if _MIPS_SIM == _ABIO32 +#define STORE sw +#define MCONTEXT_FPREG_SIZE 4 +#define MCONTEXT_PC_OFFSET 32 +#else +#define STORE sd +#define MCONTEXT_FPREG_SIZE 8 +#define MCONTEXT_PC_OFFSET 616 +#endif + +#define MCONTEXT_REG_SIZE 8 +#define MCONTEXT_GREGS_OFFSET 40 +#define MCONTEXT_FPREGS_OFFSET 296 + + // Value of register 0 is always 0. + // Registers 26 and 27 are reserved for kernel, and shouldn't be used. + STORE $1, (1 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $2, (2 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $3, (3 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $4, (4 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $5, (5 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $6, (6 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $7, (7 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $8, (8 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $9, (9 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $10, (10 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $11, (11 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $12, (12 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $13, (13 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $14, (14 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $15, (15 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $16, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $17, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $18, (18 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $19, (19 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $20, (20 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $21, (21 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $22, (22 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $23, (23 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $24, (24 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $25, (25 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $28, (28 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $29, (29 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $30, (30 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $31, (31 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0) + STORE $31, (MCONTEXT_PC_OFFSET)($a0) + +#ifdef __mips_hard_float + s.d $f0, (0 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f2, (2 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f4, (4 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f6, (6 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f8, (8 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f10, (10 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f12, (12 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f14, (14 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f16, (16 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f18, (18 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f20, (20 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f22, (22 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f24, (24 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f26, (26 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f28, (28 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f30, (30 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) +#if _MIPS_SIM != _ABIO32 + s.d $f1, (1 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f3, (3 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f5, (5 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f7, (7 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f9, (9 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f11, (11 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f13, (13 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f15, (15 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f17, (17 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f19, (19 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f21, (21 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f23, (23 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f25, (25 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f27, (27 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f29, (29 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) + s.d $f31, (31 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0) +#endif // _MIPS_SIM != _ABIO32 +#endif // __mips_hard_float + + jr $ra + + .set at #endif // __i386__ diff --git a/util/misc/capture_context_test.cc b/util/misc/capture_context_test.cc index deeea9e3..1117789d 100644 --- a/util/misc/capture_context_test.cc +++ b/util/misc/capture_context_test.cc @@ -49,7 +49,7 @@ void TestCaptureContext() { // reference program counter. uintptr_t pc = ProgramCounterFromContext(context_1); -#if !defined(ADDRESS_SANITIZER) +#if !defined(ADDRESS_SANITIZER) && !defined(ARCH_CPU_MIPS_FAMILY) // AddressSanitizer can cause enough code bloat that the “nearby” check would // likely fail. const uintptr_t kReferencePC = diff --git a/util/misc/capture_context_test_util_linux.cc b/util/misc/capture_context_test_util_linux.cc index 3ba13d85..9fc5db28 100644 --- a/util/misc/capture_context_test_util_linux.cc +++ b/util/misc/capture_context_test_util_linux.cc @@ -34,6 +34,8 @@ void SanityCheckContext(const NativeCPUContext& context) { EXPECT_EQ(context.uc_mcontext.arm_r0, FromPointerCast(&context)); #elif defined(ARCH_CPU_ARM64) EXPECT_EQ(context.uc_mcontext.regs[0], FromPointerCast(&context)); +#elif defined(ARCH_CPU_MIPS_FAMILY) + EXPECT_EQ(context.uc_mcontext.gregs[4], FromPointerCast(&context)); #endif } @@ -46,6 +48,8 @@ uintptr_t ProgramCounterFromContext(const NativeCPUContext& context) { return context.uc_mcontext.arm_pc; #elif defined(ARCH_CPU_ARM64) return context.uc_mcontext.pc; +#elif defined(ARCH_CPU_MIPS_FAMILY) + return context.uc_mcontext.pc; #endif } @@ -58,6 +62,8 @@ uintptr_t StackPointerFromContext(const NativeCPUContext& context) { return context.uc_mcontext.arm_sp; #elif defined(ARCH_CPU_ARM64) return context.uc_mcontext.sp; +#elif defined(ARCH_CPU_MIPS_FAMILY) + return context.uc_mcontext.gregs[29]; #endif } diff --git a/util/posix/symbolic_constants_posix.cc b/util/posix/symbolic_constants_posix.cc index c973c146..8008ffb6 100644 --- a/util/posix/symbolic_constants_posix.cc +++ b/util/posix/symbolic_constants_posix.cc @@ -65,6 +65,39 @@ constexpr const char* kSignalNames[] = { "USR1", "USR2", #elif defined(OS_LINUX) || defined(OS_ANDROID) +#if defined(ARCH_CPU_MIPS_FAMILY) + "HUP", + "INT", + "QUIT", + "ILL", + "TRAP", + "ABRT", + "EMT", + "FPE", + "KILL", + "BUS", + "SEGV", + "SYS", + "PIPE", + "ALRM", + "TERM", + "USR1", + "USR2", + "CHLD", + "PWR", + "WINCH", + "URG", + "IO", + "STOP", + "TSTP", + "CONT", + "TTIN", + "TTOU", + "VTALRM", + "PROF", + "XCPU", + "XFSZ", +#else // sed -Ene 's/^#define[[:space:]]SIG([[:alnum:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/ "\1",/p' // /usr/include/asm-generic/signal.h // and fix up by removing SIGIOT, SIGLOST, SIGUNUSED, and SIGRTMIN. @@ -99,6 +132,7 @@ constexpr const char* kSignalNames[] = { "IO", "PWR", "SYS", +#endif // defined(ARCH_CPU_MIPS_FAMILY) #endif }; #if defined(OS_LINUX) || defined(OS_ANDROID) diff --git a/util/posix/symbolic_constants_posix_test.cc b/util/posix/symbolic_constants_posix_test.cc index 8478b7bc..32c1d434 100644 --- a/util/posix/symbolic_constants_posix_test.cc +++ b/util/posix/symbolic_constants_posix_test.cc @@ -67,8 +67,10 @@ constexpr struct { {SIGINFO, "SIGINFO", "INFO"}, #elif defined(OS_LINUX) || defined(OS_ANDROID) {SIGPWR, "SIGPWR", "PWR"}, +#if !defined(ARCH_CPU_MIPS_FAMILY) {SIGSTKFLT, "SIGSTKFLT", "STKFLT"}, #endif +#endif }; // If |expect| is nullptr, the conversion is expected to fail. If |expect| is From 1e504474a041bb80ee8f3331e658e6f976dfac55 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 10 Jul 2018 09:29:51 -0700 Subject: [PATCH 321/326] linux: Get exe name from main arguments When building in chromium, the test is linked into the crashpad_tests target instead of crashpad_util_test. Change-Id: I4e0f6b9956f191ebac10f0aaa3812e30885a4e0a Reviewed-on: https://chromium-review.googlesource.com/1131688 Commit-Queue: Joshua Peraza Reviewed-by: Robert Sesek --- util/linux/auxiliary_vector_test.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/linux/auxiliary_vector_test.cc b/util/linux/auxiliary_vector_test.cc index add2d1e2..d4cfcb74 100644 --- a/util/linux/auxiliary_vector_test.cc +++ b/util/linux/auxiliary_vector_test.cc @@ -26,6 +26,7 @@ #include "gtest/gtest.h" #include "test/errors.h" #include "test/linux/fake_ptrace_connection.h" +#include "test/main_arguments.h" #include "test/multiprocess.h" #include "util/linux/address_types.h" #include "util/linux/memory_map.h" @@ -128,7 +129,7 @@ void TestAgainstCloneOrSelf(pid_t pid) { ASSERT_TRUE(aux.GetValue(AT_EXECFN, &filename_addr)); std::string filename; ASSERT_TRUE(memory.ReadCStringSizeLimited(filename_addr, 4096, &filename)); - EXPECT_TRUE(filename.find("crashpad_util_test") != std::string::npos); + EXPECT_TRUE(filename.find(GetMainArguments()[0]) != std::string::npos); #endif // AT_EXECFN int ignore; From 0c8f035a500ae0bdaf6ba4536cfe0bdbf3dedc04 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Tue, 10 Jul 2018 14:11:05 -0700 Subject: [PATCH 322/326] linux, non-glibc: fix build after eb7d8a4c The pre-processor attempts to parse an entire expression before evaluating sub-expressions, so undefined macros result in a syntax error. Change-Id: Ie950867897a1befd221bdbe4719f2365f5cc75ee Reviewed-on: https://chromium-review.googlesource.com/1132328 Reviewed-by: Robert Sesek Commit-Queue: Joshua Peraza --- compat/linux/sys/user.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compat/linux/sys/user.h b/compat/linux/sys/user.h index 197c1dd1..0ce5338b 100644 --- a/compat/linux/sys/user.h +++ b/compat/linux/sys/user.h @@ -20,9 +20,11 @@ #include // glibc for 64-bit ARM uses different names for these structs prior to 2.20. -#if defined(__arm64__) && defined(__GLIBC__) && !__GLIBC_PREREQ(2, 20) +#if defined(__arm64__) && defined(__GLIBC__) +#if !__GLIBC_PREREQ(2, 20) using user_regs_struct = user_pt_regs; using user_fpsimd_struct = user_fpsimd_state; #endif +#endif #endif // CRASHPAD_COMPAT_LINUX_SYS_USER_H_ From fb0f7ca8d7eb55ca51b77eb89813a3283a461b0c Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Wed, 11 Jul 2018 08:59:41 -0700 Subject: [PATCH 323/326] Uninstall handlers for expected crash signals in child processes Chromium's test launcher installs crash signal handlers which call exit(1), instead of with the signal value. Change-Id: I0c1a62100ef59939a6bcfbf0733e746609a1ead8 Reviewed-on: https://chromium-review.googlesource.com/1131819 Reviewed-by: Robert Sesek Commit-Queue: Joshua Peraza --- test/multiprocess.h | 3 ++- test/multiprocess_posix.cc | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/test/multiprocess.h b/test/multiprocess.h index aac9288b..d0275020 100644 --- a/test/multiprocess.h +++ b/test/multiprocess.h @@ -89,7 +89,8 @@ class Multiprocess { //! \param[in] code If \a reason is TerminationReason::kTerminationNormal, //! this is the expected exit status of the child. If \a reason is //! TerminationReason::kTerminationSignal, this is the signal that is - //! expected to kill the child. + //! expected to kill the child. On Linux platforms, SIG_DFL will be + //! installed for \a code in the child process. void SetExpectedChildTermination(TerminationReason reason, int code); #if !defined(OS_WIN) diff --git a/test/multiprocess_posix.cc b/test/multiprocess_posix.cc index cdb5385e..2e0c3856 100644 --- a/test/multiprocess_posix.cc +++ b/test/multiprocess_posix.cc @@ -143,6 +143,10 @@ void Multiprocess::Run() { if (exception_swallower.get()) { ExceptionSwallower::SwallowExceptions(); } +#elif defined(OS_LINUX) || defined(OS_ANDROID) + if (reason_ == kTerminationSignal && Signals::IsCrashSignal(code_)) { + Signals::InstallDefaultHandler(code_); + } #endif // OS_MACOSX RunChild(); From 03abd1bb3497788c310c215a294dd34a63495668 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Wed, 18 Jul 2018 11:16:21 -0400 Subject: [PATCH 324/326] mac: Tolerate the new flavor of weird cl_kernels modules on 10.14 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenCL modules that appeared as “cl_kernels” since 10.7 now show up in 10.14 as ad-hoc signed modules at /private/var/db/CVMS/cvmsCodeSignObjXXXXXXXXXXXXXXXX (16 random characters). The modules are unlinked from the filesystem once loaded. Bug: crashpad:243 Change-Id: I00fdd1311d4e6cd4c9224ef54ac990ac1afb849c Reviewed-on: https://chromium-review.googlesource.com/1142027 Reviewed-by: Robert Sesek Commit-Queue: Mark Mentovai --- snapshot/mac/mach_o_image_segment_reader.cc | 48 +++++++++++++++------ snapshot/mac/mach_o_image_segment_reader.h | 35 +++++++++++++++ snapshot/mac/process_reader_mac_test.cc | 29 +++++++++---- 3 files changed, 92 insertions(+), 20 deletions(-) diff --git a/snapshot/mac/mach_o_image_segment_reader.cc b/snapshot/mac/mach_o_image_segment_reader.cc index 1be829dc..478b37d7 100644 --- a/snapshot/mac/mach_o_image_segment_reader.cc +++ b/snapshot/mac/mach_o_image_segment_reader.cc @@ -15,6 +15,7 @@ #include "snapshot/mac/mach_o_image_segment_reader.h" #include +#include #include @@ -35,6 +36,37 @@ std::string SizeLimitedCString(const char* c_string, size_t max_length) { } // namespace +bool IsMalformedCLKernelsModule(uint32_t mach_o_file_type, + const std::string& module_name, + bool* has_timestamp) { + if (mach_o_file_type != MH_BUNDLE) { + return false; + } + + if (module_name == "cl_kernels") { + if (MacOSXMinorVersion() >= 10) { + if (has_timestamp) { + *has_timestamp = false; + } + return true; + } + return false; + } + + static const char kCvmsObjectPathPrefix[] = + "/private/var/db/CVMS/cvmsCodeSignObj"; + if (module_name.compare( + 0, strlen(kCvmsObjectPathPrefix), kCvmsObjectPathPrefix) == 0 && + MacOSXMinorVersion() >= 14) { + if (has_timestamp) { + *has_timestamp = true; + } + return true; + } + + return false; +} + MachOImageSegmentReader::MachOImageSegmentReader() : segment_command_(), sections_(), @@ -121,21 +153,13 @@ bool MachOImageSegmentReader::Initialize(ProcessReaderMac* process_reader, load_command_info.c_str()); // cl_kernels modules (for OpenCL) aren’t ld output, and they’re formatted - // incorrectly on OS X 10.10 and later. They have a single __TEXT segment, - // but one of the sections within it claims to belong to the __LD segment. - // This mismatch shouldn’t happen. This errant section also has the - // S_ATTR_DEBUG flag set, which shouldn’t happen unless all of the other - // sections in the segment also have this bit set (they don’t). These odd - // sections are reminiscent of unwind information stored in MH_OBJECT - // images, although cl_kernels images claim to be MH_BUNDLE. Because at - // least one cl_kernels module will commonly be found in a process, and - // sometimes more will be, tolerate this quirk. + // incorrectly on OS X 10.10 and later. Because at least one cl_kernels + // module will commonly be found in a process, and sometimes more will be, + // tolerate this quirk. // // https://openradar.appspot.com/20239912 if (section_segment_name != segment_name && - !(file_type == MH_BUNDLE && - module_name == "cl_kernels" && - MacOSXMinorVersion() >= 10 && + !(IsMalformedCLKernelsModule(file_type, module_name, nullptr) && segment_name == SEG_TEXT && section_segment_name == "__LD" && section_name == "__compact_unwind" && diff --git a/snapshot/mac/mach_o_image_segment_reader.h b/snapshot/mac/mach_o_image_segment_reader.h index bdd57716..1e72785d 100644 --- a/snapshot/mac/mach_o_image_segment_reader.h +++ b/snapshot/mac/mach_o_image_segment_reader.h @@ -29,6 +29,41 @@ namespace crashpad { +//! \brief Determines whether a module appears to be a malformed OpenCL +//! `cl_kernels` module based on its name and Mach-O file type. +//! +//! `cl_kernels` modules require special handling because they’re malformed on +//! OS X 10.10 and later. A `cl_kernels` module always has Mach-O type +//! `MH_BUNDLE` and is named `"cl_kernels"` until macOS 10.14, and +//! `"/private/var/db/CVMS/cvmsCodeSignObj"` plus 16 random characters on macOS +//! 10.14. +//! +//! Malformed `cl_kernels` modules have a single `__TEXT` segment, but one of +//! the sections within it claims to belong to the `__LD` segment. This mismatch +//! shouldn’t happen. This errant section also has the `S_ATTR_DEBUG` flag set, +//! which shouldn’t happen unless all of the other sections in the segment also +//! have this bit set (they don’t). These odd sections are reminiscent of unwind +//! information stored in `MH_OBJECT` images, although `cl_kernels` images claim +//! to be `MH_BUNDLE`. +//! +//! This function is exposed for testing purposes only. +//! +//! \param[in] mach_o_file_type The Mach-O type of the module being examined. +//! \param[in] module_name The pathname that `dyld` reported having loaded the +//! module from. +//! \param[out] has_timestamp Optional, may be `nullptr`. If provided, and the +//! module is a maformed `cl_kernels` module, this will be set to `true` if +//! the module was loaded from the filesystem (as is the case when loaded +//! from the CVMS directory) and is expected to have a timestamp, and +//! `false` otherwise. Note that even when loaded from the filesystem, these +//! modules are unlinked from the filesystem after loading. +//! +//! \return `true` if the module appears to be a malformed `cl_kernels` module +//! based on the provided information, `false` otherwise. +bool IsMalformedCLKernelsModule(uint32_t mach_o_file_type, + const std::string& module_name, + bool* has_timestamp); + //! \brief A reader for `LC_SEGMENT` or `LC_SEGMENT_64` load commands in Mach-O //! images mapped into another process. //! diff --git a/snapshot/mac/process_reader_mac_test.cc b/snapshot/mac/process_reader_mac_test.cc index faec1472..59884a6d 100644 --- a/snapshot/mac/process_reader_mac_test.cc +++ b/snapshot/mac/process_reader_mac_test.cc @@ -32,6 +32,7 @@ #include "build/build_config.h" #include "gtest/gtest.h" #include "snapshot/mac/mach_o_image_reader.h" +#include "snapshot/mac/mach_o_image_segment_reader.h" #include "test/errors.h" #include "test/mac/dyld.h" #include "test/mac/mach_errors.h" @@ -663,14 +664,20 @@ TEST(ProcessReaderMac, SelfModules) { modules[index].reader->Address(), FromPointerCast(_dyld_get_image_header(index))); + bool expect_timestamp; if (index == 0) { // dyld didn’t load the main executable, so it couldn’t record its // timestamp, and it is reported as 0. EXPECT_EQ(modules[index].timestamp, 0); - } else if (modules[index].reader->FileType() == MH_BUNDLE && - modules[index].name == "cl_kernels") { - // cl_kernels doesn’t exist as a file. - EXPECT_EQ(modules[index].timestamp, 0); + } else if (IsMalformedCLKernelsModule(modules[index].reader->FileType(), + modules[index].name, + &expect_timestamp)) { + // cl_kernels doesn’t exist as a file, but may still have a timestamp. + if (!expect_timestamp) { + EXPECT_EQ(modules[index].timestamp, 0); + } else { + EXPECT_NE(modules[index].timestamp, 0); + } found_cl_kernels = true; } else { // Hope that the module didn’t change on disk. @@ -747,14 +754,20 @@ class ProcessReaderModulesChild final : public MachMultiprocess { ASSERT_TRUE(modules[index].reader); EXPECT_EQ(modules[index].reader->Address(), expect_address); + bool expect_timestamp; if (index == 0 || index == modules.size() - 1) { // dyld didn’t load the main executable or itself, so it couldn’t record // these timestamps, and they are reported as 0. EXPECT_EQ(modules[index].timestamp, 0); - } else if (modules[index].reader->FileType() == MH_BUNDLE && - modules[index].name == "cl_kernels") { - // cl_kernels doesn’t exist as a file. - EXPECT_EQ(modules[index].timestamp, 0); + } else if (IsMalformedCLKernelsModule(modules[index].reader->FileType(), + modules[index].name, + &expect_timestamp)) { + // cl_kernels doesn’t exist as a file, but may still have a timestamp. + if (!expect_timestamp) { + EXPECT_EQ(modules[index].timestamp, 0); + } else { + EXPECT_NE(modules[index].timestamp, 0); + } found_cl_kernels = true; } else { // Hope that the module didn’t change on disk. From 98ebb0060b3e05c1c1aa8e5700ec7aebb941dccf Mon Sep 17 00:00:00 2001 From: Ryan Tseng Date: Fri, 20 Jul 2018 11:33:09 -0700 Subject: [PATCH 325/326] Add whitespace file Bug: 865729 TBR: dpranke Change-Id: I906dd83cd4f96636f65efade8a9bd34b6e9bbb42 Reviewed-on: https://chromium-review.googlesource.com/1145587 Reviewed-by: Ryan Tseng Commit-Queue: Ryan Tseng --- build/whitespace.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 build/whitespace.txt diff --git a/build/whitespace.txt b/build/whitespace.txt new file mode 100644 index 00000000..cc88ce55 --- /dev/null +++ b/build/whitespace.txt @@ -0,0 +1 @@ +This is a whitespace file used to kick off jobs. From e50ea6032180384d45552e84347ff4eb2e84ce57 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Fri, 20 Jul 2018 15:56:49 -0400 Subject: [PATCH 326/326] Make BuildHandlerArgvStrings() return its result This is more direct than using an out-parameter. Copy elision should make it equally performant, and even in the absence of copy elision, this would now be an inexpensive move operation. Change-Id: Iaf0eb07b36c8e35ff8942fc422a22321bf5c3010 Reviewed-on: https://chromium-review.googlesource.com/1145495 Reviewed-by: Joshua Peraza Commit-Queue: Mark Mentovai --- build/whitespace.txt | 1 - client/client_argv_handling.cc | 20 ++++++++++---------- client/client_argv_handling.h | 8 +++----- client/crashpad_client_fuchsia.cc | 10 ++-------- client/crashpad_client_linux.cc | 10 ++++------ 5 files changed, 19 insertions(+), 30 deletions(-) delete mode 100644 build/whitespace.txt diff --git a/build/whitespace.txt b/build/whitespace.txt deleted file mode 100644 index cc88ce55..00000000 --- a/build/whitespace.txt +++ /dev/null @@ -1 +0,0 @@ -This is a whitespace file used to kick off jobs. diff --git a/client/client_argv_handling.cc b/client/client_argv_handling.cc index 9b813b1c..6933aac6 100644 --- a/client/client_argv_handling.cc +++ b/client/client_argv_handling.cc @@ -27,38 +27,38 @@ std::string FormatArgumentString(const std::string& name, } // namespace -void BuildHandlerArgvStrings( +std::vector BuildHandlerArgvStrings( const base::FilePath& handler, const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, const std::map& annotations, - const std::vector& arguments, - std::vector* argv_strings) { - argv_strings->clear(); + const std::vector& arguments) { + std::vector argv_strings(1, handler.value()); - argv_strings->push_back(handler.value()); for (const auto& argument : arguments) { - argv_strings->push_back(argument); + argv_strings.push_back(argument); } if (!database.empty()) { - argv_strings->push_back(FormatArgumentString("database", database.value())); + argv_strings.push_back(FormatArgumentString("database", database.value())); } if (!metrics_dir.empty()) { - argv_strings->push_back( + argv_strings.push_back( FormatArgumentString("metrics-dir", metrics_dir.value())); } if (!url.empty()) { - argv_strings->push_back(FormatArgumentString("url", url)); + argv_strings.push_back(FormatArgumentString("url", url)); } for (const auto& kv : annotations) { - argv_strings->push_back( + argv_strings.push_back( FormatArgumentString("annotation", kv.first + '=' + kv.second)); } + + return argv_strings; } void ConvertArgvStrings(const std::vector& argv_strings, diff --git a/client/client_argv_handling.h b/client/client_argv_handling.h index 0ef3a154..d380b1a0 100644 --- a/client/client_argv_handling.h +++ b/client/client_argv_handling.h @@ -28,16 +28,14 @@ namespace crashpad { //! //! See StartHandlerAtCrash() for documentation on the input arguments. //! -//! \param[out] A argv_strings vector of arguments suitable for starting the -//! handler with. -void BuildHandlerArgvStrings( +//! \return A vector of arguments suitable for starting the handler with. +std::vector BuildHandlerArgvStrings( const base::FilePath& handler, const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, const std::map& annotations, - const std::vector& arguments, - std::vector* argv_strings); + const std::vector& arguments); //! \brief Flattens a string vector into a const char* vector suitable for use //! in an exec() call. diff --git a/client/crashpad_client_fuchsia.cc b/client/crashpad_client_fuchsia.cc index 26781094..0d1b65bf 100644 --- a/client/crashpad_client_fuchsia.cc +++ b/client/crashpad_client_fuchsia.cc @@ -58,14 +58,8 @@ bool CrashpadClient::StartHandler( return false; } - std::vector argv_strings; - BuildHandlerArgvStrings(handler, - database, - metrics_dir, - url, - annotations, - arguments, - &argv_strings); + std::vector argv_strings = BuildHandlerArgvStrings( + handler, database, metrics_dir, url, annotations, arguments); std::vector argv; ConvertArgvStrings(argv_strings, &argv); diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc index e7fa162c..54fe268a 100644 --- a/client/crashpad_client_linux.cc +++ b/client/crashpad_client_linux.cc @@ -175,9 +175,8 @@ bool CrashpadClient::StartHandlerAtCrash( const std::string& url, const std::map& annotations, const std::vector& arguments) { - std::vector argv; - BuildHandlerArgvStrings( - handler, database, metrics_dir, url, annotations, arguments, &argv); + std::vector argv = BuildHandlerArgvStrings( + handler, database, metrics_dir, url, annotations, arguments); auto signal_handler = LaunchAtCrashHandler::Get(); if (signal_handler->Initialize(&argv)) { @@ -197,9 +196,8 @@ bool CrashpadClient::StartHandlerForClient( const std::map& annotations, const std::vector& arguments, int socket) { - std::vector argv; - BuildHandlerArgvStrings( - handler, database, metrics_dir, url, annotations, arguments, &argv); + std::vector argv = BuildHandlerArgvStrings( + handler, database, metrics_dir, url, annotations, arguments); argv.push_back(FormatArgumentInt("initial-client", socket));