diff --git a/build/secondary/third_party/crashpad/crashpad/client/BUILD.gn b/build/secondary/third_party/crashpad/crashpad/client/BUILD.gn index 0d9a0c3f4bc430..d1fe1395a06aea 100644 --- a/build/secondary/third_party/crashpad/crashpad/client/BUILD.gn +++ b/build/secondary/third_party/crashpad/crashpad/client/BUILD.gn @@ -8,6 +8,10 @@ config("client_config") { 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", diff --git a/build/secondary/third_party/crashpad/crashpad/compat/BUILD.gn b/build/secondary/third_party/crashpad/crashpad/compat/BUILD.gn index 276dd58a95edd6..cbffb36f266be2 100644 --- a/build/secondary/third_party/crashpad/crashpad/compat/BUILD.gn +++ b/build/secondary/third_party/crashpad/crashpad/compat/BUILD.gn @@ -40,6 +40,7 @@ static_library("compat") { "win/sys/types.h", "win/time.cc", "win/time.h", + "win/winbase.h", "win/winnt.h", "win/winternl.h", ] diff --git a/build/secondary/third_party/crashpad/crashpad/test/BUILD.gn b/build/secondary/third_party/crashpad/crashpad/test/BUILD.gn index c9375a102e3d21..a194928d93825e 100644 --- a/build/secondary/third_party/crashpad/crashpad/test/BUILD.gn +++ b/build/secondary/third_party/crashpad/crashpad/test/BUILD.gn @@ -10,6 +10,8 @@ static_library("crashpad_test") { "file.cc", "file.h", "gtest_death_check.h", + "gtest_disabled.cc", + "gtest_disabled.h", "hex_string.cc", "hex_string.h", "mac/dyld.cc", @@ -58,30 +60,38 @@ static_library("crashpad_test") { } } -static_library("crashpad_gtest_main") { +static_library("crashpad_gmock_main") { testonly = true - defines = [ "CRASHPAD_IN_CHROMIUM" ] + defines = [ + "CRASHPAD_IN_CHROMIUM", + "CRASHPAD_TEST_LAUNCHER_GMOCK", + ] sources = [ "gtest_main.cc", ] deps = [ ":crashpad_test", + "//base", "//base/test:test_support", + "//testing/gmock", "//testing/gtest", ] include_dirs = [ ".." ] } -static_library("crashpad_gmock_main") { +static_library("crashpad_gtest_main") { testonly = true - defines = [ "CRASHPAD_IN_CHROMIUM" ] + defines = [ + "CRASHPAD_IN_CHROMIUM", + "CRASHPAD_TEST_LAUNCHER_GTEST", + ] sources = [ - "gmock_main.cc", + "gtest_main.cc", ] deps = [ ":crashpad_test", + "//base", "//base/test:test_support", - "//testing/gmock", "//testing/gtest", ] include_dirs = [ ".." ] diff --git a/build/secondary/third_party/crashpad/crashpad/util/BUILD.gn b/build/secondary/third_party/crashpad/crashpad/util/BUILD.gn index e83602b002c0f9..65367b931d92bf 100644 --- a/build/secondary/third_party/crashpad/crashpad/util/BUILD.gn +++ b/build/secondary/third_party/crashpad/crashpad/util/BUILD.gn @@ -322,8 +322,12 @@ static_library("util") { test("crashpad_util_test") { 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/filesystem_test_util.cc", + "file/filesystem_test_util.h", "file/string_file_test.cc", "mac/launchd_test.mm", "mac/mac_util_test.mm", @@ -419,19 +423,9 @@ test("crashpad_util_test") { ":crashpad_util_test_safe_terminate_process_test_child", ] libs = [ "rpcrt4.lib" ] - } else { - # These tests use symbolic links, which require admin priviledges on - # Windows before Windows 10. - sources += [ - "file/directory_reader_test.cc", - "file/filesystem_test.cc", - "file/filesystem_test_util.cc", - "file/filesystem_test_util.h", - ] } include_dirs = [ ".." ] - defines = [ "CRASHPAD_IN_CHROMIUM" ] data = [ "net/http_transport_test_server.py", diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium index f1bb0e68af855b..6eede7f3cf0e8c 100644 --- a/third_party/crashpad/README.chromium +++ b/third_party/crashpad/README.chromium @@ -2,7 +2,7 @@ Name: Crashpad Short Name: crashpad URL: https://crashpad.chromium.org/ Version: unknown -Revision: 6d5bd1d04d5f50d9f4a58d528ccfb38c46423f23 +Revision: 3fae8ff07c097da6d0042510bdbe5b16c67a8e12 License: Apache 2.0 License File: crashpad/LICENSE Security Critical: yes diff --git a/third_party/crashpad/crashpad/build/run_tests.py b/third_party/crashpad/crashpad/build/run_tests.py index c5cbb03be737f2..3eae77a5e1f48b 100755 --- a/third_party/crashpad/crashpad/build/run_tests.py +++ b/third_party/crashpad/crashpad/build/run_tests.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# coding: utf-8 # Copyright 2014 The Crashpad Authors. All rights reserved. # @@ -31,6 +32,18 @@ def main(args): 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 + # cross-bitted testing. This relies on the fact that the GYP build by default + # uses {Debug,Release} for the 32-bit build and {Debug,Release}_x64 for the + # 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 + 'CRASHPAD_TEST_32_BIT_OUTPUT' not in os.environ): + binary_dir_32 = binary_dir[:-4] + if os.path.isdir(binary_dir_32): + os.environ['CRASHPAD_TEST_32_BIT_OUTPUT'] = binary_dir_32 + tests = [ 'crashpad_client_test', 'crashpad_minidump_test', diff --git a/third_party/crashpad/crashpad/client/annotation.cc b/third_party/crashpad/crashpad/client/annotation.cc new file mode 100644 index 00000000000000..22c7395609b2dc --- /dev/null +++ b/third_party/crashpad/crashpad/client/annotation.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 "client/annotation.h" + +#include + +#include "client/annotation_list.h" + +namespace crashpad { + +static_assert(std::is_standard_layout::value, + "Annotation must be POD"); + +// static +constexpr size_t Annotation::kNameMaxLength; +constexpr size_t Annotation::kValueMaxSize; + +void Annotation::SetSize(ValueSizeType size) { + DCHECK_LT(size, kValueMaxSize); + size_ = size; + AnnotationList::Get()->Add(this); +} + +void Annotation::Clear() { + size_ = 0; +} + +} // namespace crashpad diff --git a/third_party/crashpad/crashpad/client/annotation.h b/third_party/crashpad/crashpad/client/annotation.h new file mode 100644 index 00000000000000..141aa123b19752 --- /dev/null +++ b/third_party/crashpad/crashpad/client/annotation.h @@ -0,0 +1,223 @@ +// 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_CLIENT_ANNOTATION_H_ +#define CRASHPAD_CLIENT_ANNOTATION_H_ + +#include +#include + +#include +#include +#include + +#include "base/logging.h" +#include "base/macros.h" +#include "base/numerics/safe_conversions.h" +#include "build/build_config.h" + +namespace crashpad { + +class AnnotationList; + +//! \brief Base class for an annotation, which records a name-value pair of +//! arbitrary data when set. +//! +//! After an annotation is declared, its `value_ptr_` will not be captured in a +//! crash report until a call to \a SetSize() specifies how much data from the +//! value should be recorded. +//! +//! Annotations should be declared with static storage duration. +//! +//! An example declaration and usage: +//! +//! \code +//! // foo.cc: +//! +//! namespace { +//! char g_buffer[1024]; +//! crashpad::Annotation g_buffer_annotation( +//! crashpad::Annotation::Type::kString, "buffer_head", g_buffer); +//! } // namespace +//! +//! void OnBufferProduced(size_t n) { +//! // Capture the head of the buffer, in case we crash when parsing it. +//! g_buffer_annotation.SetSize(std::min(64, n)); +//! +//! // Start parsing the header. +//! Frobinate(g_buffer, n); +//! } +//! \endcode +//! +//! Annotation objects are not inherently thread-safe. To manipulate them +//! from multiple threads, external synchronization must be used. +//! +//! Annotation objects should never be destroyed. Once they are Set(), they +//! are permanently referenced by a global object. +class Annotation { + public: + //! \brief The maximum length of the #name field in bytes. + static constexpr size_t kNameMaxLength = 64; + + //! \brief The maximum size of the #value field in bytes. + static constexpr size_t kValueMaxSize = 2048; + + //! \brief The type used for \a SetSize(). + using ValueSizeType = uint32_t; + + //! \brief The type of data stored in the annotation. + enum class Type : uint16_t { + //! \brief An invalid annotation. Reserved for internal use. + kInvalid = 0, + + //! \brief A `NUL`-terminated C-string. + kString = 1, + + //! \brief Clients may declare their own custom types by using values + //! greater than this. + kUserDefinedStart = 0x8000, + }; + + //! \brief Creates a user-defined Annotation::Type. + //! + //! This exists to remove the casting overhead of `enum class`. + //! + //! \param[in] value A value used to create a user-defined type. + //! + //! \returns The value added to Type::kUserDefinedStart and casted. + constexpr static Type UserDefinedType(uint16_t value) { + using UnderlyingType = std::underlying_type::type; + // MSVS 2015 doesn't have full C++14 support and complains about local + // variables defined in a constexpr function, which is valid. Avoid them + // and the also-problematic DCHECK until all the infrastructure is updated: + // https://crbug.com/crashpad/201. +#if !defined(OS_WIN) || (defined(_MSC_VER) && _MSC_VER >= 1910) + const UnderlyingType start = + static_cast(Type::kUserDefinedStart); + const UnderlyingType user_type = start + value; + DCHECK(user_type > start) << "User-defined Type is 0 or overflows"; + return static_cast(user_type); +#else + return static_cast( + static_cast(Type::kUserDefinedStart) + value); +#endif + } + + //! \brief Constructs a new annotation. + //! + //! Upon construction, the annotation will not be included in any crash + //! reports until \sa SetSize() is called with a value greater than `0`. + //! + //! \param[in] type The data type of the value of the annotation. + //! \param[in] name A `NUL`-terminated C-string name for the annotation. Names + //! do not have to be unique, though not all crash processors may handle + //! Annotations with the same name. Names should be constexpr data with + //! static storage duration. + //! \param[in] value_ptr A pointer to the value for the annotation. The + //! pointer may not be changed once associated with an annotation, but + //! the data may be mutated. + constexpr Annotation(Type type, const char name[], void* const value_ptr) + : link_node_(nullptr), + name_(name), + value_ptr_(value_ptr), + size_(0), + type_(type) {} + + //! \brief Specifies the number of bytes in \a value_ptr_ to include when + //! generating a crash report. + //! + //! A size of `0` indicates that no value should be recorded and is the + //! equivalent of calling \sa Clear(). + //! + //! This method does not mutate the data referenced by the annotation, it + //! merely updates the annotation system's bookkeeping. + //! + //! Subclasses of this base class that provide additional Set methods to + //! mutate the value of the annotation must call always call this method. + //! + //! \param[in] size The number of bytes. + void SetSize(ValueSizeType size); + + //! \brief Marks the annotation as cleared, indicating the \a value_ptr_ + //! should not be included in a crash report. + //! + //! This method does not mutate the data referenced by the annotation, it + //! merely updates the annotation system's bookkeeping. + void Clear(); + + //! \brief Tests whether the annotation has been set. + bool is_set() const { return size_ > 0; } + + Type type() const { return type_; } + ValueSizeType size() const { return size_; } + const char* name() const { return name_; } + const void* value() const { return value_ptr_; } + + protected: + friend class AnnotationList; + + std::atomic& link_node() { return link_node_; } + + private: + //! \brief Linked list next-node pointer. Accessed only by \sa AnnotationList. + //! + //! This will be null until the first call to \sa SetSize(), after which the + //! presence of the pointer will prevent the node from being added to the + //! list again. + std::atomic link_node_; + + const char* const name_; + void* const value_ptr_; + ValueSizeType size_; + const Type type_; + + DISALLOW_COPY_AND_ASSIGN(Annotation); +}; + +//! \brief An \sa Annotation that stores a `NUL`-terminated C-string value. +//! +//! The storage for the value is allocated by the annotation and the template +//! parameter \a MaxSize controls the maxmium length for the value. +//! +//! It is expected that the string value be valid UTF-8, although this is not +//! validated. +template +class StringAnnotation : public Annotation { + public: + //! \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 Sets the Annotation's string value. + //! + //! \param[in] value The `NUL`-terminated C-string value. + void Set(const char* value) { + strncpy(value_, value, MaxSize); + SetSize( + std::min(MaxSize, base::saturated_cast(strlen(value)))); + } + + private: + // This value is not `NUL`-terminated, since the size is stored by the base + // annotation. + char value_[MaxSize]; + + DISALLOW_COPY_AND_ASSIGN(StringAnnotation); +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_ANNOTATION_H_ diff --git a/third_party/crashpad/crashpad/client/annotation_list.cc b/third_party/crashpad/crashpad/client/annotation_list.cc new file mode 100644 index 00000000000000..10f5ea49fcd42b --- /dev/null +++ b/third_party/crashpad/crashpad/client/annotation_list.cc @@ -0,0 +1,97 @@ +// 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/annotation_list.h" + +#include "base/logging.h" +#include "client/crashpad_info.h" + +namespace crashpad { + +AnnotationList::AnnotationList() + : tail_pointer_(&tail_), + head_(Annotation::Type::kInvalid, nullptr, nullptr), + tail_(Annotation::Type::kInvalid, nullptr, nullptr) { + head_.link_node().store(&tail_); +} + +AnnotationList::~AnnotationList() {} + +// static +AnnotationList* AnnotationList::Get() { + return CrashpadInfo::GetCrashpadInfo()->annotations_list(); +} + +// static +AnnotationList* AnnotationList::Register() { + AnnotationList* list = Get(); + if (!list) { + list = new AnnotationList(); + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(list); + } + return list; +} + +void AnnotationList::Add(Annotation* annotation) { + Annotation* null = nullptr; + Annotation* head_next = head_.link_node().load(std::memory_order_relaxed); + if (!annotation->link_node().compare_exchange_strong(null, head_next)) { + // If |annotation|'s link node is not null, then it has been added to the + // list already and no work needs to be done. + return; + } + + // Check that the annotation's name is less than the maximum size. This is + // done here, since the Annotation constructor must be constexpr and this + // path is taken once per annotation. + DCHECK_LT(strlen(annotation->name_), Annotation::kNameMaxLength); + + // Update the head link to point to the new |annotation|. + while (!head_.link_node().compare_exchange_weak(head_next, annotation)) { + // Another thread has updated the head-next pointer, so try again with the + // re-loaded |head_next|. + annotation->link_node().store(head_next, std::memory_order_relaxed); + } +} + +AnnotationList::Iterator::Iterator(Annotation* head, const Annotation* tail) + : curr_(head), tail_(tail) {} + +AnnotationList::Iterator::~Iterator() = default; + +Annotation* AnnotationList::Iterator::operator*() const { + CHECK_NE(curr_, tail_); + return curr_; +} + +AnnotationList::Iterator& AnnotationList::Iterator::operator++() { + CHECK_NE(curr_, tail_); + curr_ = curr_->link_node(); + return *this; +} + +bool AnnotationList::Iterator::operator==( + const AnnotationList::Iterator& other) const { + return curr_ == other.curr_; +} + +AnnotationList::Iterator AnnotationList::begin() { + return Iterator(head_.link_node(), tail_pointer_); +} + +AnnotationList::Iterator AnnotationList::end() { + return Iterator(&tail_, tail_pointer_); +} + +} // namespace crashpad diff --git a/third_party/crashpad/crashpad/client/annotation_list.h b/third_party/crashpad/crashpad/client/annotation_list.h new file mode 100644 index 00000000000000..9485c46c4adfe3 --- /dev/null +++ b/third_party/crashpad/crashpad/client/annotation_list.h @@ -0,0 +1,94 @@ +// 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_CLIENT_ANNOTATION_LIST_H_ +#define CRASHPAD_CLIENT_ANNOTATION_LIST_H_ + +#include "base/macros.h" +#include "client/annotation.h" + +namespace crashpad { + +//! \brief A list that contains all the currently set annotations. +//! +//! An instance of this class must be registered on the \a CrashpadInfo +//! structure in order to use the annotations system. Once a list object has +//! been registered on the CrashpadInfo, a different instance should not +//! be used instead. +class AnnotationList { + public: + AnnotationList(); + ~AnnotationList(); + + //! \brief Returns the instance of the list that has been registered on the + //! CrashapdInfo structure. + static AnnotationList* Get(); + + //! \brief Returns the instace of the list, creating and registering + //! it if one is not already set on the CrashapdInfo structure. + static AnnotationList* Register(); + + //! \brief Adds \a annotation to the global list. This method does not need + //! to be called by clients directly. The Annotation object will do so + //! automatically. + //! + //! Once an annotation is added to the list, it is not removed. This is + //! because the AnnotationList avoids the use of locks/mutexes, in case it is + //! being manipulated in a compromised context. Instead, an Annotation keeps + //! track of when it has been cleared, which excludes it from a crash report. + //! This design also avoids linear scans of the list when repeatedly setting + //! and/or clearing the value. + void Add(Annotation* annotation); + + //! \brief An InputIterator for the AnnotationList. + class Iterator { + public: + ~Iterator(); + + Annotation* operator*() const; + Iterator& operator++(); + bool operator==(const Iterator& other) const; + bool operator!=(const Iterator& other) const { return !(*this == other); } + + private: + friend class AnnotationList; + Iterator(Annotation* head, const Annotation* tail); + + Annotation* curr_; + const Annotation* const tail_; + + // Copy and assign are required. + }; + + //! \brief Returns an iterator to the first element of the annotation list. + Iterator begin(); + + //! \brief Returns an iterator past the last element of the annotation list. + Iterator end(); + + private: + // To make it easier for the handler to locate the dummy tail node, store the + // pointer. Placed first for packing. + const Annotation* const tail_pointer_; + + // Dummy linked-list head and tail elements of \a Annotation::Type::kInvalid. + Annotation head_; + Annotation tail_; + + DISALLOW_COPY_AND_ASSIGN(AnnotationList); +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_ANNOTATION_LIST_H_ diff --git a/third_party/crashpad/crashpad/client/annotation_list_test.cc b/third_party/crashpad/crashpad/client/annotation_list_test.cc new file mode 100644 index 00000000000000..ef3039c5f6b0b4 --- /dev/null +++ b/third_party/crashpad/crashpad/client/annotation_list_test.cc @@ -0,0 +1,183 @@ +// 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/annotation.h" + +#include +#include + +#include "base/rand_util.h" +#include "client/crashpad_info.h" +#include "gtest/gtest.h" +#include "util/misc/clock.h" +#include "util/thread/thread.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(AnnotationListStatic, Register) { + ASSERT_FALSE(AnnotationList::Get()); + EXPECT_TRUE(AnnotationList::Register()); + EXPECT_TRUE(AnnotationList::Get()); + EXPECT_EQ(AnnotationList::Get(), AnnotationList::Register()); + + // This isn't expected usage of the AnnotationList API, but it is necessary + // for testing. + AnnotationList* list = AnnotationList::Get(); + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(nullptr); + delete list; + + EXPECT_FALSE(AnnotationList::Get()); +} + +class AnnotationList : public testing::Test { + public: + void SetUp() override { + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(&annotations_); + } + + void TearDown() override { + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(nullptr); + } + + // NOTE: Annotations should be declared at file-scope, but in order to test + // them, they are declared as part of the test. These members are public so + // they are accessible from global helpers. + crashpad::StringAnnotation<8> one_{"First"}; + crashpad::StringAnnotation<256> two_{"Second"}; + crashpad::StringAnnotation<101> three_{"First"}; + + protected: + using AllAnnotations = std::vector>; + + AllAnnotations CollectAnnotations() { + AllAnnotations annotations; + + for (Annotation* curr : annotations_) { + if (!curr->is_set()) + continue; + std::string value(static_cast(curr->value()), curr->size()); + annotations.push_back(std::make_pair(curr->name(), value)); + } + + return annotations; + } + + bool ContainsNameValue(const AllAnnotations& annotations, + const std::string& name, + const std::string& value) { + return std::find(annotations.begin(), + annotations.end(), + std::make_pair(name, value)) != annotations.end(); + } + + crashpad::AnnotationList annotations_; +}; + +TEST_F(AnnotationList, SetAndClear) { + one_.Set("this is a value longer than 8 bytes"); + AllAnnotations annotations = CollectAnnotations(); + + EXPECT_EQ(1u, annotations.size()); + EXPECT_TRUE(ContainsNameValue(annotations, "First", "this is ")); + + one_.Clear(); + + EXPECT_EQ(0u, CollectAnnotations().size()); + + one_.Set("short"); + two_.Set(std::string(500, 'A').data()); + + annotations = CollectAnnotations(); + EXPECT_EQ(2u, annotations.size()); + + EXPECT_EQ(5u, one_.size()); + EXPECT_EQ(256u, two_.size()); + + EXPECT_TRUE(ContainsNameValue(annotations, "First", "short")); + EXPECT_TRUE(ContainsNameValue(annotations, "Second", std::string(256, 'A'))); +} + +TEST_F(AnnotationList, DuplicateKeys) { + ASSERT_EQ(0u, CollectAnnotations().size()); + + one_.Set("1"); + three_.Set("2"); + + AllAnnotations annotations = CollectAnnotations(); + EXPECT_EQ(2u, annotations.size()); + + EXPECT_TRUE(ContainsNameValue(annotations, "First", "1")); + EXPECT_TRUE(ContainsNameValue(annotations, "First", "2")); + + one_.Clear(); + + annotations = CollectAnnotations(); + EXPECT_EQ(1u, annotations.size()); +} + +class RaceThread : public Thread { + public: + explicit RaceThread(test::AnnotationList* test) : Thread(), test_(test) {} + + private: + void ThreadMain() override { + for (int i = 0; i <= 50; ++i) { + if (i % 2 == 0) { + test_->three_.Set("three"); + test_->two_.Clear(); + } else { + test_->three_.Clear(); + } + SleepNanoseconds(base::RandInt(1, 1000)); + } + } + + test::AnnotationList* test_; +}; + +TEST_F(AnnotationList, MultipleThreads) { + ASSERT_EQ(0u, CollectAnnotations().size()); + + RaceThread other_thread(this); + other_thread.Start(); + + for (int i = 0; i <= 50; ++i) { + if (i % 2 == 0) { + one_.Set("one"); + two_.Set("two"); + } else { + one_.Clear(); + } + SleepNanoseconds(base::RandInt(1, 1000)); + } + + other_thread.Join(); + + AllAnnotations annotations = CollectAnnotations(); + EXPECT_GE(annotations.size(), 2u); + EXPECT_LE(annotations.size(), 3u); + + EXPECT_TRUE(ContainsNameValue(annotations, "First", "one")); + EXPECT_TRUE(ContainsNameValue(annotations, "First", "three")); + + if (annotations.size() == 3) { + EXPECT_TRUE(ContainsNameValue(annotations, "Second", "two")); + } +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/third_party/crashpad/crashpad/client/annotation_test.cc b/third_party/crashpad/crashpad/client/annotation_test.cc new file mode 100644 index 00000000000000..25530715873169 --- /dev/null +++ b/third_party/crashpad/crashpad/client/annotation_test.cc @@ -0,0 +1,112 @@ +// 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/annotation.h" + +#include + +#include "client/annotation_list.h" +#include "client/crashpad_info.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +class Annotation : public testing::Test { + public: + void SetUp() override { + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(&annotations_); + } + + void TearDown() override { + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(nullptr); + } + + size_t AnnotationsCount() { + size_t result = 0; + for (auto* annotation : annotations_) { + if (annotation->is_set()) + ++result; + } + return result; + } + + protected: + crashpad::AnnotationList annotations_; +}; + +TEST_F(Annotation, Basics) { + constexpr crashpad::Annotation::Type kType = + crashpad::Annotation::UserDefinedType(1); + + const char kName[] = "annotation 1"; + char buffer[1024]; + crashpad::Annotation annotation(kType, kName, buffer); + + EXPECT_FALSE(annotation.is_set()); + EXPECT_EQ(0u, AnnotationsCount()); + + EXPECT_EQ(kType, annotation.type()); + EXPECT_EQ(0u, annotation.size()); + EXPECT_EQ(std::string(kName), annotation.name()); + EXPECT_EQ(buffer, annotation.value()); + + annotation.SetSize(10); + + EXPECT_TRUE(annotation.is_set()); + EXPECT_EQ(1u, AnnotationsCount()); + + EXPECT_EQ(10u, annotation.size()); + EXPECT_EQ(&annotation, *annotations_.begin()); + + annotation.Clear(); + + EXPECT_FALSE(annotation.is_set()); + EXPECT_EQ(0u, AnnotationsCount()); + + EXPECT_EQ(0u, annotation.size()); +} + +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)); + + annotation.Set("test"); + + EXPECT_TRUE(annotation.is_set()); + EXPECT_EQ(1u, AnnotationsCount()); + + EXPECT_EQ(4u, annotation.size()); + EXPECT_EQ(std::string("test"), value_ptr); + + annotation.Set("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())); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/third_party/crashpad/crashpad/client/client.gyp b/third_party/crashpad/crashpad/client/client.gyp index 0e4a89d87e6147..4b14c4bf1669fb 100644 --- a/third_party/crashpad/crashpad/client/client.gyp +++ b/third_party/crashpad/crashpad/client/client.gyp @@ -29,6 +29,10 @@ '..', ], 'sources': [ + 'annotation.cc', + 'annotation.h', + 'annotation_list.cc', + 'annotation_list.h', 'capture_context_mac.S', 'capture_context_mac.h', 'crash_report_database.cc', diff --git a/third_party/crashpad/crashpad/client/client_test.gyp b/third_party/crashpad/crashpad/client/client_test.gyp index 03cde2cbaaa060..4ea4ef2b737c0f 100644 --- a/third_party/crashpad/crashpad/client/client_test.gyp +++ b/third_party/crashpad/crashpad/client/client_test.gyp @@ -35,6 +35,8 @@ '..', ], 'sources': [ + 'annotation_test.cc', + 'annotation_list_test.cc', 'capture_context_mac_test.cc', 'crash_report_database_test.cc', 'crashpad_client_win_test.cc', diff --git a/third_party/crashpad/crashpad/client/crashpad_info.cc b/third_party/crashpad/crashpad/client/crashpad_info.cc index 18866ba3173c0b..9452382b794da9 100644 --- a/third_party/crashpad/crashpad/client/crashpad_info.cc +++ b/third_party/crashpad/crashpad/client/crashpad_info.cc @@ -105,7 +105,8 @@ CrashpadInfo::CrashpadInfo() padding_1_(0), extra_memory_ranges_(nullptr), simple_annotations_(nullptr), - user_data_minidump_stream_head_(nullptr) + user_data_minidump_stream_head_(nullptr), + annotations_list_(nullptr) #if !defined(NDEBUG) && defined(OS_WIN) , invalid_read_detection_(0xbadc0de) diff --git a/third_party/crashpad/crashpad/client/crashpad_info.h b/third_party/crashpad/crashpad/client/crashpad_info.h index 77c9097a7637b2..7ce55edc23b7a1 100644 --- a/third_party/crashpad/crashpad/client/crashpad_info.h +++ b/third_party/crashpad/crashpad/client/crashpad_info.h @@ -19,6 +19,7 @@ #include "base/macros.h" #include "build/build_config.h" +#include "client/annotation_list.h" #include "client/simple_address_range_bag.h" #include "client/simple_string_dictionary.h" #include "util/misc/tri_state.h" @@ -109,6 +110,33 @@ struct CrashpadInfo { return simple_annotations_; } + //! \brief Sets the annotations list. + //! + //! Unlike the \a simple_annotations structure, the \a annotations can + //! typed data and it is not limited to a dictionary form. Annotations are + //! interpreted by Crashpad as module-level annotations. + //! + //! Annotations may exist in \a annotations_list at the time that this + //! method is called, or they may be added, removed, or modified in \a + //! annotations_list after this method is called. + //! + //! \param[in] annotations_list A list of set Annotation objects that maintain + //! arbitrary, typed key-value state. The CrashpadInfo object does not + //! take ownership of the AnnotationsList object. It is the caller’s + //! responsibility to ensure that this pointer remains valid while it is + //! in effect for a CrashpadInfo object. + //! + //! \sa annotations_list() + //! \sa AnnotationList::Register() + void set_annotations_list(AnnotationList* list) { annotations_list_ = list; } + + //! \return The annotations list. + //! + //! \sa set_annotations_list() + //! \sa AnnotationList::Get() + //! \sa AnnotationList::Register() + AnnotationList* annotations_list() const { return annotations_list_; } + //! \brief Enables or disables Crashpad handler processing. //! //! When handling an exception, the Crashpad handler will scan all modules in @@ -218,6 +246,7 @@ struct CrashpadInfo { SimpleAddressRangeBag* extra_memory_ranges_; // weak SimpleStringDictionary* simple_annotations_; // weak internal::UserDataMinidumpStreamListEntry* user_data_minidump_stream_head_; + AnnotationList* annotations_list_; // weak #if !defined(NDEBUG) && defined(OS_WIN) uint32_t invalid_read_detection_; diff --git a/third_party/crashpad/crashpad/compat/compat.gyp b/third_party/crashpad/crashpad/compat/compat.gyp index 52cca468c551ea..b0aec0af32222b 100644 --- a/third_party/crashpad/crashpad/compat/compat.gyp +++ b/third_party/crashpad/crashpad/compat/compat.gyp @@ -54,6 +54,7 @@ 'win/sys/types.h', 'win/time.cc', 'win/time.h', + 'win/winbase.h', 'win/winnt.h', 'win/winternl.h', ], diff --git a/third_party/crashpad/crashpad/compat/win/winbase.h b/third_party/crashpad/crashpad/compat/win/winbase.h new file mode 100644 index 00000000000000..ffd850bf281970 --- /dev/null +++ b/third_party/crashpad/crashpad/compat/win/winbase.h @@ -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. + +#ifndef CRASHPAD_COMPAT_WIN_WINBASE_H_ +#define CRASHPAD_COMPAT_WIN_WINBASE_H_ + +// include_next +#include <../um/winbase.h> + +// 10.0.15063.0 SDK + +#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE +#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2) +#endif + +#endif // CRASHPAD_COMPAT_WIN_WINBASE_H_ diff --git a/third_party/crashpad/crashpad/handler/crashpad_handler_test.cc b/third_party/crashpad/crashpad/handler/crashpad_handler_test.cc index 125157ff3b351d..1534443a34763d 100644 --- a/third_party/crashpad/crashpad/handler/crashpad_handler_test.cc +++ b/third_party/crashpad/crashpad/handler/crashpad_handler_test.cc @@ -28,11 +28,14 @@ #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" namespace crashpad { namespace test { namespace { +constexpr DWORD kExpectedExitCode = 0x1CEB00DA; + void StartAndCrashWithExtendedHandler(const base::FilePath& temp_dir) { base::FilePath handler_path = TestPaths::Executable().DirName().Append( FILE_PATH_LITERAL("crashpad_handler_test_extended_handler.exe")); @@ -47,7 +50,14 @@ void StartAndCrashWithExtendedHandler(const base::FilePath& temp_dir) { false, false)); - __debugbreak(); + // It appears that the GoogleTest fixture will catch and handle exceptions + // from here. Hence the fabricated crash in favor of raising an exception. + EXCEPTION_RECORD exception_record = {kExpectedExitCode, + EXCEPTION_NONCONTINUABLE}; + CONTEXT context; + CaptureContext(&context); + EXCEPTION_POINTERS exception_pointers = {&exception_record, &context}; + CrashpadClient::DumpAndCrash(&exception_pointers); } class CrashWithExtendedHandler final : public WinMultiprocessWithTempDir { @@ -59,7 +69,7 @@ class CrashWithExtendedHandler final : public WinMultiprocessWithTempDir { void ValidateGeneratedDump(); void WinMultiprocessParent() override { - SetExpectedChildExitCode(EXCEPTION_BREAKPOINT); + SetExpectedChildExitCode(kExpectedExitCode); } void WinMultiprocessChild() override { diff --git a/third_party/crashpad/crashpad/snapshot/linux/process_reader.cc b/third_party/crashpad/crashpad/snapshot/linux/process_reader.cc index 904fdcedd4cc89..c9a5a3825212a9 100644 --- a/third_party/crashpad/crashpad/snapshot/linux/process_reader.cc +++ b/third_party/crashpad/crashpad/snapshot/linux/process_reader.cc @@ -192,8 +192,7 @@ bool ProcessReader::Initialize(PtraceConnection* connection) { return false; } - process_memory_.reset(new ProcessMemoryLinux()); - if (!process_memory_->Initialize(pid)) { + if (!process_memory_.Initialize(pid)) { return false; } diff --git a/third_party/crashpad/crashpad/snapshot/linux/process_reader.h b/third_party/crashpad/crashpad/snapshot/linux/process_reader.h index 857083836bc075..26d777e46f09e4 100644 --- a/third_party/crashpad/crashpad/snapshot/linux/process_reader.h +++ b/third_party/crashpad/crashpad/snapshot/linux/process_reader.h @@ -18,7 +18,6 @@ #include #include -#include #include #include "base/macros.h" @@ -79,7 +78,7 @@ class ProcessReader { pid_t ParentProcessID() const { return process_info_.ParentProcessID(); } //! \brief Return a memory reader for the target process. - ProcessMemory* Memory() { return process_memory_.get(); } + ProcessMemory* Memory() { return &process_memory_; } //! \brief Return a memory map of the target process. MemoryMap* GetMemoryMap() { return &memory_map_; } @@ -113,9 +112,9 @@ class ProcessReader { PtraceConnection* connection_; // weak ProcessInfo process_info_; - class MemoryMap memory_map_; + MemoryMap memory_map_; std::vector threads_; - std::unique_ptr process_memory_; + ProcessMemoryLinux process_memory_; bool is_64_bit_; bool initialized_threads_; InitializationStateDcheck initialized_; diff --git a/third_party/crashpad/crashpad/snapshot/linux/process_reader_test.cc b/third_party/crashpad/crashpad/snapshot/linux/process_reader_test.cc index 5c8b8cbadb95e6..08edf02a95f42a 100644 --- a/third_party/crashpad/crashpad/snapshot/linux/process_reader_test.cc +++ b/third_party/crashpad/crashpad/snapshot/linux/process_reader_test.cc @@ -24,6 +24,7 @@ #include #include +#include #include #include diff --git a/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype b/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype index 713bb09d718a7a..41af5fc9c605a4 100644 --- a/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype +++ b/third_party/crashpad/crashpad/snapshot/mac/process_types/crashpad_info.proctype @@ -47,4 +47,7 @@ PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo) // UserDataStreamListEntry* PROCESS_TYPE_STRUCT_MEMBER(Pointer, user_data_minidump_stream_head) + + // AnnotationList* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, annotations_list) PROCESS_TYPE_STRUCT_END(CrashpadInfo) diff --git a/third_party/crashpad/crashpad/snapshot/win/end_to_end_test.py b/third_party/crashpad/crashpad/snapshot/win/end_to_end_test.py index e6df56e96ebf0d..c640a5482b950b 100755 --- a/third_party/crashpad/crashpad/snapshot/win/end_to_end_test.py +++ b/third_party/crashpad/crashpad/snapshot/win/end_to_end_test.py @@ -462,7 +462,7 @@ def main(args): return 1 z7_dump_path = None - if not args[0].endswith('x64'): + if not args[0].endswith('_x64'): z7_dump_path = GetDumpFromZ7Program(args[0], pipe_name) if not z7_dump_path: return 1 diff --git a/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win_test.cc b/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win_test.cc index cc8dd9d4a91289..a782ae66f08d70 100644 --- a/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win_test.cc +++ b/third_party/crashpad/crashpad/snapshot/win/exception_snapshot_win_test.cc @@ -23,6 +23,7 @@ #include "gtest/gtest.h" #include "snapshot/win/process_snapshot_win.h" #include "test/errors.h" +#include "test/gtest_disabled.h" #include "test/test_paths.h" #include "test/win/child_launcher.h" #include "util/file/file_io.h" @@ -120,7 +121,7 @@ class CrashingDelegate : public ExceptionHandlerServer::Delegate { DISALLOW_COPY_AND_ASSIGN(CrashingDelegate); }; -void TestCrashingChild(const base::string16& directory_modification) { +void TestCrashingChild(const base::FilePath& directory) { // Set up the registration server on a background thread. ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr)); ASSERT_TRUE(server_ready.is_valid()) << ErrorMessage("CreateEvent"); @@ -140,13 +141,13 @@ void TestCrashingChild(const base::string16& directory_modification) { << ErrorMessage("WaitForSingleObject"); // Spawn a child process, passing it the pipe name to connect to. - base::FilePath test_executable = TestPaths::Executable(); - std::wstring child_test_executable = - test_executable.DirName() - .Append(directory_modification) - .Append(test_executable.BaseName().RemoveFinalExtension().value() + - L"_crashing_child.exe") - .value(); + std::wstring child_test_executable = directory + .Append(TestPaths::Executable() + .BaseName() + .RemoveFinalExtension() + .value() + + L"_crashing_child.exe") + .value(); ChildLauncher child(child_test_executable, pipe_name); ASSERT_NO_FATAL_FAILURE(child.Start()); @@ -165,16 +166,17 @@ void TestCrashingChild(const base::string16& directory_modification) { } TEST(ExceptionSnapshotWinTest, ChildCrash) { - TestCrashingChild(FILE_PATH_LITERAL(".")); + TestCrashingChild(TestPaths::Executable().DirName()); } #if defined(ARCH_CPU_64_BITS) TEST(ExceptionSnapshotWinTest, ChildCrashWOW64) { -#ifndef NDEBUG - TestCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Debug")); -#else - TestCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Release")); -#endif + base::FilePath output_32_bit_directory = TestPaths::Output32BitDirectory(); + if (output_32_bit_directory.empty()) { + DISABLED_TEST(); + } + + TestCrashingChild(output_32_bit_directory); } #endif // ARCH_CPU_64_BITS @@ -227,8 +229,7 @@ class SimulateDelegate : public ExceptionHandlerServer::Delegate { DISALLOW_COPY_AND_ASSIGN(SimulateDelegate); }; -void TestDumpWithoutCrashingChild( - const base::string16& directory_modification) { +void TestDumpWithoutCrashingChild(const base::FilePath& directory) { // Set up the registration server on a background thread. ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr)); ASSERT_TRUE(server_ready.is_valid()) << ErrorMessage("CreateEvent"); @@ -248,11 +249,12 @@ void TestDumpWithoutCrashingChild( << ErrorMessage("WaitForSingleObject"); // Spawn a child process, passing it the pipe name to connect to. - base::FilePath test_executable = TestPaths::Executable(); std::wstring child_test_executable = - test_executable.DirName() - .Append(directory_modification) - .Append(test_executable.BaseName().RemoveFinalExtension().value() + + directory + .Append(TestPaths::Executable() + .BaseName() + .RemoveFinalExtension() + .value() + L"_dump_without_crashing.exe") .value(); ChildLauncher child(child_test_executable, pipe_name); @@ -273,16 +275,17 @@ void TestDumpWithoutCrashingChild( } TEST(SimulateCrash, ChildDumpWithoutCrashing) { - TestDumpWithoutCrashingChild(FILE_PATH_LITERAL(".")); + TestDumpWithoutCrashingChild(TestPaths::Executable().DirName()); } #if defined(ARCH_CPU_64_BITS) TEST(SimulateCrash, ChildDumpWithoutCrashingWOW64) { -#ifndef NDEBUG - TestDumpWithoutCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Debug")); -#else - TestDumpWithoutCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Release")); -#endif + base::FilePath output_32_bit_directory = TestPaths::Output32BitDirectory(); + if (output_32_bit_directory.empty()) { + DISABLED_TEST(); + } + + TestDumpWithoutCrashingChild(output_32_bit_directory); } #endif // ARCH_CPU_64_BITS diff --git a/third_party/crashpad/crashpad/snapshot/win/extra_memory_ranges_test.cc b/third_party/crashpad/crashpad/snapshot/win/extra_memory_ranges_test.cc index a012027b409853..d0820a040995be 100644 --- a/third_party/crashpad/crashpad/snapshot/win/extra_memory_ranges_test.cc +++ b/third_party/crashpad/crashpad/snapshot/win/extra_memory_ranges_test.cc @@ -25,6 +25,7 @@ #include "client/simple_address_range_bag.h" #include "gtest/gtest.h" #include "snapshot/win/process_snapshot_win.h" +#include "test/gtest_disabled.h" #include "test/test_paths.h" #include "test/win/child_launcher.h" #include "util/file/file_io.h" @@ -42,16 +43,15 @@ enum TestType { kCrashDebugBreak, }; -void TestExtraMemoryRanges(TestType type, - const base::string16& directory_modification) { +void TestExtraMemoryRanges(TestType type, const base::FilePath& directory) { // Spawn a child process, passing it the pipe name to connect to. - base::FilePath test_executable = TestPaths::Executable(); - std::wstring child_test_executable = - test_executable.DirName() - .Append(directory_modification) - .Append(test_executable.BaseName().RemoveFinalExtension().value() + - L"_extra_memory_ranges.exe") - .value(); + std::wstring child_test_executable = directory + .Append(TestPaths::Executable() + .BaseName() + .RemoveFinalExtension() + .value() + + L"_extra_memory_ranges.exe") + .value(); ChildLauncher child(child_test_executable, L""); ASSERT_NO_FATAL_FAILURE(child.Start()); @@ -101,30 +101,30 @@ void TestExtraMemoryRanges(TestType type, } TEST(ExtraMemoryRanges, DontCrash) { - TestExtraMemoryRanges(kDontCrash, FILE_PATH_LITERAL(".")); + TestExtraMemoryRanges(kDontCrash, TestPaths::Executable().DirName()); } TEST(ExtraMemoryRanges, CrashDebugBreak) { - TestExtraMemoryRanges(kCrashDebugBreak, FILE_PATH_LITERAL(".")); + TestExtraMemoryRanges(kCrashDebugBreak, TestPaths::Executable().DirName()); } #if defined(ARCH_CPU_64_BITS) TEST(ExtraMemoryRanges, DontCrashWOW64) { -#ifndef NDEBUG - TestExtraMemoryRanges(kDontCrash, FILE_PATH_LITERAL("..\\..\\out\\Debug")); -#else - TestExtraMemoryRanges(kDontCrash, FILE_PATH_LITERAL("..\\..\\out\\Release")); -#endif + base::FilePath output_32_bit_directory = TestPaths::Output32BitDirectory(); + if (output_32_bit_directory.empty()) { + DISABLED_TEST(); + } + + TestExtraMemoryRanges(kDontCrash, output_32_bit_directory); } TEST(ExtraMemoryRanges, CrashDebugBreakWOW64) { -#ifndef NDEBUG - TestExtraMemoryRanges(kCrashDebugBreak, - FILE_PATH_LITERAL("..\\..\\out\\Debug")); -#else - TestExtraMemoryRanges(kCrashDebugBreak, - FILE_PATH_LITERAL("..\\..\\out\\Release")); -#endif + base::FilePath output_32_bit_directory = TestPaths::Output32BitDirectory(); + if (output_32_bit_directory.empty()) { + DISABLED_TEST(); + } + + TestExtraMemoryRanges(kCrashDebugBreak, output_32_bit_directory); } #endif // ARCH_CPU_64_BITS diff --git a/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader_test.cc b/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader_test.cc index a20e986bb084f9..f69f5ab8be8ec2 100644 --- a/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader_test.cc +++ b/third_party/crashpad/crashpad/snapshot/win/pe_image_annotations_reader_test.cc @@ -29,6 +29,7 @@ #include "gtest/gtest.h" #include "snapshot/win/pe_image_reader.h" #include "snapshot/win/process_reader_win.h" +#include "test/gtest_disabled.h" #include "test/test_paths.h" #include "test/win/child_launcher.h" #include "util/file/file_io.h" @@ -46,16 +47,15 @@ enum TestType { kCrashDebugBreak, }; -void TestAnnotationsOnCrash(TestType type, - const base::string16& directory_modification) { +void TestAnnotationsOnCrash(TestType type, const base::FilePath& directory) { // Spawn a child process, passing it the pipe name to connect to. - base::FilePath test_executable = TestPaths::Executable(); - std::wstring child_test_executable = - test_executable.DirName() - .Append(directory_modification) - .Append(test_executable.BaseName().RemoveFinalExtension().value() + - L"_simple_annotations.exe") - .value(); + std::wstring child_test_executable = directory + .Append(TestPaths::Executable() + .BaseName() + .RemoveFinalExtension() + .value() + + L"_simple_annotations.exe") + .value(); ChildLauncher child(child_test_executable, L""); ASSERT_NO_FATAL_FAILURE(child.Start()); @@ -112,30 +112,30 @@ void TestAnnotationsOnCrash(TestType type, } TEST(PEImageAnnotationsReader, DontCrash) { - TestAnnotationsOnCrash(kDontCrash, FILE_PATH_LITERAL(".")); + TestAnnotationsOnCrash(kDontCrash, TestPaths::Executable().DirName()); } TEST(PEImageAnnotationsReader, CrashDebugBreak) { - TestAnnotationsOnCrash(kCrashDebugBreak, FILE_PATH_LITERAL(".")); + TestAnnotationsOnCrash(kCrashDebugBreak, TestPaths::Executable().DirName()); } #if defined(ARCH_CPU_64_BITS) TEST(PEImageAnnotationsReader, DontCrashWOW64) { -#ifndef NDEBUG - TestAnnotationsOnCrash(kDontCrash, FILE_PATH_LITERAL("..\\..\\out\\Debug")); -#else - TestAnnotationsOnCrash(kDontCrash, FILE_PATH_LITERAL("..\\..\\out\\Release")); -#endif + base::FilePath output_32_bit_directory = TestPaths::Output32BitDirectory(); + if (output_32_bit_directory.empty()) { + DISABLED_TEST(); + } + + TestAnnotationsOnCrash(kDontCrash, output_32_bit_directory); } TEST(PEImageAnnotationsReader, CrashDebugBreakWOW64) { -#ifndef NDEBUG - TestAnnotationsOnCrash(kCrashDebugBreak, - FILE_PATH_LITERAL("..\\..\\out\\Debug")); -#else - TestAnnotationsOnCrash(kCrashDebugBreak, - FILE_PATH_LITERAL("..\\..\\out\\Release")); -#endif + base::FilePath output_32_bit_directory = TestPaths::Output32BitDirectory(); + if (output_32_bit_directory.empty()) { + DISABLED_TEST(); + } + + TestAnnotationsOnCrash(kCrashDebugBreak, output_32_bit_directory); } #endif // ARCH_CPU_64_BITS diff --git a/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h b/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h index be1854195a31a1..56a991b842e8f8 100644 --- a/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h +++ b/third_party/crashpad/crashpad/snapshot/win/pe_image_reader.h @@ -48,6 +48,7 @@ struct CrashpadInfo { typename Traits::Pointer extra_address_ranges; typename Traits::Pointer simple_annotations; typename Traits::Pointer user_data_minidump_stream_head; + typename Traits::Pointer annotations_list; }; } // namespace process_types diff --git a/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win_test.cc b/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win_test.cc index 1bb2027b413a66..839ba48ac69f8c 100644 --- a/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win_test.cc +++ b/third_party/crashpad/crashpad/snapshot/win/process_snapshot_win_test.cc @@ -20,6 +20,7 @@ #include "snapshot/win/pe_image_reader.h" #include "snapshot/win/process_reader_win.h" #include "test/errors.h" +#include "test/gtest_disabled.h" #include "test/test_paths.h" #include "test/win/child_launcher.h" #include "util/file/file_io.h" @@ -30,20 +31,20 @@ namespace crashpad { namespace test { namespace { -void TestImageReaderChild(const base::string16& directory_modification) { +void TestImageReaderChild(const base::FilePath& directory) { UUID done_uuid; done_uuid.InitializeWithNew(); ScopedKernelHANDLE done( CreateEvent(nullptr, true, false, done_uuid.ToString16().c_str())); ASSERT_TRUE(done.is_valid()) << ErrorMessage("CreateEvent"); - base::FilePath test_executable = TestPaths::Executable(); - std::wstring child_test_executable = - test_executable.DirName() - .Append(directory_modification) - .Append(test_executable.BaseName().RemoveFinalExtension().value() + - L"_image_reader.exe") - .value(); + std::wstring child_test_executable = directory + .Append(TestPaths::Executable() + .BaseName() + .RemoveFinalExtension() + .value() + + L"_image_reader.exe") + .value(); ChildLauncher child(child_test_executable, done_uuid.ToString16()); ASSERT_NO_FATAL_FAILURE(child.Start()); @@ -112,16 +113,17 @@ void TestImageReaderChild(const base::string16& directory_modification) { } TEST(ProcessSnapshotTest, CrashpadInfoChild) { - TestImageReaderChild(FILE_PATH_LITERAL(".")); + TestImageReaderChild(TestPaths::Executable().DirName()); } #if defined(ARCH_CPU_64_BITS) TEST(ProcessSnapshotTest, CrashpadInfoChildWOW64) { -#ifndef NDEBUG - TestImageReaderChild(FILE_PATH_LITERAL("..\\..\\out\\Debug")); -#else - TestImageReaderChild(FILE_PATH_LITERAL("..\\..\\out\\Release")); -#endif + base::FilePath output_32_bit_directory = TestPaths::Output32BitDirectory(); + if (output_32_bit_directory.empty()) { + DISABLED_TEST(); + } + + TestImageReaderChild(output_32_bit_directory); } #endif diff --git a/third_party/crashpad/crashpad/test/gmock_main.cc b/third_party/crashpad/crashpad/test/gmock_main.cc deleted file mode 100644 index 7f7bc9372bf347..00000000000000 --- a/third_party/crashpad/crashpad/test/gmock_main.cc +++ /dev/null @@ -1,38 +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 "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/main_arguments.h" - -#if defined(CRASHPAD_IN_CHROMIUM) -#include "base/bind.h" -#include "base/test/launcher/unit_test_launcher.h" -#include "base/test/test_suite.h" -#endif - -int main(int argc, char* argv[]) { - crashpad::test::InitializeMainArguments(argc, argv); -#if defined(CRASHPAD_IN_CHROMIUM) - // Writes a json file with test details which is needed by swarming. - base::TestSuite test_suite(argc, argv); - return base::LaunchUnitTests( - argc, - argv, - base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite))); -#else - testing::InitGoogleMock(&argc, argv); - return RUN_ALL_TESTS(); -#endif -} diff --git a/third_party/crashpad/crashpad/test/gtest_disabled.cc b/third_party/crashpad/crashpad/test/gtest_disabled.cc new file mode 100644 index 00000000000000..fab6802a637b9b --- /dev/null +++ b/third_party/crashpad/crashpad/test/gtest_disabled.cc @@ -0,0 +1,83 @@ +// 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/gtest_disabled.h" + +#include + +#include "base/format_macros.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" + +namespace crashpad { +namespace test { + +namespace { + +DisabledTestGtestEnvironment* g_instance; + +} // namespace + +// static +DisabledTestGtestEnvironment* DisabledTestGtestEnvironment::Get() { + if (!g_instance) { + g_instance = new DisabledTestGtestEnvironment(); + } + return g_instance; +} + +void DisabledTestGtestEnvironment::DisabledTest() { + const testing::TestInfo* test_info = + testing::UnitTest::GetInstance()->current_test_info(); + std::string disabled_test = base::StringPrintf( + "%s.%s", test_info->test_case_name(), test_info->name()); + + // Show a DISABLED message using a format similar to gtest, along with a hint + // explaining that OK or FAILED will also appear. + printf( + "This test has been disabled dynamically.\n" + "It will appear as both DISABLED and OK or FAILED.\n" + "[ DISABLED ] %s\n", + disabled_test.c_str()); + + disabled_tests_.push_back(disabled_test); +} + +DisabledTestGtestEnvironment::DisabledTestGtestEnvironment() + : testing::Environment(), + disabled_tests_() { + DCHECK(!g_instance); +} + +DisabledTestGtestEnvironment::~DisabledTestGtestEnvironment() { + DCHECK_EQ(this, g_instance); + g_instance = nullptr; +} + +void DisabledTestGtestEnvironment::TearDown() { + if (!disabled_tests_.empty()) { + printf( + "[ DISABLED ] %" PRIuS " dynamically disabled test%s, listed below:\n" + "[ DISABLED ] %s also counted in PASSED or FAILED below.\n", + disabled_tests_.size(), + disabled_tests_.size() == 1 ? "" : "s", + disabled_tests_.size() == 1 ? "This test is" : "These tests are"); + for (const std::string& disabled_test : disabled_tests_) { + printf("[ DISABLED ] %s\n", disabled_test.c_str()); + } + } +} + +} // namespace test +} // namespace crashpad diff --git a/third_party/crashpad/crashpad/test/gtest_disabled.h b/third_party/crashpad/crashpad/test/gtest_disabled.h new file mode 100644 index 00000000000000..9415cba2fd3c41 --- /dev/null +++ b/third_party/crashpad/crashpad/test/gtest_disabled.h @@ -0,0 +1,87 @@ +// 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_GTEST_DISABLED_H_ +#define CRASHPAD_TEST_GTEST_DISABLED_H_ + +#include +#include + +#include "base/macros.h" +#include "gtest/gtest.h" + +//! \file + +namespace crashpad { +namespace test { + +//! \brief Provides support for dynamically disabled gtest tests. +//! +//! A test runner must register this with gtest as follows prior to calling +//! `RUN_ALL_TESTS()`: +//! \code +//! testing::AddGlobalTestEnvironment( +//! crashpad::test::DisabledTestGtestEnvironment::Get()); +//! \endcode +class DisabledTestGtestEnvironment final : public testing::Environment { + public: + //! \brief Returns the DisabledTestGtestEnvironment singleton instance, + //! creating it if necessary. + static DisabledTestGtestEnvironment* Get(); + + //! \brief Displays a message about a test being disabled, and arranges for + //! this information to be duplicated in TearDown(). + //! + //! This method is for the internal use of the DISABLED_TEST() macro. Do not + //! call it directly, use the macro instead. + void DisabledTest(); + + private: + DisabledTestGtestEnvironment(); + ~DisabledTestGtestEnvironment() override; + + // testing::Environment: + void TearDown() override; + + std::vector disabled_tests_; + + DISALLOW_COPY_AND_ASSIGN(DisabledTestGtestEnvironment); +}; + +} // namespace test +} // namespace crashpad + +//! \brief Displays a message about a test being disabled, and returns early. +//! +//! gtest only provides a mechanism for tests to be disabled statically, by +//! prefixing test case names or test names with `DISABLED_`. When it is +//! necessary to disable tests dynamically, gtest provides no assistance. This +//! macro displays a message about the disabled test and returns early. The +//! dynamically disabled test will also be displayed during gtest global test +//! environment tear-down before the test executable exits. +//! +//! This macro may only be invoked from the context of a gtest test. +//! +//! There’s a long-standing gtest +//! feature request to provide this functionality directly in gtest, but +//! since it hasn’t been implemented, this macro provides a local mechanism to +//! achieve it. +#define DISABLED_TEST() \ + do { \ + ::crashpad::test::DisabledTestGtestEnvironment::Get()->DisabledTest(); \ + return; \ + } while (false) + +#endif // CRASHPAD_TEST_GTEST_DISABLED_H_ diff --git a/third_party/crashpad/crashpad/test/gtest_main.cc b/third_party/crashpad/crashpad/test/gtest_main.cc index c3f6899ccb4c78..6e08282899cfc6 100644 --- a/third_party/crashpad/crashpad/test/gtest_main.cc +++ b/third_party/crashpad/crashpad/test/gtest_main.cc @@ -12,26 +12,62 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "build/build_config.h" #include "gtest/gtest.h" +#include "test/gtest_disabled.h" #include "test/main_arguments.h" +#if defined(CRASHPAD_TEST_LAUNCHER_GMOCK) +#include "gmock/gmock.h" +#endif // CRASHPAD_TEST_LAUNCHER_GMOCK + +#if defined(OS_WIN) +#include "test/win/win_child_process.h" +#endif // OS_WIN + #if defined(CRASHPAD_IN_CHROMIUM) #include "base/bind.h" #include "base/test/launcher/unit_test_launcher.h" #include "base/test/test_suite.h" -#endif +#endif // CRASHPAD_IN_CHROMIUM int main(int argc, char* argv[]) { crashpad::test::InitializeMainArguments(argc, argv); -#if defined(CRASHPAD_IN_CHROMIUM) - // Writes a json file with test details which is needed by swarming. - base::TestSuite test_suite(argc, argv); - return base::LaunchUnitTests( - argc, - argv, - base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite))); -#else + +#if defined(CRASHPAD_TEST_LAUNCHER_GMOCK) + testing::InitGoogleMock(&argc, argv); +#elif defined(CRASHPAD_TEST_LAUNCHER_GTEST) testing::InitGoogleTest(&argc, argv); +#else // CRASHPAD_TEST_LAUNCHER_GTEST +#error #define CRASHPAD_TEST_LAUNCHER_GTEST or CRASHPAD_TEST_LAUNCHER_GMOCK +#endif // CRASHPAD_TEST_LAUNCHER_GTEST + + testing::AddGlobalTestEnvironment( + crashpad::test::DisabledTestGtestEnvironment::Get()); + +#if defined(CRASHPAD_IN_CHROMIUM) + +#if defined(OS_WIN) + // Chromium’s test launcher interferes with WinMultiprocess-based tests. Allow + // their child processes to be launched by the standard gtest-based test + // runner. + const bool use_chromium_test_launcher = + !crashpad::test::WinChildProcess::IsChildProcess(); +#else // OS_WIN + constexpr bool use_chromium_test_launcher = true; +#endif // OS_WIN + + if (use_chromium_test_launcher) { + // This supports --test-launcher-summary-output, which writes a JSON file + // containing test details needed by Swarming. + base::TestSuite test_suite(argc, argv); + return base::LaunchUnitTests( + argc, + argv, + base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite))); + } + +#endif // CRASHPAD_IN_CHROMIUM + return RUN_ALL_TESTS(); -#endif } diff --git a/third_party/crashpad/crashpad/test/test.gyp b/third_party/crashpad/crashpad/test/test.gyp index 31a83811a79c20..931d948581cb02 100644 --- a/third_party/crashpad/crashpad/test/test.gyp +++ b/third_party/crashpad/crashpad/test/test.gyp @@ -36,6 +36,8 @@ 'file.cc', 'file.h', 'gtest_death_check.h', + 'gtest_disabled.cc', + 'gtest_disabled.h', 'hex_string.cc', 'hex_string.h', 'linux/fake_ptrace_connection.cc', @@ -100,26 +102,40 @@ ], }, { - 'target_name': 'crashpad_gtest_main', + 'target_name': 'crashpad_gmock_main', 'type': 'static_library', 'dependencies': [ 'crashpad_test', + '../third_party/gtest/gmock.gyp:gmock', '../third_party/gtest/gtest.gyp:gtest', + '../third_party/mini_chromium/mini_chromium.gyp:base', + ], + 'include_dirs': [ + '..', + ], + 'defines': [ + 'CRASHPAD_TEST_LAUNCHER_GMOCK=1', ], 'sources': [ 'gtest_main.cc', ], }, { - 'target_name': 'crashpad_gmock_main', + 'target_name': 'crashpad_gtest_main', 'type': 'static_library', 'dependencies': [ 'crashpad_test', - '../third_party/gtest/gmock.gyp:gmock', '../third_party/gtest/gtest.gyp:gtest', + '../third_party/mini_chromium/mini_chromium.gyp:base', + ], + 'include_dirs': [ + '..', + ], + 'defines': [ + 'CRASHPAD_TEST_LAUNCHER_GTEST=1', ], 'sources': [ - 'gmock_main.cc', + 'gtest_main.cc', ], }, ], diff --git a/third_party/crashpad/crashpad/test/test_paths.cc b/third_party/crashpad/crashpad/test/test_paths.cc index 4c25603049c6aa..a6012085bf0f15 100644 --- a/third_party/crashpad/crashpad/test/test_paths.cc +++ b/third_party/crashpad/crashpad/test/test_paths.cc @@ -106,5 +106,19 @@ base::FilePath TestPaths::TestDataRoot() { return *test_data_root; } +#if defined(OS_WIN) && defined(ARCH_CPU_64_BITS) + +// static +base::FilePath TestPaths::Output32BitDirectory() { + const wchar_t* environment_value = _wgetenv(L"CRASHPAD_TEST_32_BIT_OUTPUT"); + if (!environment_value) { + return base::FilePath(); + } + + return base::FilePath(environment_value); +} + +#endif // defined(OS_WIN) && defined(ARCH_CPU_64_BITS) + } // namespace test } // namespace crashpad diff --git a/third_party/crashpad/crashpad/test/test_paths.h b/third_party/crashpad/crashpad/test/test_paths.h index 261ab9110c0b35..f5629ed5a351b6 100644 --- a/third_party/crashpad/crashpad/test/test_paths.h +++ b/third_party/crashpad/crashpad/test/test_paths.h @@ -17,6 +17,7 @@ #include "base/files/file_path.h" #include "base/macros.h" +#include "build/build_config.h" namespace crashpad { namespace test { @@ -42,6 +43,22 @@ class TestPaths { //! files. static base::FilePath TestDataRoot(); +#if (defined(OS_WIN) && defined(ARCH_CPU_64_BITS)) || DOXYGEN + //! \brief Returns the pathname of a directory containing 32-bit test build + //! output. + //! + //! Tests that require the use of 32-bit build output should call this + //! function to locate that output. This function is only provided to allow + //! 64-bit test code to locate 32-bit output. 32-bit test code can find 32-bit + //! output in its own directory, the parent of Executable(). + //! + //! If the `CRASHPAD_TEST_32_BIT_OUTPUT` environment variable is set, its + //! value will be returned. Otherwise, this function will return an empty + //! path, and tests that require the use of 32-bit build output should disable + //! themselves. The DISABLED_TEST() macro may be useful for this purpose. + static base::FilePath Output32BitDirectory(); +#endif + DISALLOW_IMPLICIT_CONSTRUCTORS(TestPaths); }; diff --git a/third_party/crashpad/crashpad/test/win/child_launcher.cc b/third_party/crashpad/crashpad/test/win/child_launcher.cc index 4ab1c816803a51..c5f897c31889da 100644 --- a/third_party/crashpad/crashpad/test/win/child_launcher.cc +++ b/third_party/crashpad/crashpad/test/win/child_launcher.cc @@ -15,6 +15,7 @@ #include "test/win/child_launcher.h" #include "gtest/gtest.h" +#include "test/errors.h" #include "util/win/command_line.h" namespace crashpad { @@ -47,25 +48,31 @@ void ChildLauncher::Start() { HANDLE stdout_read; HANDLE stdout_write; - ASSERT_TRUE(CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0)); + ASSERT_TRUE(CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0)) + << ErrorMessage("CreatePipe"); stdout_read_handle_.reset(stdout_read); ScopedFileHANDLE write_handle(stdout_write); ASSERT_TRUE( - SetHandleInformation(stdout_read_handle_.get(), HANDLE_FLAG_INHERIT, 0)); + SetHandleInformation(stdout_read_handle_.get(), HANDLE_FLAG_INHERIT, 0)) + << ErrorMessage("SetHandleInformation"); HANDLE stdin_read; HANDLE stdin_write; - ASSERT_TRUE(CreatePipe(&stdin_read, &stdin_write, &security_attributes, 0)); + ASSERT_TRUE(CreatePipe(&stdin_read, &stdin_write, &security_attributes, 0)) + << ErrorMessage("CreatePipe"); stdin_write_handle_.reset(stdin_write); ScopedFileHANDLE read_handle(stdin_read); ASSERT_TRUE( - SetHandleInformation(stdin_write_handle_.get(), HANDLE_FLAG_INHERIT, 0)); + SetHandleInformation(stdin_write_handle_.get(), HANDLE_FLAG_INHERIT, 0)) + << ErrorMessage("SetHandleInformation"); STARTUPINFO startup_info = {0}; startup_info.cb = sizeof(startup_info); startup_info.hStdInput = read_handle.get(); startup_info.hStdOutput = write_handle.get(); startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); + EXPECT_NE(startup_info.hStdError, INVALID_HANDLE_VALUE) + << ErrorMessage("GetStdHandle"); startup_info.dwFlags = STARTF_USESTDHANDLES; PROCESS_INFORMATION process_information; std::wstring command_line; @@ -81,7 +88,8 @@ void ChildLauncher::Start() { nullptr, nullptr, &startup_info, - &process_information)); + &process_information)) + << ErrorMessage("CreateProcess"); // Take ownership of the two process handles returned. main_thread_handle_.reset(process_information.hThread); process_handle_.reset(process_information.hProcess); @@ -89,10 +97,11 @@ void ChildLauncher::Start() { DWORD ChildLauncher::WaitForExit() { EXPECT_TRUE(process_handle_.is_valid()); - EXPECT_EQ(WaitForSingleObject(process_handle_.get(), INFINITE), - WAIT_OBJECT_0); + EXPECT_EQ(WaitForSingleObject(process_handle_.get(), INFINITE), WAIT_OBJECT_0) + << ErrorMessage("WaitForSingleObject"); DWORD exit_code = 0; - EXPECT_TRUE(GetExitCodeProcess(process_handle_.get(), &exit_code)); + EXPECT_TRUE(GetExitCodeProcess(process_handle_.get(), &exit_code)) + << ErrorMessage("GetExitCodeProcess"); process_handle_.reset(); return exit_code; } diff --git a/third_party/crashpad/crashpad/test/win/win_child_process.cc b/third_party/crashpad/crashpad/test/win/win_child_process.cc index 74687ee1f5b525..14299e6bc4ea83 100644 --- a/third_party/crashpad/crashpad/test/win/win_child_process.cc +++ b/third_party/crashpad/crashpad/test/win/win_child_process.cc @@ -182,17 +182,20 @@ std::unique_ptr WinChildProcess::Launch() { } // Build a command line for the child process that tells it only to run the - // current test, and to pass down the values of the pipe handles. + // current test, and to pass down the values of the pipe handles. Use + // --gtest_also_run_disabled_tests because the test may be DISABLED_, but if + // it managed to run in the parent, disabled tests must be running. const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); std::wstring command_line = - TestPaths::Executable().value() + L" " + - base::UTF8ToUTF16(base::StringPrintf("--gtest_filter=%s.%s %s=0x%x|0x%x", - test_info->test_case_name(), - test_info->name(), - kIsMultiprocessChild, - HandleToInt(write_for_child.get()), - HandleToInt(read_for_child.get()))); + TestPaths::Executable().value() + + base::UTF8ToUTF16(base::StringPrintf( + " --gtest_filter=%s.%s %s=0x%x|0x%x --gtest_also_run_disabled_tests", + test_info->test_case_name(), + test_info->name(), + kIsMultiprocessChild, + HandleToInt(write_for_child.get()), + HandleToInt(read_for_child.get()))); // Command-line buffer cannot be constant, per CreateProcess signature. handles_for_parent->process = LaunchCommandLine(&command_line[0]); diff --git a/third_party/crashpad/crashpad/util/file/directory_reader_test.cc b/third_party/crashpad/crashpad/util/file/directory_reader_test.cc index da4bfb737207c3..640b850f8dda29 100644 --- a/third_party/crashpad/crashpad/util/file/directory_reader_test.cc +++ b/third_party/crashpad/crashpad/util/file/directory_reader_test.cc @@ -21,6 +21,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "gtest/gtest.h" +#include "test/gtest_disabled.h" #include "test/scoped_temp_dir.h" #include "util/file/file_io.h" #include "util/file/filesystem.h" @@ -30,17 +31,6 @@ namespace crashpad { namespace test { namespace { -void ExpectFiles(const std::set& files, - const std::set& expected) { - EXPECT_EQ(files.size(), expected.size()); - - for (const auto& filename : expected) { - SCOPED_TRACE( - base::StringPrintf("Filename: %" PRFilePath, filename.value().c_str())); - EXPECT_NE(files.find(filename), files.end()); - } -} - TEST(DirectoryReader, BadPaths) { DirectoryReader reader; EXPECT_FALSE(reader.Open(base::FilePath())); @@ -50,17 +40,33 @@ TEST(DirectoryReader, BadPaths) { ASSERT_TRUE(CreateFile(file)); EXPECT_FALSE(reader.Open(file)); + EXPECT_FALSE( + reader.Open(temp_dir.path().Append(FILE_PATH_LITERAL("doesntexist")))); +} + +#if !defined(OS_FUCHSIA) + +TEST(DirectoryReader, BadPaths_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + ScopedTempDir temp_dir; + 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(CreateSymbolicLink(file, link)); + + DirectoryReader reader; EXPECT_FALSE(reader.Open(link)); ASSERT_TRUE(LoggingRemoveFile(file)); EXPECT_FALSE(reader.Open(link)); - - EXPECT_FALSE( - reader.Open(temp_dir.path().Append(FILE_PATH_LITERAL("doesntexist")))); } +#endif // !OS_FUCHSIA + TEST(DirectoryReader, EmptyDirectory) { ScopedTempDir temp_dir; DirectoryReader reader; @@ -70,7 +76,18 @@ TEST(DirectoryReader, EmptyDirectory) { EXPECT_EQ(reader.NextFile(&filename), DirectoryReader::Result::kNoMoreFiles); } -TEST(DirectoryReader, FilesAndDirectories) { +void ExpectFiles(const std::set& files, + const std::set& expected) { + EXPECT_EQ(files.size(), expected.size()); + + for (const auto& filename : expected) { + SCOPED_TRACE( + base::StringPrintf("Filename: %" PRFilePath, filename.value().c_str())); + EXPECT_NE(files.find(filename), files.end()); + } +} + +void TestFilesAndDirectories(bool symbolic_links) { ScopedTempDir temp_dir; std::set expected_files; @@ -78,17 +95,6 @@ TEST(DirectoryReader, FilesAndDirectories) { ASSERT_TRUE(CreateFile(temp_dir.path().Append(file))); EXPECT_TRUE(expected_files.insert(file).second); - base::FilePath link(FILE_PATH_LITERAL("link")); - ASSERT_TRUE(CreateSymbolicLink(temp_dir.path().Append(file), - temp_dir.path().Append(link))); - EXPECT_TRUE(expected_files.insert(link).second); - - base::FilePath dangling(FILE_PATH_LITERAL("dangling")); - ASSERT_TRUE( - CreateSymbolicLink(base::FilePath(FILE_PATH_LITERAL("not_a_file")), - temp_dir.path().Append(dangling))); - EXPECT_TRUE(expected_files.insert(dangling).second); - base::FilePath directory(FILE_PATH_LITERAL("directory")); ASSERT_TRUE(LoggingCreateDirectory(temp_dir.path().Append(directory), FilePermissions::kWorldReadable, @@ -99,6 +105,23 @@ TEST(DirectoryReader, FilesAndDirectories) { ASSERT_TRUE( CreateFile(temp_dir.path().Append(directory).Append(nested_file))); +#if !defined(OS_FUCHSIA) + + if (symbolic_links) { + base::FilePath link(FILE_PATH_LITERAL("link")); + ASSERT_TRUE(CreateSymbolicLink(temp_dir.path().Append(file), + temp_dir.path().Append(link))); + EXPECT_TRUE(expected_files.insert(link).second); + + base::FilePath dangling(FILE_PATH_LITERAL("dangling")); + ASSERT_TRUE( + CreateSymbolicLink(base::FilePath(FILE_PATH_LITERAL("not_a_file")), + temp_dir.path().Append(dangling))); + EXPECT_TRUE(expected_files.insert(dangling).second); + } + +#endif // !OS_FUCHSIA + std::set files; DirectoryReader reader; ASSERT_TRUE(reader.Open(temp_dir.path())); @@ -113,6 +136,22 @@ TEST(DirectoryReader, FilesAndDirectories) { ExpectFiles(files, expected_files); } +TEST(DirectoryReader, FilesAndDirectories) { + TestFilesAndDirectories(false); +} + +#if !defined(OS_FUCHSIA) + +TEST(DirectoryReader, FilesAndDirectories_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + TestFilesAndDirectories(true); +} + +#endif // !OS_FUCHSIA + } // namespace } // namespace test } // namespace crashpad diff --git a/third_party/crashpad/crashpad/util/file/filesystem_posix.cc b/third_party/crashpad/crashpad/util/file/filesystem_posix.cc index 802c3fc487a6ae..a35db1cda3161a 100644 --- a/third_party/crashpad/crashpad/util/file/filesystem_posix.cc +++ b/third_party/crashpad/crashpad/util/file/filesystem_posix.cc @@ -56,7 +56,7 @@ bool MoveFileOrDirectory(const base::FilePath& source, bool IsRegularFile(const base::FilePath& path) { struct stat st; if (lstat(path.value().c_str(), &st) != 0) { - PLOG_IF(ERROR, errno != ENOENT) << "stat " << path.value(); + PLOG(ERROR) << "stat " << path.value(); return false; } return S_ISREG(st.st_mode); @@ -69,11 +69,9 @@ bool IsDirectory(const base::FilePath& path, bool allow_symlinks) { PLOG(ERROR) << "stat " << path.value(); return false; } - } else { - if (lstat(path.value().c_str(), &st) != 0) { - PLOG(ERROR) << "lstat " << path.value(); - return false; - } + } else if (lstat(path.value().c_str(), &st) != 0) { + PLOG(ERROR) << "lstat " << path.value(); + return false; } return S_ISDIR(st.st_mode); } diff --git a/third_party/crashpad/crashpad/util/file/filesystem_test.cc b/third_party/crashpad/crashpad/util/file/filesystem_test.cc index 4fc856a1af54a1..8657e5b006965c 100644 --- a/third_party/crashpad/crashpad/util/file/filesystem_test.cc +++ b/third_party/crashpad/crashpad/util/file/filesystem_test.cc @@ -15,7 +15,9 @@ #include "util/file/filesystem.h" #include "base/logging.h" +#include "build/build_config.h" #include "gtest/gtest.h" +#include "test/gtest_disabled.h" #include "test/scoped_temp_dir.h" #include "util/file/filesystem_test_util.h" @@ -60,16 +62,16 @@ TEST(Filesystem, MoveFileOrDirectory) { base::FilePath file2(temp_dir.path().Append(FILE_PATH_LITERAL("file2"))); EXPECT_TRUE(MoveFileOrDirectory(file, file2)); EXPECT_TRUE(IsRegularFile(file2)); - EXPECT_FALSE(IsRegularFile(file)); + EXPECT_FALSE(PathExists(file)); EXPECT_FALSE(MoveFileOrDirectory(file, file2)); EXPECT_TRUE(IsRegularFile(file2)); - EXPECT_FALSE(IsRegularFile(file)); + EXPECT_FALSE(PathExists(file)); ASSERT_TRUE(CreateFile(file)); EXPECT_TRUE(MoveFileOrDirectory(file2, file)); EXPECT_TRUE(IsRegularFile(file)); - EXPECT_FALSE(IsRegularFile(file2)); + EXPECT_FALSE(PathExists(file2)); // directories base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir"))); @@ -100,6 +102,19 @@ TEST(Filesystem, MoveFileOrDirectory) { EXPECT_FALSE(MoveFileOrDirectory(dir2, file)); EXPECT_TRUE(IsDirectory(dir2, false)); EXPECT_TRUE(IsRegularFile(file)); +} + +#if !defined(OS_FUCHSIA) + +TEST(Filesystem, MoveFileOrDirectory_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + ScopedTempDir temp_dir; + + base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file"))); + ASSERT_TRUE(CreateFile(file)); // file links base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link"))); @@ -135,6 +150,10 @@ TEST(Filesystem, MoveFileOrDirectory) { EXPECT_FALSE(PathExists(link)); // directory links + base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir"))); + ASSERT_TRUE( + LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false)); + ASSERT_TRUE(CreateSymbolicLink(dir, link)); EXPECT_TRUE(MoveFileOrDirectory(link, link2)); @@ -148,6 +167,8 @@ TEST(Filesystem, MoveFileOrDirectory) { EXPECT_FALSE(PathExists(link2)); } +#endif // !OS_FUCHSIA + TEST(Filesystem, IsRegularFile) { EXPECT_FALSE(IsRegularFile(base::FilePath())); @@ -159,6 +180,18 @@ TEST(Filesystem, IsRegularFile) { ASSERT_TRUE(CreateFile(file)); EXPECT_TRUE(IsRegularFile(file)); +} + +#if !defined(OS_FUCHSIA) + +TEST(Filesystem, IsRegularFile_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + ScopedTempDir temp_dir; + 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(CreateSymbolicLink(file, link)); @@ -173,6 +206,8 @@ TEST(Filesystem, IsRegularFile) { EXPECT_FALSE(IsRegularFile(dir_link)); } +#endif // !OS_FUCHSIA + TEST(Filesystem, IsDirectory) { EXPECT_FALSE(IsDirectory(base::FilePath(), false)); EXPECT_FALSE(IsDirectory(base::FilePath(), true)); @@ -187,6 +222,18 @@ TEST(Filesystem, IsDirectory) { ASSERT_TRUE(CreateFile(file)); EXPECT_FALSE(IsDirectory(file, false)); EXPECT_FALSE(IsDirectory(file, true)); +} + +#if !defined(OS_FUCHSIA) + +TEST(Filesystem, IsDirectory_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + ScopedTempDir temp_dir; + 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(CreateSymbolicLink(file, link)); @@ -204,6 +251,8 @@ TEST(Filesystem, IsDirectory) { EXPECT_TRUE(IsDirectory(dir_link, true)); } +#endif // !OS_FUCHSIA + TEST(Filesystem, RemoveFile) { EXPECT_FALSE(LoggingRemoveFile(base::FilePath())); @@ -216,27 +265,45 @@ TEST(Filesystem, RemoveFile) { ASSERT_TRUE(CreateFile(file)); EXPECT_TRUE(IsRegularFile(file)); + 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)); + EXPECT_FALSE(LoggingRemoveFile(file)); +} + +#if !defined(OS_FUCHSIA) + +TEST(Filesystem, RemoveFile_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + ScopedTempDir temp_dir; + base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file"))); + ASSERT_TRUE(CreateFile(file)); + + base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir"))); + ASSERT_TRUE( + LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false)); + base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link"))); ASSERT_TRUE(CreateSymbolicLink(file, link)); EXPECT_TRUE(LoggingRemoveFile(link)); EXPECT_FALSE(PathExists(link)); EXPECT_TRUE(PathExists(file)); - base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir"))); - ASSERT_TRUE( - LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false)); - EXPECT_FALSE(LoggingRemoveFile(dir)); - ASSERT_TRUE(CreateSymbolicLink(dir, link)); EXPECT_TRUE(LoggingRemoveFile(link)); EXPECT_FALSE(PathExists(link)); EXPECT_TRUE(PathExists(dir)); - - EXPECT_TRUE(LoggingRemoveFile(file)); - EXPECT_FALSE(IsRegularFile(file)); - EXPECT_FALSE(LoggingRemoveFile(file)); } +#endif // !OS_FUCHSIA + TEST(Filesystem, RemoveDirectory) { EXPECT_FALSE(LoggingRemoveDirectory(base::FilePath())); @@ -253,6 +320,25 @@ TEST(Filesystem, RemoveDirectory) { EXPECT_FALSE(LoggingRemoveDirectory(file)); EXPECT_FALSE(LoggingRemoveDirectory(dir)); + ASSERT_TRUE(LoggingRemoveFile(file)); + EXPECT_TRUE(LoggingRemoveDirectory(dir)); +} + +#if !defined(OS_FUCHSIA) + +TEST(Filesystem, RemoveDirectory_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + ScopedTempDir temp_dir; + base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir"))); + ASSERT_TRUE( + LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false)); + + base::FilePath file(dir.Append(FILE_PATH_LITERAL("file"))); + EXPECT_FALSE(LoggingRemoveDirectory(file)); + base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link"))); ASSERT_TRUE(CreateSymbolicLink(file, link)); EXPECT_FALSE(LoggingRemoveDirectory(link)); @@ -261,11 +347,10 @@ TEST(Filesystem, RemoveDirectory) { ASSERT_TRUE(CreateSymbolicLink(dir, link)); EXPECT_FALSE(LoggingRemoveDirectory(link)); EXPECT_TRUE(LoggingRemoveFile(link)); - - ASSERT_TRUE(LoggingRemoveFile(file)); - EXPECT_TRUE(LoggingRemoveDirectory(dir)); } +#endif // !OS_FUCHSIA + } // namespace } // namespace test } // namespace crashpad diff --git a/third_party/crashpad/crashpad/util/file/filesystem_test_util.cc b/third_party/crashpad/crashpad/util/file/filesystem_test_util.cc index 49e13ecb456220..5d991ee780fcfd 100644 --- a/third_party/crashpad/crashpad/util/file/filesystem_test_util.cc +++ b/third_party/crashpad/crashpad/util/file/filesystem_test_util.cc @@ -20,9 +20,9 @@ #include "base/logging.h" #include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" #include "gtest/gtest.h" #include "test/errors.h" +#include "test/scoped_temp_dir.h" #include "util/file/file_io.h" #include "util/file/filesystem.h" @@ -37,6 +37,59 @@ namespace crashpad { namespace test { +namespace { + +#if defined(OS_WIN) + +// Detects the flags necessary to create symbolic links and stores them in +// |flags| if non-nullptr, and returns true on success. If symbolic links can’t +// be created, returns false. +bool SymbolicLinkFlags(DWORD* flags) { + static DWORD symbolic_link_flags = []() { + ScopedTempDir temp_dir_; + + base::FilePath target_path = temp_dir_.path().Append(L"target"); + base::FilePath symlink_path = temp_dir_.path().Append(L"symlink"); + if (::CreateSymbolicLink( + symlink_path.value().c_str(), target_path.value().c_str(), 0)) { + return 0; + } + + DWORD error = GetLastError(); + if (error == ERROR_PRIVILEGE_NOT_HELD) { + if (::CreateSymbolicLink(symlink_path.value().c_str(), + target_path.value().c_str(), + SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) { + return SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; + } + + // This may fail with ERROR_INVALID_PARAMETER if the OS is too old to + // understand SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, so keep + // ERROR_PRIVILEGE_NOT_HELD for |error|. + } + + // Don’t use ErrorMessage() here because the second CreateSymbolicLink() may + // have scrambled it. Use the saved |error| value instead. + EXPECT_EQ(error, static_cast(ERROR_PRIVILEGE_NOT_HELD)) + << "CreateSymbolicLink: " << logging::SystemErrorCodeToString(error); + return -1; + }(); + + if (symbolic_link_flags == static_cast(-1)) { + return false; + } + + if (flags) { + *flags = symbolic_link_flags; + } + + return true; +} + +#endif // OS_WIN + +} // namespace + bool CreateFile(const base::FilePath& file) { ScopedFileHandle fd(LoggingOpenFileForWrite( file, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)); @@ -44,6 +97,35 @@ bool CreateFile(const base::FilePath& file) { return fd.is_valid(); } +bool PathExists(const base::FilePath& path) { +#if defined(OS_POSIX) + struct stat st; + if (lstat(path.value().c_str(), &st) != 0) { + EXPECT_EQ(errno, ENOENT) << ErrnoMessage("lstat ") << path.value(); + return false; + } + return true; +#elif defined(OS_WIN) + if (GetFileAttributes(path.value().c_str()) == INVALID_FILE_ATTRIBUTES) { + EXPECT_EQ(GetLastError(), static_cast(ERROR_FILE_NOT_FOUND)) + << ErrorMessage("GetFileAttributes ") + << base::UTF16ToUTF8(path.value()); + return false; + } + return true; +#endif +} + +#if !defined(OS_FUCHSIA) + +bool CanCreateSymbolicLinks() { +#if defined(OS_POSIX) + return true; +#elif defined(OS_WIN) + return SymbolicLinkFlags(nullptr); +#endif // OS_POSIX +} + bool CreateSymbolicLink(const base::FilePath& target_path, const base::FilePath& symlink_path) { #if defined(OS_POSIX) @@ -53,35 +135,24 @@ bool CreateSymbolicLink(const base::FilePath& target_path, PLOG(ERROR) << "symlink"; return false; } + return true; #elif defined(OS_WIN) + DWORD symbolic_link_flags = 0; + SymbolicLinkFlags(&symbolic_link_flags); if (!::CreateSymbolicLink( symlink_path.value().c_str(), target_path.value().c_str(), - IsDirectory(target_path, true) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0)) { + symbolic_link_flags | + (IsDirectory(target_path, true) ? SYMBOLIC_LINK_FLAG_DIRECTORY + : 0))) { PLOG(ERROR) << "CreateSymbolicLink"; return false; } -#endif // OS_POSIX return true; +#endif // OS_POSIX } -bool PathExists(const base::FilePath& path) { -#if defined(OS_POSIX) - struct stat st; - if (lstat(path.value().c_str(), &st) != 0) { - EXPECT_EQ(errno, ENOENT) << ErrnoMessage("lstat ") << path.value(); - return false; - } -#elif defined(OS_WIN) - if (GetFileAttributes(path.value().c_str()) == INVALID_FILE_ATTRIBUTES) { - EXPECT_EQ(GetLastError(), ERROR_FILE_NOT_FOUND) - << ErrorMessage("GetFileAttributes ") - << base::UTF16ToUTF8(path.value()); - return false; - } -#endif - return true; -} +#endif // !OS_FUCHSIA } // namespace test } // namespace crashpad diff --git a/third_party/crashpad/crashpad/util/file/filesystem_test_util.h b/third_party/crashpad/crashpad/util/file/filesystem_test_util.h index 393bc8b59f4ec3..b74653f1b921dd 100644 --- a/third_party/crashpad/crashpad/util/file/filesystem_test_util.h +++ b/third_party/crashpad/crashpad/util/file/filesystem_test_util.h @@ -17,15 +17,41 @@ #include "base/files/file_path.h" +#include "build/build_config.h" + namespace crashpad { namespace test { bool CreateFile(const base::FilePath& file); +bool PathExists(const base::FilePath& path); + +#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 +// that symbolic links might be available on Fuchsia. + +//! \brief Determines whether it should be possible to create symbolic links. +//! +//! It is always possible to create symbolic links on POSIX. +//! +//! On Windows, it is only possible to create symbolic links when running as an +//! administrator, or as a non-administrator when running Windows 10 build 15063 +//! (1703, Creators Update) or later, provided that developer mode is enabled +//! and `SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE` is used. This function +//! tests the creation of a symbolic link and returns true on success, and false +//! on failure. If the symbolic link could not be created for a reason other +//! than the expected lack of privilege, a message is logged. +//! +//! Additional background: Symlinks +//! in Windows 10! +bool CanCreateSymbolicLinks(); + bool CreateSymbolicLink(const base::FilePath& target_path, const base::FilePath& symlink_path); -bool PathExists(const base::FilePath& path); +#endif // !OS_FUCHSIA || DOXYGEN } // namespace test } // namespace crashpad diff --git a/third_party/crashpad/crashpad/util/file/filesystem_win.cc b/third_party/crashpad/crashpad/util/file/filesystem_win.cc index 5a1242ec0b1ed4..b874ed8c0ae2d0 100644 --- a/third_party/crashpad/crashpad/util/file/filesystem_win.cc +++ b/third_party/crashpad/crashpad/util/file/filesystem_win.cc @@ -82,8 +82,7 @@ bool MoveFileOrDirectory(const base::FilePath& source, bool IsRegularFile(const base::FilePath& path) { DWORD fileattr = GetFileAttributes(path.value().c_str()); if (fileattr == INVALID_FILE_ATTRIBUTES) { - PLOG_IF(ERROR, GetLastError() != ERROR_FILE_NOT_FOUND) - << "GetFileAttributes " << base::UTF16ToUTF8(path.value()); + PLOG(ERROR) << "GetFileAttributes " << base::UTF16ToUTF8(path.value()); return false; } if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0 || diff --git a/third_party/crashpad/crashpad/util/misc/from_pointer_cast.h b/third_party/crashpad/crashpad/util/misc/from_pointer_cast.h index f1b37349600ca2..89a99414405723 100644 --- a/third_party/crashpad/crashpad/util/misc/from_pointer_cast.h +++ b/third_party/crashpad/crashpad/util/misc/from_pointer_cast.h @@ -21,6 +21,7 @@ #include #include "base/numerics/safe_conversions.h" +#include "build/build_config.h" namespace crashpad { @@ -56,6 +57,20 @@ FromPointerCast(From) { return To(); } +// FromPointerCast<>() with a function pointer “From” type raises +// -Wnoexcept-type in GCC 7.2 if the function pointer type has a throw() or +// noexcept specification. This is the case for all standard C library functions +// provided by glibc. Various tests make use of FromPointerCast<>() with +// pointers to standard C library functions. +// +// Clang has the similar -Wc++1z-compat-mangling, which is not triggered by this +// pattern. +#if defined(COMPILER_GCC) && !defined(__clang__) && \ + (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnoexcept-type" +#endif + // Cast a pointer to any other pointer type. template typename std::enable_if::value && @@ -88,6 +103,11 @@ FromPointerCast(From from) { return base::checked_cast(intermediate); } +#if defined(COMPILER_GCC) && !defined(__clang__) && \ + (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2)) +#pragma GCC diagnostic pop +#endif + #endif // DOXYGEN } // namespace crashpad diff --git a/third_party/crashpad/crashpad/util/process/process_memory_linux.h b/third_party/crashpad/crashpad/util/process/process_memory_linux.h index 95e93f5a69bfa5..e03e251f0ffa62 100644 --- a/third_party/crashpad/crashpad/util/process/process_memory_linux.h +++ b/third_party/crashpad/crashpad/util/process/process_memory_linux.h @@ -27,7 +27,7 @@ namespace crashpad { //! \brief Accesses the memory of another Linux process. -class ProcessMemoryLinux : public ProcessMemory { +class ProcessMemoryLinux final : public ProcessMemory { public: ProcessMemoryLinux(); ~ProcessMemoryLinux(); diff --git a/third_party/crashpad/crashpad/util/win/exception_handler_server_test.cc b/third_party/crashpad/crashpad/util/win/exception_handler_server_test.cc index 6f9ffc3a6091eb..6f8805164e5a5d 100644 --- a/third_party/crashpad/crashpad/util/win/exception_handler_server_test.cc +++ b/third_party/crashpad/crashpad/util/win/exception_handler_server_test.cc @@ -180,13 +180,7 @@ class TestClient final : public WinChildProcess { DISALLOW_COPY_AND_ASSIGN(TestClient); }; -// https://crashpad.chromium.org/bug/205 -#if defined(CRASHPAD_IN_CHROMIUM) -#define MAYBE_MultipleConnections DISABLED_MultipleConnections -#else -#define MAYBE_MultipleConnections MultipleConnections -#endif -TEST_F(ExceptionHandlerServerTest, MAYBE_MultipleConnections) { +TEST_F(ExceptionHandlerServerTest, MultipleConnections) { WinChildProcess::EntryPoint(); std::unique_ptr handles_1 = diff --git a/third_party/crashpad/crashpad/util/win/process_info_test.cc b/third_party/crashpad/crashpad/util/win/process_info_test.cc index 590ef1512c84dc..051479d13bfe21 100644 --- a/third_party/crashpad/crashpad/util/win/process_info_test.cc +++ b/third_party/crashpad/crashpad/util/win/process_info_test.cc @@ -26,6 +26,7 @@ #include "build/build_config.h" #include "gtest/gtest.h" #include "test/errors.h" +#include "test/gtest_disabled.h" #include "test/scoped_temp_dir.h" #include "test/test_paths.h" #include "test/win/child_launcher.h" @@ -131,7 +132,7 @@ TEST(ProcessInfo, Self) { FromPointerCast(_ReturnAddress())); } -void TestOtherProcess(const base::string16& directory_modification) { +void TestOtherProcess(const base::FilePath& directory) { ProcessInfo process_info; UUID done_uuid; @@ -141,12 +142,12 @@ void TestOtherProcess(const base::string16& directory_modification) { CreateEvent(nullptr, true, false, done_uuid.ToString16().c_str())); ASSERT_TRUE(done.get()) << ErrorMessage("CreateEvent"); - base::FilePath test_executable = TestPaths::Executable(); - std::wstring child_test_executable = - test_executable.DirName() - .Append(directory_modification) - .Append(test_executable.BaseName().RemoveFinalExtension().value() + + directory + .Append(TestPaths::Executable() + .BaseName() + .RemoveFinalExtension() + .value() + L"_process_info_test_child.exe") .value(); @@ -189,19 +190,18 @@ void TestOtherProcess(const base::string16& directory_modification) { VerifyAddressInInCodePage(process_info, code_address); } -// https://crashpad.chromium.org/bug/204 -TEST(ProcessInfo, DISABLED_OtherProcess) { - TestOtherProcess(FILE_PATH_LITERAL(".")); +TEST(ProcessInfo, OtherProcess) { + TestOtherProcess(TestPaths::Executable().DirName()); } #if defined(ARCH_CPU_64_BITS) -// https://crashpad.chromium.org/bug/203 -TEST(ProcessInfo, DISABLED_OtherProcessWOW64) { -#ifndef NDEBUG - TestOtherProcess(FILE_PATH_LITERAL("..\\..\\out\\Debug")); -#else - TestOtherProcess(FILE_PATH_LITERAL("..\\..\\out\\Release")); -#endif +TEST(ProcessInfo, OtherProcessWOW64) { + base::FilePath output_32_bit_directory = TestPaths::Output32BitDirectory(); + if (output_32_bit_directory.empty()) { + DISABLED_TEST(); + } + + TestOtherProcess(output_32_bit_directory); } #endif // ARCH_CPU_64_BITS diff --git a/third_party/crashpad/crashpad/util/win/scoped_process_suspend_test.cc b/third_party/crashpad/crashpad/util/win/scoped_process_suspend_test.cc index 7dfcdf297e982e..2d0f5a0b90ddd8 100644 --- a/third_party/crashpad/crashpad/util/win/scoped_process_suspend_test.cc +++ b/third_party/crashpad/crashpad/util/win/scoped_process_suspend_test.cc @@ -89,13 +89,7 @@ class ScopedProcessSuspendTest final : public WinChildProcess { DISALLOW_COPY_AND_ASSIGN(ScopedProcessSuspendTest); }; -// https://crashpad.chromium.org/bug/205 -#if defined(CRASHPAD_IN_CHROMIUM) -#define MAYBE_ScopedProcessSuspend DISABLED_ScopedProcessSuspend -#else -#define MAYBE_ScopedProcessSuspend ScopedProcessSuspend -#endif -TEST(ScopedProcessSuspend, MAYBE_ScopedProcessSuspend) { +TEST(ScopedProcessSuspend, ScopedProcessSuspend) { WinChildProcess::EntryPoint(); std::unique_ptr handles = WinChildProcess::Launch();