diff --git a/DEPS b/DEPS index 742430d5..f4136874 100644 --- a/DEPS +++ b/DEPS @@ -38,7 +38,7 @@ deps = { 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - '723e840a2f100a525f7feaad2e93df31d701780a', + 'ee67585e3115982282b86e210939ead1791e696d', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/client/crash_report_database_mac.mm b/client/crash_report_database_mac.mm index 3cc2d7fe..7a9154b8 100644 --- a/client/crash_report_database_mac.mm +++ b/client/crash_report_database_mac.mm @@ -42,29 +42,30 @@ namespace crashpad { namespace { -const char kWriteDirectory[] = "new"; -const char kUploadPendingDirectory[] = "pending"; -const char kCompletedDirectory[] = "completed"; +constexpr char kWriteDirectory[] = "new"; +constexpr char kUploadPendingDirectory[] = "pending"; +constexpr char kCompletedDirectory[] = "completed"; -const char kSettings[] = "settings.dat"; +constexpr char kSettings[] = "settings.dat"; -const char* const kReportDirectories[] = { +constexpr const char* kReportDirectories[] = { kWriteDirectory, kUploadPendingDirectory, kCompletedDirectory, }; -const char kCrashReportFileExtension[] = "dmp"; +constexpr char kCrashReportFileExtension[] = "dmp"; -const char kXattrUUID[] = "uuid"; -const char kXattrCollectorID[] = "id"; -const char kXattrCreationTime[] = "creation_time"; -const char kXattrIsUploaded[] = "uploaded"; -const char kXattrLastUploadTime[] = "last_upload_time"; -const char kXattrUploadAttemptCount[] = "upload_count"; -const char kXattrIsUploadExplicitlyRequested[] = "upload_explicitly_requested"; +constexpr char kXattrUUID[] = "uuid"; +constexpr char kXattrCollectorID[] = "id"; +constexpr char kXattrCreationTime[] = "creation_time"; +constexpr char kXattrIsUploaded[] = "uploaded"; +constexpr char kXattrLastUploadTime[] = "last_upload_time"; +constexpr char kXattrUploadAttemptCount[] = "upload_count"; +constexpr char kXattrIsUploadExplicitlyRequested[] = + "upload_explicitly_requested"; -const char kXattrDatabaseInitialized[] = "initialized"; +constexpr char kXattrDatabaseInitialized[] = "initialized"; // Ensures that the node at |path| is a directory. If the |path| refers to a // file, rather than a directory, returns false. Otherwise, returns true, diff --git a/client/crash_report_database_test.cc b/client/crash_report_database_test.cc index da44ebfb..c4266961 100644 --- a/client/crash_report_database_test.cc +++ b/client/crash_report_database_test.cc @@ -51,7 +51,7 @@ class CrashReportDatabaseTest : public testing::Test { CrashReportDatabase::NewReport* new_report = nullptr; ASSERT_EQ(db_->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); - const char kTest[] = "test"; + static constexpr char kTest[] = "test"; ASSERT_TRUE(LoggingWriteFile(new_report->handle, kTest, sizeof(kTest))); UUID uuid; diff --git a/client/crash_report_database_win.cc b/client/crash_report_database_win.cc index a97b1cf0..538eff5f 100644 --- a/client/crash_report_database_win.cc +++ b/client/crash_report_database_win.cc @@ -36,15 +36,15 @@ namespace crashpad { namespace { -const wchar_t kReportsDirectory[] = L"reports"; -const wchar_t kMetadataFileName[] = L"metadata"; +constexpr wchar_t kReportsDirectory[] = L"reports"; +constexpr wchar_t kMetadataFileName[] = L"metadata"; -const wchar_t kSettings[] = L"settings.dat"; +constexpr wchar_t kSettings[] = L"settings.dat"; -const wchar_t kCrashReportFileExtension[] = L"dmp"; +constexpr wchar_t kCrashReportFileExtension[] = L"dmp"; -const uint32_t kMetadataFileHeaderMagic = 'CPAD'; -const uint32_t kMetadataFileVersion = 1; +constexpr uint32_t kMetadataFileHeaderMagic = 'CPAD'; +constexpr uint32_t kMetadataFileVersion = 1; using OperationStatus = CrashReportDatabase::OperationStatus; diff --git a/client/crashpad_client_mac.cc b/client/crashpad_client_mac.cc index 978024e5..a9e4aca4 100644 --- a/client/crashpad_client_mac.cc +++ b/client/crashpad_client_mac.cc @@ -284,8 +284,8 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { if (restart) { // If the handler was ever started before, don’t restart it too quickly. - const uint64_t kNanosecondsPerSecond = 1E9; - const uint64_t kMinimumStartInterval = 1 * kNanosecondsPerSecond; + constexpr uint64_t kNanosecondsPerSecond = 1E9; + constexpr uint64_t kMinimumStartInterval = 1 * kNanosecondsPerSecond; const uint64_t earliest_next_start_time = handler_restarter->last_start_time_ + kMinimumStartInterval; diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc index 487b39ab..f9d8e129 100644 --- a/client/crashpad_client_win.cc +++ b/client/crashpad_client_win.cc @@ -164,7 +164,7 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { SetEvent(g_signal_exception); // Time to wait for the handler to create a dump. - const DWORD kMillisecondsUntilTerminate = 60 * 1000; + constexpr DWORD kMillisecondsUntilTerminate = 60 * 1000; // Sleep for a while to allow it to process us. Eventually, we terminate // ourselves in case the crash server is gone, so that we don't leave zombies @@ -753,7 +753,7 @@ void CrashpadClient::DumpWithoutCrash(const CONTEXT& context) { // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363082.aspx have // some of the top nibble set, so we make sure to pick a value that doesn't, // so as to be unlikely to conflict. - const uint32_t kSimulatedExceptionCode = 0x517a7ed; + constexpr uint32_t kSimulatedExceptionCode = 0x517a7ed; EXCEPTION_RECORD record = {}; record.ExceptionCode = kSimulatedExceptionCode; #if defined(ARCH_CPU_64_BITS) @@ -829,7 +829,7 @@ bool CrashpadClient::DumpAndCrashTargetProcess(HANDLE process, } } - const size_t kInjectBufferSize = 4 * 1024; + constexpr size_t kInjectBufferSize = 4 * 1024; WinVMAddress inject_memory = FromPointerCast(VirtualAllocEx(process, nullptr, @@ -1001,7 +1001,10 @@ bool CrashpadClient::DumpAndCrashTargetProcess(HANDLE process, // letting this cause an exception, even when the target is stuck in the // loader lock. HANDLE injected_thread; - const size_t kStackSize = 0x4000; // This is what DebugBreakProcess() uses. + + // This is what DebugBreakProcess() uses. + constexpr size_t kStackSize = 0x4000; + NTSTATUS status = NtCreateThreadEx(&injected_thread, STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL, nullptr, diff --git a/client/crashpad_info.cc b/client/crashpad_info.cc index b7ba5490..58d30ce8 100644 --- a/client/crashpad_info.cc +++ b/client/crashpad_info.cc @@ -28,7 +28,7 @@ namespace { -static const uint32_t kCrashpadInfoVersion = 1; +constexpr uint32_t kCrashpadInfoVersion = 1; } // namespace diff --git a/client/prune_crash_reports_test.cc b/client/prune_crash_reports_test.cc index 130ea72a..54d6941e 100644 --- a/client/prune_crash_reports_test.cc +++ b/client/prune_crash_reports_test.cc @@ -147,7 +147,7 @@ class StaticCondition final : public PruneCondition { }; TEST(PruneCrashReports, BinaryCondition) { - const struct { + static constexpr struct { const char* name; BinaryPruneCondition::Operator op; bool lhs_value; diff --git a/client/settings_test.cc b/client/settings_test.cc index 866e58d7..ca961a23 100644 --- a/client/settings_test.cc +++ b/client/settings_test.cc @@ -41,7 +41,7 @@ class SettingsTest : public testing::Test { FilePermissions::kWorldReadable)); ASSERT_TRUE(handle.is_valid()); - const char kBuf[] = "test bad file"; + static constexpr char kBuf[] = "test bad file"; ASSERT_TRUE(LoggingWriteFile(handle.get(), kBuf, sizeof(kBuf))); handle.reset(); } diff --git a/client/simple_string_dictionary_test.cc b/client/simple_string_dictionary_test.cc index 6d827b8f..6944934f 100644 --- a/client/simple_string_dictionary_test.cc +++ b/client/simple_string_dictionary_test.cc @@ -121,8 +121,8 @@ TEST(SimpleStringDictionary, Iterator) { char key[SimpleStringDictionary::key_size]; char value[SimpleStringDictionary::value_size]; - const int kDictionaryCapacity = SimpleStringDictionary::num_entries; - const int kPartitionIndex = kDictionaryCapacity - 5; + constexpr int kDictionaryCapacity = SimpleStringDictionary::num_entries; + constexpr int kPartitionIndex = kDictionaryCapacity - 5; // We assume at least this size in the tests below ASSERT_GE(kDictionaryCapacity, 64); diff --git a/client/simulate_crash_mac.cc b/client/simulate_crash_mac.cc index 27864388..1e2d8e42 100644 --- a/client/simulate_crash_mac.cc +++ b/client/simulate_crash_mac.cc @@ -196,7 +196,7 @@ void SimulateCrash(const NativeCPUContext& cpu_context) { // Look up the handler for EXC_CRASH exceptions in the same way that the // kernel would: try a thread handler, then a task handler, and finally a host // handler. 10.9.5 xnu-2422.115.4/osfmk/kern/exception.c exception_triage(). - const ExceptionPorts::TargetType kTargetTypes[] = { + static constexpr ExceptionPorts::TargetType kTargetTypes[] = { ExceptionPorts::kTargetTypeThread, ExceptionPorts::kTargetTypeTask, diff --git a/client/simulate_crash_mac_test.cc b/client/simulate_crash_mac_test.cc index c1953524..1d63ff65 100644 --- a/client/simulate_crash_mac_test.cc +++ b/client/simulate_crash_mac_test.cc @@ -307,14 +307,14 @@ class TestSimulateCrashMac final : public MachMultiprocess, }; TEST(SimulateCrash, SimulateCrash) { - const TestSimulateCrashMac::ExceptionPortsTarget kTargets[] = { + static constexpr TestSimulateCrashMac::ExceptionPortsTarget kTargets[] = { TestSimulateCrashMac::kExceptionPortsTargetNone, TestSimulateCrashMac::kExceptionPortsTargetTask, TestSimulateCrashMac::kExceptionPortsTargetThread, TestSimulateCrashMac::kExceptionPortsTargetBoth, }; - const exception_behavior_t kBehaviors[] = { + static constexpr exception_behavior_t kBehaviors[] = { EXCEPTION_DEFAULT, EXCEPTION_STATE, EXCEPTION_STATE_IDENTITY, @@ -323,7 +323,7 @@ TEST(SimulateCrash, SimulateCrash) { EXCEPTION_STATE_IDENTITY | kMachExceptionCodes, }; - const thread_state_flavor_t kFlavors[] = { + static constexpr thread_state_flavor_t kFlavors[] = { #if defined(ARCH_CPU_X86_FAMILY) x86_THREAD_STATE, x86_FLOAT_STATE, diff --git a/compat/android/elf.h b/compat/android/elf.h new file mode 100644 index 00000000..79e44fca --- /dev/null +++ b/compat/android/elf.h @@ -0,0 +1,38 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_COMPAT_ANDROID_ELF_H_ +#define CRASHPAD_COMPAT_ANDROID_ELF_H_ + +#include_next + +#if !defined(ELF32_ST_VISIBILITY) +#define ELF32_ST_VISIBILITY(other) ((other) & 0x3) +#endif + +#if !defined(ELF64_ST_VISIBILITY) +#define ELF64_ST_VISIBILITY(other) ELF32_ST_VISIBILITY(other) +#endif + +// Android 5.0.0 (API 21) NDK + +#if !defined(STT_COMMON) +#define STT_COMMON 5 +#endif + +#if !defined(STT_TLS) +#define STT_TLS 6 +#endif + +#endif // CRASHPAD_COMPAT_ANDROID_ELF_H_ diff --git a/compat/android/sched.h b/compat/android/sched.h new file mode 100644 index 00000000..c4a027ae --- /dev/null +++ b/compat/android/sched.h @@ -0,0 +1,30 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_COMPAT_ANDROID_SCHED_H_ +#define CRASHPAD_COMPAT_ANDROID_SCHED_H_ + +#include_next + +// Android 5.0.0 (API 21) NDK + +#if !defined(SCHED_BATCH) +#define SCHED_BATCH 3 +#endif + +#if !defined(SCHED_IDLE) +#define SCHED_IDLE 5 +#endif + +#endif // CRASHPAD_COMPAT_ANDROID_SCHED_H_ diff --git a/util/net/http_headers.cc b/compat/android/sys/syscall.h similarity index 63% rename from util/net/http_headers.cc rename to compat/android/sys/syscall.h index 37d84c67..4d1253dc 100644 --- a/util/net/http_headers.cc +++ b/compat/android/sys/syscall.h @@ -1,4 +1,4 @@ -// Copyright 2014 The Crashpad Authors. All rights reserved. +// 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. @@ -12,12 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "util/net/http_headers.h" +#ifndef CRASHPAD_COMPAT_ANDROID_SYS_SYSCALL_H_ +#define CRASHPAD_COMPAT_ANDROID_SYS_SYSCALL_H_ -namespace crashpad { +#include_next -const char kContentType[] = "Content-Type"; -const char kContentLength[] = "Content-Length"; -const char kContentEncoding[] = "Content-Encoding"; +// Android 5.0.0 (API 21) NDK +#if !defined(SYS_gettid) +#define SYS_gettid __NR_gettid +#endif -} // namespace crashpad +#endif // CRASHPAD_COMPAT_ANDROID_SYS_SYSCALL_H_ diff --git a/compat/compat.gyp b/compat/compat.gyp index e2b2df6d..e14bb498 100644 --- a/compat/compat.gyp +++ b/compat/compat.gyp @@ -94,6 +94,16 @@ ], }, }], + ['OS=="linux"', { + 'include_dirs': [ + 'linux', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + 'linux', + ], + }, + }], ], }, ], diff --git a/compat/linux/sys/ptrace.h b/compat/linux/sys/ptrace.h new file mode 100644 index 00000000..57c5c5ba --- /dev/null +++ b/compat/linux/sys/ptrace.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_LINUX_SYS_PTRACE_H_ +#define CRASHPAD_COMPAT_LINUX_SYS_PTRACE_H_ + +#include_next + +#include + +#if defined(__GLIBC__) && defined(__x86_64__) +static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = + static_cast<__ptrace_request>(25); +#endif // __GLIBC__ && __x86_64__ + +#endif // CRASHPAD_COMPAT_LINUX_SYS_PTRACE_H_ diff --git a/doc/developing.md b/doc/developing.md index 6a4c4fd1..6b2a9e4e 100644 --- a/doc/developing.md +++ b/doc/developing.md @@ -119,7 +119,7 @@ Kit)](https://developer.android.com/ndk/) runs on. If it’s not already present on your system, [download the NDK package for your system](https://developer.android.com/ndk/downloads/) and expand it to a suitable location. These instructions assume that it’s been expanded to -`~/android-ndk-r14`. +`~/android-ndk-r15b`. To build Crashpad, portions of the NDK must be reassembled into a [standalone toolchain](https://developer.android.com/ndk/guides/standalone_toolchain.html). @@ -133,8 +133,8 @@ desired. To build a standalone toolchain targeting 64-bit ARM and API level 21 ``` $ cd ~ -$ python android-ndk-r14/build/tools/make_standalone_toolchain.py \ - --arch=arm64 --api=21 --install-dir=android-ndk-r14_arm64_api21 +$ python android-ndk-r15b/build/tools/make_standalone_toolchain.py \ + --arch=arm64 --api=21 --install-dir=android-ndk-r15b_arm64_api21 ``` Note that Chrome uses Android API level 21 for 64-bit platforms and 16 for @@ -152,7 +152,7 @@ operation. ``` $ cd ~/crashpad/crashpad $ python build/gyp_crashpad_android.py \ - --ndk ~/android-ndk-r14_arm64_api21 \ + --ndk ~/android-ndk-r15b_arm64_api21 \ --generator-output out/android_arm64_api21 ``` diff --git a/handler/crash_report_upload_thread.cc b/handler/crash_report_upload_thread.cc index d77cdbad..19d25ab5 100644 --- a/handler/crash_report_upload_thread.cc +++ b/handler/crash_report_upload_thread.cc @@ -268,7 +268,7 @@ void CrashReportUploadThread::ProcessPendingReport( // If the most recent upload attempt occurred within the past hour, // don’t attempt to upload the new report. If it happened longer ago, // attempt to upload the report. - const int kUploadAttemptIntervalSeconds = 60 * 60; // 1 hour + constexpr int kUploadAttemptIntervalSeconds = 60 * 60; // 1 hour if (now - last_upload_attempt_time < kUploadAttemptIntervalSeconds) { database_->SkipReportUpload( report.uuid, Metrics::CrashSkippedReason::kUploadThrottled); @@ -280,7 +280,7 @@ void CrashReportUploadThread::ProcessPendingReport( // upload attempt time is bogus, and attempt to upload the report. If // the most recent upload time is in the future but within one day, // accept it and don’t attempt to upload the report. - const int kBackwardsClockTolerance = 60 * 60 * 24; // 1 day + constexpr int kBackwardsClockTolerance = 60 * 60 * 24; // 1 day if (last_upload_attempt_time - now < kBackwardsClockTolerance) { database_->SkipReportUpload( report.uuid, Metrics::CrashSkippedReason::kUnexpectedTime); @@ -361,7 +361,7 @@ CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport( HTTPMultipartBuilder http_multipart_builder; http_multipart_builder.SetGzipEnabled(upload_gzip_); - const char kMinidumpKey[] = "upload_file_minidump"; + static constexpr char kMinidumpKey[] = "upload_file_minidump"; for (const auto& kv : parameters) { if (kv.first == kMinidumpKey) { diff --git a/handler/crashpad_handler_test.cc b/handler/crashpad_handler_test.cc index 7237e8c8..125157ff 100644 --- a/handler/crashpad_handler_test.cc +++ b/handler/crashpad_handler_test.cc @@ -115,7 +115,7 @@ void CrashWithExtendedHandler::ValidateGeneratedDump() { ASSERT_TRUE(reader.ReadExactly(data.data(), data.size())); - static const char kExpectedData[] = "Injected extension stream!"; + static constexpr char kExpectedData[] = "Injected extension stream!"; EXPECT_EQ(memcmp(kExpectedData, data.data(), sizeof(kExpectedData)), 0); } } diff --git a/handler/crashpad_handler_test_extended_handler.cc b/handler/crashpad_handler_test_extended_handler.cc index ede0f6e7..e2134e78 100644 --- a/handler/crashpad_handler_test_extended_handler.cc +++ b/handler/crashpad_handler_test_extended_handler.cc @@ -39,7 +39,7 @@ class TestUserStreamDataSource : public crashpad::UserStreamDataSource { std::unique_ptr TestUserStreamDataSource::ProduceStreamData( crashpad::ProcessSnapshot* process_snapshot) { - static const char kTestData[] = "Injected extension stream!"; + static constexpr char kTestData[] = "Injected extension stream!"; return base::WrapUnique(new crashpad::test::BufferExtensionStreamDataSource( 0xCAFEBABE, kTestData, sizeof(kTestData))); diff --git a/handler/handler_main.cc b/handler/handler_main.cc index e6d05e22..abc22bf3 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -436,7 +436,7 @@ int HandlerMain(int argc, kOptionVersion = -3, }; - const option long_options[] = { + static constexpr option long_options[] = { {"annotation", required_argument, nullptr, kOptionAnnotation}, {"database", required_argument, nullptr, kOptionDatabase}, #if defined(OS_MACOSX) @@ -714,8 +714,8 @@ int HandlerMain(int argc, base::GlobalHistogramAllocator* histogram_allocator = nullptr; if (!options.metrics_dir.empty()) { - static const char kMetricsName[] = "CrashpadMetrics"; - const size_t kMetricsFileSize = 1 << 20; + static constexpr char kMetricsName[] = "CrashpadMetrics"; + constexpr size_t kMetricsFileSize = 1 << 20; if (base::GlobalHistogramAllocator::CreateWithActiveFileInDir( options.metrics_dir, kMetricsFileSize, 0, kMetricsName)) { histogram_allocator = base::GlobalHistogramAllocator::Get(); diff --git a/handler/mac/file_limit_annotation.cc b/handler/mac/file_limit_annotation.cc index 359ed8e0..bdd8fb1e 100644 --- a/handler/mac/file_limit_annotation.cc +++ b/handler/mac/file_limit_annotation.cc @@ -14,7 +14,9 @@ #include "handler/mac/file_limit_annotation.h" +#include #include +#include #include #include #include @@ -26,6 +28,9 @@ #include "base/strings/stringprintf.h" #include "client/crashpad_info.h" #include "client/simple_string_dictionary.h" +#include "util/posix/scoped_dir.h" + +namespace crashpad { namespace { @@ -44,6 +49,38 @@ std::string FormatFromSysctl(int rv, const int* value, const size_t* size) { return base::StringPrintf("%d", *value); } +// Counts the number of open file descriptors in the process and returns it as a +// string. This /dev/fd and the value returned will include the open file +// descriptor for that directory. If opendir() fails, the returned string will +// be "E" followed by the error number. If readdir_r() fails, it will be "R" +// followed by the error number. +std::string CountOpenFileDescriptors() { + DIR* dir = opendir("/dev/fd"); + if (!dir) { + return base::StringPrintf("E%d", errno); + } + + ScopedDIR dir_owner(dir); + + dirent entry; + dirent* result; + int count = 0; + while ((errno = readdir_r(dir, &entry, &result)) == 0 && result != nullptr) { + const char* entry_name = &(*result->d_name); + if (strcmp(entry_name, ".") == 0 || strcmp(entry_name, "..") == 0) { + continue; + } + + ++count; + } + + if (errno != 0) { + return base::StringPrintf("R%d", errno); + } + + return base::StringPrintf("%d", count); +} + // Returns a string for |limit|, or "inf" if |limit| is RLIM_INFINITY. std::string StringForRLim(rlim_t limit) { if (limit == RLIM_INFINITY) { @@ -55,8 +92,6 @@ std::string StringForRLim(rlim_t limit) { } // namespace -namespace crashpad { - void RecordFileLimitAnnotation() { CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo(); SimpleStringDictionary* simple_annotations = @@ -76,6 +111,8 @@ void RecordFileLimitAnnotation() { std::string max_files = FormatFromSysctl( sysctl(mib, arraysize(mib), &value, &size, nullptr, 0), &value, &size); + std::string open_files = CountOpenFileDescriptors(); + rlimit limit; std::string nofile; if (getrlimit(RLIMIT_NOFILE, &limit) != 0) { @@ -85,8 +122,11 @@ void RecordFileLimitAnnotation() { StringForRLim(limit.rlim_cur) + "," + StringForRLim(limit.rlim_max); } - std::string annotation = base::StringPrintf( - "%s,%s,%s", num_files.c_str(), max_files.c_str(), nofile.c_str()); + std::string annotation = base::StringPrintf("%s,%s,%s,%s", + num_files.c_str(), + max_files.c_str(), + open_files.c_str(), + nofile.c_str()); simple_annotations->SetKeyValue("file-limits", annotation.c_str()); } diff --git a/handler/win/crashy_test_program.cc b/handler/win/crashy_test_program.cc index 58afa47f..a5532da8 100644 --- a/handler/win/crashy_test_program.cc +++ b/handler/win/crashy_test_program.cc @@ -63,20 +63,20 @@ void AllocateMemoryOfVariousProtections() { const size_t kPageSize = system_info.dwPageSize; - const uint32_t kPageTypes[] = { - PAGE_NOACCESS, - PAGE_READONLY, - PAGE_READWRITE, - PAGE_EXECUTE, - PAGE_EXECUTE_READ, - PAGE_EXECUTE_READWRITE, + static constexpr uint32_t kPageTypes[] = { + PAGE_NOACCESS, + PAGE_READONLY, + PAGE_READWRITE, + PAGE_EXECUTE, + PAGE_EXECUTE_READ, + PAGE_EXECUTE_READWRITE, - // PAGE_NOACCESS is invalid with PAGE_GUARD. - PAGE_READONLY | PAGE_GUARD, - PAGE_READWRITE | PAGE_GUARD, - PAGE_EXECUTE | PAGE_GUARD, - PAGE_EXECUTE_READ | PAGE_GUARD, - PAGE_EXECUTE_READWRITE | PAGE_GUARD, + // PAGE_NOACCESS is invalid with PAGE_GUARD. + PAGE_READONLY | PAGE_GUARD, + PAGE_READWRITE | PAGE_GUARD, + PAGE_EXECUTE | PAGE_GUARD, + PAGE_EXECUTE_READ | PAGE_GUARD, + PAGE_EXECUTE_READWRITE | PAGE_GUARD, }; // All of these allocations are leaked, we want to view them in windbg via @@ -142,7 +142,7 @@ void SomeCrashyFunction() { void AllocateExtraMemoryToBeSaved( crashpad::SimpleAddressRangeBag* extra_ranges) { - const size_t kNumInts = 2000; + constexpr size_t kNumInts = 2000; int* extra_memory = new int[kNumInts]; g_extra_memory_pointer = extra_memory; for (int i = 0; i < kNumInts; ++i) @@ -154,7 +154,7 @@ void AllocateExtraMemoryToBeSaved( void AllocateExtraUnsavedMemory(crashpad::SimpleAddressRangeBag* extra_ranges) { // Allocate some extra memory, and then Insert() but also Remove() it so we // can confirm it doesn't get saved. - const size_t kNumInts = 2000; + constexpr size_t kNumInts = 2000; int* extra_memory = new int[kNumInts]; g_extra_memory_not_saved = extra_memory; for (int i = 0; i < kNumInts; ++i) @@ -211,7 +211,7 @@ int CrashyMain(int argc, wchar_t* argv[]) { FreeLibrary(wmerror); // Make sure data pointed to by the stack is captured. - const int kDataSize = 512; + constexpr int kDataSize = 512; int* pointed_to_data = new int[kDataSize]; for (int i = 0; i < kDataSize; ++i) pointed_to_data[i] = i | ((i % 2 == 0) ? 0x80000000 : 0); diff --git a/infra/config/cq.cfg b/infra/config/cq.cfg index b436f9f8..b25a4aaa 100644 --- a/infra/config/cq.cfg +++ b/infra/config/cq.cfg @@ -33,14 +33,10 @@ verifiers { try_job { buckets { name: "master.client.crashpad" - builders { name: "crashpad_try_mac_dbg" } - builders { name: "crashpad_try_mac_rel" } - builders { name: "crashpad_try_win_x64_dbg" } - builders { name: "crashpad_try_win_x64_rel" } - builders { name: "crashpad_try_win_x86_dbg" } - builders { name: "crashpad_try_win_x86_rel" } - builders { name: "crashpad_try_win_x86_wow64_dbg" } - builders { name: "crashpad_try_win_x86_wow64_rel" } + builders { name: "mac_dbg" } + builders { name: "mac_rel" } + builders { name: "win_x64_dbg" } + builders { name: "win_x64_rel" } } } } diff --git a/minidump/minidump_context_writer_test.cc b/minidump/minidump_context_writer_test.cc index a6b5f3a9..0c2b5ea2 100644 --- a/minidump/minidump_context_writer_test.cc +++ b/minidump/minidump_context_writer_test.cc @@ -52,7 +52,7 @@ TEST(MinidumpContextWriter, MinidumpContextX86Writer) { SCOPED_TRACE("nonzero"); string_file.Reset(); - const uint32_t kSeed = 0x8086; + constexpr uint32_t kSeed = 0x8086; MinidumpContextX86Writer context_writer; InitializeMinidumpContextX86(context_writer.context(), kSeed); @@ -92,7 +92,7 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) { SCOPED_TRACE("nonzero"); string_file.Reset(); - const uint32_t kSeed = 0x808664; + constexpr uint32_t kSeed = 0x808664; MinidumpContextAMD64Writer context_writer; InitializeMinidumpContextAMD64(context_writer.context(), kSeed); @@ -109,7 +109,7 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) { } TEST(MinidumpContextWriter, CreateFromSnapshot_X86) { - const uint32_t kSeed = 32; + constexpr uint32_t kSeed = 32; CPUContextX86 context_snapshot_x86; CPUContext context_snapshot; @@ -131,7 +131,7 @@ TEST(MinidumpContextWriter, CreateFromSnapshot_X86) { } TEST(MinidumpContextWriter, CreateFromSnapshot_AMD64) { - const uint32_t kSeed = 64; + constexpr uint32_t kSeed = 64; CPUContextX86_64 context_snapshot_x86_64; CPUContext context_snapshot; diff --git a/minidump/minidump_crashpad_info_writer_test.cc b/minidump/minidump_crashpad_info_writer_test.cc index e24a606c..ab470f14 100644 --- a/minidump/minidump_crashpad_info_writer_test.cc +++ b/minidump/minidump_crashpad_info_writer_test.cc @@ -130,10 +130,10 @@ TEST(MinidumpCrashpadInfoWriter, SimpleAnnotations) { auto crashpad_info_writer = base::WrapUnique(new MinidumpCrashpadInfoWriter()); - const char kKey[] = + static constexpr char kKey[] = "a thing that provides a means of gaining access to or understanding " "something"; - const char kValue[] = + static constexpr char kValue[] = "the numerical amount denoted by an algebraic term; a magnitude, " "quantity, or number"; auto simple_string_dictionary_writer = @@ -174,7 +174,7 @@ TEST(MinidumpCrashpadInfoWriter, SimpleAnnotations) { } TEST(MinidumpCrashpadInfoWriter, CrashpadModuleList) { - const uint32_t kMinidumpModuleListIndex = 3; + constexpr uint32_t kMinidumpModuleListIndex = 3; MinidumpFileWriter minidump_file_writer; auto crashpad_info_writer = @@ -230,9 +230,9 @@ TEST(MinidumpCrashpadInfoWriter, InitializeFromSnapshot) { ASSERT_TRUE( client_id.InitializeFromString("fedcba98-7654-3210-0123-456789abcdef")); - const char kKey[] = "version"; - const char kValue[] = "40.0.2214.111"; - const char kEntry[] = "This is a simple annotation in a list."; + static constexpr char kKey[] = "version"; + static constexpr char kValue[] = "40.0.2214.111"; + static constexpr char kEntry[] = "This is a simple annotation in a list."; // Test with a useless module, one that doesn’t carry anything that would // require MinidumpCrashpadInfo or any child object. diff --git a/minidump/minidump_exception_writer.cc b/minidump/minidump_exception_writer.cc index d2466a6b..d870de36 100644 --- a/minidump/minidump_exception_writer.cc +++ b/minidump/minidump_exception_writer.cc @@ -64,7 +64,7 @@ void MinidumpExceptionWriter::SetExceptionInformation( DCHECK_EQ(state(), kStateMutable); const size_t parameters = exception_information.size(); - const size_t kMaxParameters = + constexpr size_t kMaxParameters = ARRAYSIZE_UNSAFE(exception_.ExceptionRecord.ExceptionInformation); CHECK_LE(parameters, kMaxParameters); diff --git a/minidump/minidump_exception_writer_test.cc b/minidump/minidump_exception_writer_test.cc index f2c0816d..5fa7f4a2 100644 --- a/minidump/minidump_exception_writer_test.cc +++ b/minidump/minidump_exception_writer_test.cc @@ -39,12 +39,12 @@ namespace { // This returns the MINIDUMP_EXCEPTION_STREAM stream in |exception_stream|. void GetExceptionStream(const std::string& file_contents, const MINIDUMP_EXCEPTION_STREAM** exception_stream) { - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kExceptionStreamOffset = + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kExceptionStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); - const size_t kContextOffset = + constexpr size_t kContextOffset = kExceptionStreamOffset + sizeof(MINIDUMP_EXCEPTION_STREAM); - const size_t kFileSize = kContextOffset + sizeof(MinidumpContextX86); + constexpr size_t kFileSize = kContextOffset + sizeof(MinidumpContextX86); ASSERT_EQ(kFileSize, file_contents.size()); const MINIDUMP_DIRECTORY* directory; @@ -96,7 +96,7 @@ TEST(MinidumpExceptionWriter, Minimal) { MinidumpFileWriter minidump_file_writer; auto exception_writer = base::WrapUnique(new MinidumpExceptionWriter()); - const uint32_t kSeed = 100; + constexpr uint32_t kSeed = 100; auto context_x86_writer = base::WrapUnique(new MinidumpContextX86Writer()); InitializeMinidumpContextX86(context_x86_writer->context(), kSeed); @@ -128,15 +128,15 @@ TEST(MinidumpExceptionWriter, Standard) { MinidumpFileWriter minidump_file_writer; auto exception_writer = base::WrapUnique(new MinidumpExceptionWriter()); - const uint32_t kSeed = 200; - const uint32_t kThreadID = 1; - const uint32_t kExceptionCode = 2; - const uint32_t kExceptionFlags = 3; - const uint32_t kExceptionRecord = 4; - const uint32_t kExceptionAddress = 5; - const uint64_t kExceptionInformation0 = 6; - const uint64_t kExceptionInformation1 = 7; - const uint64_t kExceptionInformation2 = 7; + constexpr uint32_t kSeed = 200; + constexpr uint32_t kThreadID = 1; + constexpr uint32_t kExceptionCode = 2; + constexpr uint32_t kExceptionFlags = 3; + constexpr uint32_t kExceptionRecord = 4; + constexpr uint32_t kExceptionAddress = 5; + constexpr uint64_t kExceptionInformation0 = 6; + constexpr uint64_t kExceptionInformation1 = 7; + constexpr uint64_t kExceptionInformation2 = 7; auto context_x86_writer = base::WrapUnique(new MinidumpContextX86Writer()); InitializeMinidumpContextX86(context_x86_writer->context(), kSeed); @@ -212,8 +212,8 @@ TEST(MinidumpExceptionWriter, InitializeFromSnapshot) { expect_exception.ExceptionRecord.ExceptionInformation[index] = exception_codes[index]; } - const uint64_t kThreadID = 0xaaaaaaaaaaaaaaaa; - const uint32_t kSeed = 65; + constexpr uint64_t kThreadID = 0xaaaaaaaaaaaaaaaa; + constexpr uint32_t kSeed = 65; TestExceptionSnapshot exception_snapshot; exception_snapshot.SetThreadID(kThreadID); diff --git a/minidump/minidump_extensions.cc b/minidump/minidump_extensions.cc index a956817e..bfb0e60e 100644 --- a/minidump/minidump_extensions.cc +++ b/minidump/minidump_extensions.cc @@ -16,7 +16,7 @@ namespace crashpad { -const uint32_t MinidumpModuleCrashpadInfo::kVersion; -const uint32_t MinidumpCrashpadInfo::kVersion; +constexpr uint32_t MinidumpModuleCrashpadInfo::kVersion; +constexpr uint32_t MinidumpCrashpadInfo::kVersion; } // namespace crashpad diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h index 245ef8b5..f3615669 100644 --- a/minidump/minidump_extensions.h +++ b/minidump/minidump_extensions.h @@ -290,7 +290,7 @@ struct ALIGNAS(4) PACKED MinidumpModuleCrashpadInfo { //! \brief The structure’s currently-defined version number. //! //! \sa version - static const uint32_t kVersion = 1; + static constexpr uint32_t kVersion = 1; //! \brief The structure’s version number. //! @@ -387,7 +387,7 @@ struct ALIGNAS(4) PACKED MinidumpCrashpadInfo { //! \brief The structure’s currently-defined version number. //! //! \sa version - static const uint32_t kVersion = 1; + static constexpr uint32_t kVersion = 1; //! \brief The structure’s version number. //! diff --git a/minidump/minidump_file_writer_test.cc b/minidump/minidump_file_writer_test.cc index 63f2ade8..b123742e 100644 --- a/minidump/minidump_file_writer_test.cc +++ b/minidump/minidump_file_writer_test.cc @@ -92,12 +92,13 @@ class TestStream final : public internal::MinidumpStreamWriter { TEST(MinidumpFileWriter, OneStream) { MinidumpFileWriter minidump_file; - const time_t kTimestamp = 0x155d2fb8; + constexpr time_t kTimestamp = 0x155d2fb8; minidump_file.SetTimestamp(kTimestamp); - const size_t kStreamSize = 5; - const MinidumpStreamType kStreamType = static_cast(0x4d); - const uint8_t kStreamValue = 0x5a; + constexpr size_t kStreamSize = 5; + constexpr MinidumpStreamType kStreamType = + static_cast(0x4d); + constexpr uint8_t kStreamValue = 0x5a; auto stream = base::WrapUnique(new TestStream(kStreamType, kStreamSize, kStreamValue)); ASSERT_TRUE(minidump_file.AddStream(std::move(stream))); @@ -105,9 +106,10 @@ TEST(MinidumpFileWriter, OneStream) { StringFile string_file; ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); - const size_t kFileSize = kStreamOffset + kStreamSize; + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kFileSize = kStreamOffset + kStreamSize; ASSERT_EQ(string_file.string().size(), kFileSize); @@ -131,12 +133,13 @@ TEST(MinidumpFileWriter, OneStream) { TEST(MinidumpFileWriter, AddUserExtensionStream) { MinidumpFileWriter minidump_file; - const time_t kTimestamp = 0x155d2fb8; + constexpr time_t kTimestamp = 0x155d2fb8; minidump_file.SetTimestamp(kTimestamp); - static const uint8_t kStreamData[] = "Hello World!"; - const size_t kStreamSize = arraysize(kStreamData); - const MinidumpStreamType kStreamType = static_cast(0x4d); + static constexpr uint8_t kStreamData[] = "Hello World!"; + constexpr size_t kStreamSize = arraysize(kStreamData); + constexpr MinidumpStreamType kStreamType = + static_cast(0x4d); auto data_source = base::WrapUnique(new test::BufferExtensionStreamDataSource( kStreamType, kStreamData, kStreamSize)); @@ -150,9 +153,10 @@ TEST(MinidumpFileWriter, AddUserExtensionStream) { StringFile string_file; ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); - const size_t kFileSize = kStreamOffset + kStreamSize; + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kFileSize = kStreamOffset + kStreamSize; ASSERT_EQ(string_file.string().size(), kFileSize); @@ -175,10 +179,11 @@ TEST(MinidumpFileWriter, AddUserExtensionStream) { TEST(MinidumpFileWriter, AddEmptyUserExtensionStream) { MinidumpFileWriter minidump_file; - const time_t kTimestamp = 0x155d2fb8; + constexpr time_t kTimestamp = 0x155d2fb8; minidump_file.SetTimestamp(kTimestamp); - const MinidumpStreamType kStreamType = static_cast(0x4d); + constexpr MinidumpStreamType kStreamType = + static_cast(0x4d); auto data_source = base::WrapUnique( new test::BufferExtensionStreamDataSource(kStreamType, nullptr, 0)); @@ -187,9 +192,10 @@ TEST(MinidumpFileWriter, AddEmptyUserExtensionStream) { StringFile string_file; ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); - const size_t kFileSize = kStreamOffset; + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kFileSize = kStreamOffset; ASSERT_EQ(string_file.string().size(), kFileSize); @@ -206,12 +212,13 @@ TEST(MinidumpFileWriter, AddEmptyUserExtensionStream) { TEST(MinidumpFileWriter, ThreeStreams) { MinidumpFileWriter minidump_file; - const time_t kTimestamp = 0x155d2fb8; + constexpr time_t kTimestamp = 0x155d2fb8; minidump_file.SetTimestamp(kTimestamp); - const size_t kStream0Size = 5; - const MinidumpStreamType kStream0Type = static_cast(0x6d); - const uint8_t kStream0Value = 0x5a; + constexpr size_t kStream0Size = 5; + constexpr MinidumpStreamType kStream0Type = + static_cast(0x6d); + constexpr uint8_t kStream0Value = 0x5a; auto stream0 = base::WrapUnique( new TestStream(kStream0Type, kStream0Size, kStream0Value)); ASSERT_TRUE(minidump_file.AddStream(std::move(stream0))); @@ -219,16 +226,18 @@ TEST(MinidumpFileWriter, ThreeStreams) { // Make the second stream’s type be a smaller quantity than the first stream’s // to test that the streams show up in the order that they were added, not in // numeric order. - const size_t kStream1Size = 3; - const MinidumpStreamType kStream1Type = static_cast(0x4d); - const uint8_t kStream1Value = 0xa5; + constexpr size_t kStream1Size = 3; + constexpr MinidumpStreamType kStream1Type = + static_cast(0x4d); + constexpr uint8_t kStream1Value = 0xa5; auto stream1 = base::WrapUnique( new TestStream(kStream1Type, kStream1Size, kStream1Value)); ASSERT_TRUE(minidump_file.AddStream(std::move(stream1))); - const size_t kStream2Size = 1; - const MinidumpStreamType kStream2Type = static_cast(0x7e); - const uint8_t kStream2Value = 0x36; + constexpr size_t kStream2Size = 1; + constexpr MinidumpStreamType kStream2Type = + static_cast(0x7e); + constexpr uint8_t kStream2Value = 0x36; auto stream2 = base::WrapUnique( new TestStream(kStream2Type, kStream2Size, kStream2Value)); ASSERT_TRUE(minidump_file.AddStream(std::move(stream2))); @@ -236,14 +245,16 @@ TEST(MinidumpFileWriter, ThreeStreams) { StringFile string_file; ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kStream0Offset = + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kStream0Offset = kDirectoryOffset + 3 * sizeof(MINIDUMP_DIRECTORY); - const size_t kStream1Padding = 3; - const size_t kStream1Offset = kStream0Offset + kStream0Size + kStream1Padding; - const size_t kStream2Padding = 1; - const size_t kStream2Offset = kStream1Offset + kStream1Size + kStream2Padding; - const size_t kFileSize = kStream2Offset + kStream2Size; + constexpr size_t kStream1Padding = 3; + constexpr size_t kStream1Offset = + kStream0Offset + kStream0Size + kStream1Padding; + constexpr size_t kStream2Padding = 1; + constexpr size_t kStream2Offset = + kStream1Offset + kStream1Size + kStream2Padding; + constexpr size_t kFileSize = kStream2Offset + kStream2Size; ASSERT_EQ(string_file.string().size(), kFileSize); @@ -270,7 +281,7 @@ TEST(MinidumpFileWriter, ThreeStreams) { std::string expected_stream0(kStream0Size, kStream0Value); EXPECT_EQ(memcmp(stream0_data, expected_stream0.c_str(), kStream0Size), 0); - const int kZeroes[16] = {}; + static constexpr int kZeroes[16] = {}; ASSERT_GE(sizeof(kZeroes), kStream1Padding); EXPECT_EQ(memcmp(stream0_data + kStream0Size, kZeroes, kStream1Padding), 0); @@ -295,17 +306,19 @@ TEST(MinidumpFileWriter, ThreeStreams) { TEST(MinidumpFileWriter, ZeroLengthStream) { MinidumpFileWriter minidump_file; - const size_t kStreamSize = 0; - const MinidumpStreamType kStreamType = static_cast(0x4d); + constexpr size_t kStreamSize = 0; + constexpr MinidumpStreamType kStreamType = + static_cast(0x4d); auto stream = base::WrapUnique(new TestStream(kStreamType, kStreamSize, 0)); ASSERT_TRUE(minidump_file.AddStream(std::move(stream))); StringFile string_file; ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); - const size_t kFileSize = kStreamOffset + kStreamSize; + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kFileSize = kStreamOffset + kStreamSize; ASSERT_EQ(string_file.string().size(), kFileSize); @@ -321,8 +334,8 @@ TEST(MinidumpFileWriter, ZeroLengthStream) { } TEST(MinidumpFileWriter, InitializeFromSnapshot_Basic) { - const uint32_t kSnapshotTime = 0x4976043c; - const timeval kSnapshotTimeval = { static_cast(kSnapshotTime), 0 }; + constexpr uint32_t kSnapshotTime = 0x4976043c; + constexpr timeval kSnapshotTimeval = {static_cast(kSnapshotTime), 0}; TestProcessSnapshot process_snapshot; process_snapshot.SetSnapshotTime(kSnapshotTimeval); @@ -333,9 +346,9 @@ TEST(MinidumpFileWriter, InitializeFromSnapshot_Basic) { process_snapshot.SetSystem(std::move(system_snapshot)); auto peb_snapshot = base::WrapUnique(new TestMemorySnapshot()); - const uint64_t kPebAddress = 0x07f90000; + constexpr uint64_t kPebAddress = 0x07f90000; peb_snapshot->SetAddress(kPebAddress); - const size_t kPebSize = 0x280; + constexpr size_t kPebSize = 0x280; peb_snapshot->SetSize(kPebSize); peb_snapshot->SetValue('p'); process_snapshot.AddExtraMemory(std::move(peb_snapshot)); @@ -383,10 +396,10 @@ TEST(MinidumpFileWriter, InitializeFromSnapshot_Basic) { TEST(MinidumpFileWriter, InitializeFromSnapshot_Exception) { // In a 32-bit environment, this will give a “timestamp out of range” warning, // but the test should complete without failure. - const uint32_t kSnapshotTime = 0xfd469ab8; + constexpr uint32_t kSnapshotTime = 0xfd469ab8; MSVC_SUPPRESS_WARNING(4309); // Truncation of constant value. MSVC_SUPPRESS_WARNING(4838); // Narrowing conversion. - const timeval kSnapshotTimeval = { static_cast(kSnapshotTime), 0 }; + constexpr timeval kSnapshotTimeval = {static_cast(kSnapshotTime), 0}; TestProcessSnapshot process_snapshot; process_snapshot.SetSnapshotTime(kSnapshotTimeval); @@ -449,8 +462,8 @@ TEST(MinidumpFileWriter, InitializeFromSnapshot_Exception) { } TEST(MinidumpFileWriter, InitializeFromSnapshot_CrashpadInfo) { - const uint32_t kSnapshotTime = 0x15393bd3; - const timeval kSnapshotTimeval = { static_cast(kSnapshotTime), 0 }; + constexpr uint32_t kSnapshotTime = 0x15393bd3; + constexpr timeval kSnapshotTimeval = {static_cast(kSnapshotTime), 0}; TestProcessSnapshot process_snapshot; process_snapshot.SetSnapshotTime(kSnapshotTimeval); @@ -519,16 +532,17 @@ TEST(MinidumpFileWriter, InitializeFromSnapshot_CrashpadInfo) { TEST(MinidumpFileWriter, SameStreamType) { MinidumpFileWriter minidump_file; - const size_t kStream0Size = 3; - const MinidumpStreamType kStreamType = static_cast(0x4d); - const uint8_t kStream0Value = 0x5a; + constexpr size_t kStream0Size = 3; + constexpr MinidumpStreamType kStreamType = + static_cast(0x4d); + constexpr uint8_t kStream0Value = 0x5a; auto stream0 = base::WrapUnique( new TestStream(kStreamType, kStream0Size, kStream0Value)); ASSERT_TRUE(minidump_file.AddStream(std::move(stream0))); // An attempt to add a second stream of the same type should fail. - const size_t kStream1Size = 5; - const uint8_t kStream1Value = 0xa5; + constexpr size_t kStream1Size = 5; + constexpr uint8_t kStream1Value = 0xa5; auto stream1 = base::WrapUnique( new TestStream(kStreamType, kStream1Size, kStream1Value)); ASSERT_FALSE(minidump_file.AddStream(std::move(stream1))); @@ -536,9 +550,10 @@ TEST(MinidumpFileWriter, SameStreamType) { StringFile string_file; ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kStream0Offset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); - const size_t kFileSize = kStream0Offset + kStream0Size; + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kStream0Offset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kFileSize = kStream0Offset + kStream0Size; ASSERT_EQ(string_file.string().size(), kFileSize); diff --git a/minidump/minidump_handle_writer_test.cc b/minidump/minidump_handle_writer_test.cc index 6f238c8f..68b4ff0e 100644 --- a/minidump/minidump_handle_writer_test.cc +++ b/minidump/minidump_handle_writer_test.cc @@ -34,8 +34,8 @@ namespace { void GetHandleDataStream( const std::string& file_contents, const MINIDUMP_HANDLE_DATA_STREAM** handle_data_stream) { - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kHandleDataStreamOffset = + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kHandleDataStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); const MINIDUMP_DIRECTORY* directory; @@ -44,7 +44,7 @@ void GetHandleDataStream( ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); ASSERT_TRUE(directory); - const size_t kDirectoryIndex = 0; + constexpr size_t kDirectoryIndex = 0; ASSERT_EQ(directory[kDirectoryIndex].StreamType, kMinidumpStreamTypeHandleData); diff --git a/minidump/minidump_memory_info_writer_test.cc b/minidump/minidump_memory_info_writer_test.cc index 1458279f..18978fe8 100644 --- a/minidump/minidump_memory_info_writer_test.cc +++ b/minidump/minidump_memory_info_writer_test.cc @@ -33,8 +33,8 @@ namespace { void GetMemoryInfoListStream( const std::string& file_contents, const MINIDUMP_MEMORY_INFO_LIST** memory_info_list) { - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kMemoryInfoListStreamOffset = + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kMemoryInfoListStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); const MINIDUMP_DIRECTORY* directory; @@ -43,7 +43,7 @@ void GetMemoryInfoListStream( ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); ASSERT_TRUE(directory); - const size_t kDirectoryIndex = 0; + constexpr size_t kDirectoryIndex = 0; ASSERT_EQ(directory[kDirectoryIndex].StreamType, kMinidumpStreamTypeMemoryInfoList); diff --git a/minidump/minidump_memory_writer_test.cc b/minidump/minidump_memory_writer_test.cc index 45fdf491..ffa56d51 100644 --- a/minidump/minidump_memory_writer_test.cc +++ b/minidump/minidump_memory_writer_test.cc @@ -35,7 +35,7 @@ namespace crashpad { namespace test { namespace { -const MinidumpStreamType kBogusStreamType = +constexpr MinidumpStreamType kBogusStreamType = static_cast(1234); // expected_streams is the expected number of streams in the file. The memory @@ -44,7 +44,7 @@ const MinidumpStreamType kBogusStreamType = void GetMemoryListStream(const std::string& file_contents, const MINIDUMP_MEMORY_LIST** memory_list, const uint32_t expected_streams) { - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); const size_t kMemoryListStreamOffset = kDirectoryOffset + expected_streams * sizeof(MINIDUMP_DIRECTORY); const size_t kMemoryDescriptorsOffset = @@ -99,9 +99,9 @@ TEST(MinidumpMemoryWriter, OneMemoryRegion) { MinidumpFileWriter minidump_file_writer; auto memory_list_writer = base::WrapUnique(new MinidumpMemoryListWriter()); - const uint64_t kBaseAddress = 0xfedcba9876543210; - const uint64_t kSize = 0x1000; - const uint8_t kValue = 'm'; + constexpr uint64_t kBaseAddress = 0xfedcba9876543210; + constexpr uint64_t kSize = 0x1000; + constexpr uint8_t kValue = 'm'; auto memory_writer = base::WrapUnique( new TestMinidumpMemoryWriter(kBaseAddress, kSize, kValue)); @@ -134,12 +134,12 @@ TEST(MinidumpMemoryWriter, TwoMemoryRegions) { MinidumpFileWriter minidump_file_writer; auto memory_list_writer = base::WrapUnique(new MinidumpMemoryListWriter()); - const uint64_t kBaseAddress0 = 0xc0ffee; - const uint64_t kSize0 = 0x0100; - const uint8_t kValue0 = '6'; - const uint64_t kBaseAddress1 = 0xfac00fac; - const uint64_t kSize1 = 0x0200; - const uint8_t kValue1 = '!'; + constexpr uint64_t kBaseAddress0 = 0xc0ffee; + constexpr uint64_t kSize0 = 0x0100; + constexpr uint8_t kValue0 = '6'; + constexpr uint64_t kBaseAddress1 = 0xfac00fac; + constexpr uint64_t kSize1 = 0x0200; + constexpr uint8_t kValue1 = '!'; auto memory_writer_0 = base::WrapUnique( new TestMinidumpMemoryWriter(kBaseAddress0, kSize0, kValue0)); @@ -238,9 +238,9 @@ TEST(MinidumpMemoryWriter, ExtraMemory) { // memory writer a child of the memory list writer. MinidumpFileWriter minidump_file_writer; - const uint64_t kBaseAddress0 = 0x1000; - const size_t kSize0 = 0x0400; - const uint8_t kValue0 = '1'; + constexpr uint64_t kBaseAddress0 = 0x1000; + constexpr size_t kSize0 = 0x0400; + constexpr uint8_t kValue0 = '1'; auto test_memory_stream = base::WrapUnique(new TestMemoryStream(kBaseAddress0, kSize0, kValue0)); @@ -249,9 +249,9 @@ TEST(MinidumpMemoryWriter, ExtraMemory) { ASSERT_TRUE(minidump_file_writer.AddStream(std::move(test_memory_stream))); - const uint64_t kBaseAddress1 = 0x2000; - const size_t kSize1 = 0x0400; - const uint8_t kValue1 = 'm'; + constexpr uint64_t kBaseAddress1 = 0x2000; + constexpr size_t kSize1 = 0x0400; + constexpr uint8_t kValue1 = 'm'; auto memory_writer = base::WrapUnique( new TestMinidumpMemoryWriter(kBaseAddress1, kSize1, kValue1)); diff --git a/minidump/minidump_misc_info_writer.cc b/minidump/minidump_misc_info_writer.cc index 2ea8fa90..3e0dbbc9 100644 --- a/minidump/minidump_misc_info_writer.cc +++ b/minidump/minidump_misc_info_writer.cc @@ -42,7 +42,7 @@ namespace { uint32_t TimevalToRoundedSeconds(const timeval& tv) { uint32_t seconds = InRangeCast(tv.tv_sec, std::numeric_limits::max()); - const int kMicrosecondsPerSecond = static_cast(1E6); + constexpr int kMicrosecondsPerSecond = static_cast(1E6); if (tv.tv_usec >= kMicrosecondsPerSecond / 2 && seconds != std::numeric_limits::max()) { ++seconds; @@ -101,25 +101,25 @@ std::string MinidumpMiscInfoDebugBuildString() { // plus a UTF-16 NUL terminator. Don’t let strings get longer than this, or // they will be truncated and a message will be logged. #if defined(OS_MACOSX) - const char kOS[] = "mac"; + static constexpr char kOS[] = "mac"; #elif defined(OS_ANDROID) - const char kOS[] = "android"; + static constexpr char kOS[] = "android"; #elif defined(OS_LINUX) - const char kOS[] = "linux"; + static constexpr char kOS[] = "linux"; #elif defined(OS_WIN) - const char kOS[] = "win"; + static constexpr char kOS[] = "win"; #else #error define kOS for this operating system #endif #if defined(ARCH_CPU_X86) - const char kCPU[] = "i386"; + static constexpr char kCPU[] = "i386"; #elif defined(ARCH_CPU_X86_64) - const char kCPU[] = "amd64"; + static constexpr char kCPU[] = "amd64"; #elif defined(ARCH_CPU_ARMEL) - const char kCPU[] = "arm"; + static constexpr char kCPU[] = "arm"; #elif defined(ARCH_CPU_ARM64) - const char kCPU[] = "arm64"; + static constexpr char kCPU[] = "arm64"; #else #error define kCPU for this CPU #endif @@ -163,7 +163,7 @@ void MinidumpMiscInfoWriter::InitializeFromSnapshot( uint64_t current_hz; uint64_t max_hz; system_snapshot->CPUFrequency(¤t_hz, &max_hz); - const uint32_t kHzPerMHz = static_cast(1E6); + constexpr uint32_t kHzPerMHz = static_cast(1E6); SetProcessorPowerInfo( InRangeCast(current_hz / kHzPerMHz, std::numeric_limits::max()), diff --git a/minidump/minidump_misc_info_writer_test.cc b/minidump/minidump_misc_info_writer_test.cc index 74a41701..eb682752 100644 --- a/minidump/minidump_misc_info_writer_test.cc +++ b/minidump/minidump_misc_info_writer_test.cc @@ -43,11 +43,11 @@ namespace { template void GetMiscInfoStream(const std::string& file_contents, const T** misc_info) { - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kMiscInfoStreamOffset = + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kMiscInfoStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); - const size_t kMiscInfoStreamSize = sizeof(T); - const size_t kFileSize = kMiscInfoStreamOffset + kMiscInfoStreamSize; + constexpr size_t kMiscInfoStreamSize = sizeof(T); + constexpr size_t kFileSize = kMiscInfoStreamOffset + kMiscInfoStreamSize; ASSERT_EQ(file_contents.size(), kFileSize); @@ -211,7 +211,7 @@ TEST(MinidumpMiscInfoWriter, ProcessId) { MinidumpFileWriter minidump_file_writer; auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter()); - const uint32_t kProcessId = 12345; + constexpr uint32_t kProcessId = 12345; misc_info_writer->SetProcessID(kProcessId); @@ -234,9 +234,9 @@ TEST(MinidumpMiscInfoWriter, ProcessTimes) { MinidumpFileWriter minidump_file_writer; auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter()); - const time_t kProcessCreateTime = 0x15252f00; - const uint32_t kProcessUserTime = 10; - const uint32_t kProcessKernelTime = 5; + constexpr time_t kProcessCreateTime = 0x15252f00; + constexpr uint32_t kProcessUserTime = 10; + constexpr uint32_t kProcessKernelTime = 5; misc_info_writer->SetProcessTimes( kProcessCreateTime, kProcessUserTime, kProcessKernelTime); @@ -262,11 +262,11 @@ TEST(MinidumpMiscInfoWriter, ProcessorPowerInfo) { MinidumpFileWriter minidump_file_writer; auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter()); - const uint32_t kProcessorMaxMhz = 2800; - const uint32_t kProcessorCurrentMhz = 2300; - const uint32_t kProcessorMhzLimit = 3300; - const uint32_t kProcessorMaxIdleState = 5; - const uint32_t kProcessorCurrentIdleState = 1; + constexpr uint32_t kProcessorMaxMhz = 2800; + constexpr uint32_t kProcessorCurrentMhz = 2300; + constexpr uint32_t kProcessorMhzLimit = 3300; + constexpr uint32_t kProcessorMaxIdleState = 5; + constexpr uint32_t kProcessorCurrentIdleState = 1; misc_info_writer->SetProcessorPowerInfo(kProcessorMaxMhz, kProcessorCurrentMhz, @@ -297,7 +297,7 @@ TEST(MinidumpMiscInfoWriter, ProcessIntegrityLevel) { MinidumpFileWriter minidump_file_writer; auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter()); - const uint32_t kProcessIntegrityLevel = 0x2000; + constexpr uint32_t kProcessIntegrityLevel = 0x2000; misc_info_writer->SetProcessIntegrityLevel(kProcessIntegrityLevel); @@ -320,7 +320,7 @@ TEST(MinidumpMiscInfoWriter, ProcessExecuteFlags) { MinidumpFileWriter minidump_file_writer; auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter()); - const uint32_t kProcessExecuteFlags = 0x13579bdf; + constexpr uint32_t kProcessExecuteFlags = 0x13579bdf; misc_info_writer->SetProcessExecuteFlags(kProcessExecuteFlags); @@ -343,7 +343,7 @@ TEST(MinidumpMiscInfoWriter, ProtectedProcess) { MinidumpFileWriter minidump_file_writer; auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter()); - const uint32_t kProtectedProcess = 1; + constexpr uint32_t kProtectedProcess = 1; misc_info_writer->SetProtectedProcess(kProtectedProcess); @@ -366,14 +366,14 @@ TEST(MinidumpMiscInfoWriter, TimeZone) { MinidumpFileWriter minidump_file_writer; auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter()); - const uint32_t kTimeZoneId = 2; - const int32_t kBias = 300; - const char kStandardName[] = "EST"; - const SYSTEMTIME kStandardDate = {0, 11, 1, 0, 2, 0, 0, 0}; - const int32_t kStandardBias = 0; - const char kDaylightName[] = "EDT"; - const SYSTEMTIME kDaylightDate = {0, 3, 2, 0, 2, 0, 0, 0}; - const int32_t kDaylightBias = -60; + constexpr uint32_t kTimeZoneId = 2; + constexpr int32_t kBias = 300; + static constexpr char kStandardName[] = "EST"; + constexpr SYSTEMTIME kStandardDate = {0, 11, 1, 0, 2, 0, 0, 0}; + constexpr int32_t kStandardBias = 0; + static constexpr char kDaylightName[] = "EDT"; + constexpr SYSTEMTIME kDaylightDate = {0, 3, 2, 0, 2, 0, 0, 0}; + constexpr int32_t kDaylightBias = -60; misc_info_writer->SetTimeZone(kTimeZoneId, kBias, @@ -423,19 +423,19 @@ TEST(MinidumpMiscInfoWriter, TimeZoneStringsOverflow) { MinidumpFileWriter minidump_file_writer; auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter()); - const uint32_t kTimeZoneId = 2; - const int32_t kBias = 300; + constexpr uint32_t kTimeZoneId = 2; + constexpr int32_t kBias = 300; MINIDUMP_MISC_INFO_N tmp; ALLOW_UNUSED_LOCAL(tmp); std::string standard_name(ARRAYSIZE_UNSAFE(tmp.TimeZone.StandardName) + 1, 's'); - const int32_t kStandardBias = 0; + constexpr int32_t kStandardBias = 0; std::string daylight_name(ARRAYSIZE_UNSAFE(tmp.TimeZone.DaylightName), 'd'); - const int32_t kDaylightBias = -60; + constexpr int32_t kDaylightBias = -60; // Test using kSystemTimeZero, because not all platforms will be able to // provide daylight saving time transition times. - const SYSTEMTIME kSystemTimeZero = {}; + constexpr SYSTEMTIME kSystemTimeZero = {}; misc_info_writer->SetTimeZone(kTimeZoneId, kBias, @@ -482,8 +482,8 @@ TEST(MinidumpMiscInfoWriter, BuildStrings) { MinidumpFileWriter minidump_file_writer; auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter()); - const char kBuildString[] = "build string"; - const char kDebugBuildString[] = "debug build string"; + static constexpr char kBuildString[] = "build string"; + static constexpr char kDebugBuildString[] = "debug build string"; misc_info_writer->SetBuildString(kBuildString, kDebugBuildString); @@ -551,7 +551,7 @@ TEST(MinidumpMiscInfoWriter, XStateData) { MinidumpFileWriter minidump_file_writer; auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter()); - const XSTATE_CONFIG_FEATURE_MSC_INFO kXStateData = { + constexpr XSTATE_CONFIG_FEATURE_MSC_INFO kXStateData = { sizeof(XSTATE_CONFIG_FEATURE_MSC_INFO), 1024, 0x000000000000005f, @@ -585,7 +585,7 @@ TEST(MinidumpMiscInfoWriter, ProcessCookie) { MinidumpFileWriter minidump_file_writer; auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter()); - const uint32_t kProcessCookie = 0x12345678; + constexpr uint32_t kProcessCookie = 0x12345678; misc_info_writer->SetProcessCookie(kProcessCookie); @@ -608,27 +608,27 @@ TEST(MinidumpMiscInfoWriter, Everything) { MinidumpFileWriter minidump_file_writer; auto misc_info_writer = base::WrapUnique(new MinidumpMiscInfoWriter()); - const uint32_t kProcessId = 12345; - const time_t kProcessCreateTime = 0x15252f00; - const uint32_t kProcessUserTime = 10; - const uint32_t kProcessKernelTime = 5; - const uint32_t kProcessorMaxMhz = 2800; - const uint32_t kProcessorCurrentMhz = 2300; - const uint32_t kProcessorMhzLimit = 3300; - const uint32_t kProcessorMaxIdleState = 5; - const uint32_t kProcessorCurrentIdleState = 1; - const uint32_t kProcessIntegrityLevel = 0x2000; - const uint32_t kProcessExecuteFlags = 0x13579bdf; - const uint32_t kProtectedProcess = 1; - const uint32_t kTimeZoneId = 2; - const int32_t kBias = 300; - const char kStandardName[] = "EST"; - const int32_t kStandardBias = 0; - const char kDaylightName[] = "EDT"; - const int32_t kDaylightBias = -60; - const SYSTEMTIME kSystemTimeZero = {}; - const char kBuildString[] = "build string"; - const char kDebugBuildString[] = "debug build string"; + constexpr uint32_t kProcessId = 12345; + constexpr time_t kProcessCreateTime = 0x15252f00; + constexpr uint32_t kProcessUserTime = 10; + constexpr uint32_t kProcessKernelTime = 5; + constexpr uint32_t kProcessorMaxMhz = 2800; + constexpr uint32_t kProcessorCurrentMhz = 2300; + constexpr uint32_t kProcessorMhzLimit = 3300; + constexpr uint32_t kProcessorMaxIdleState = 5; + constexpr uint32_t kProcessorCurrentIdleState = 1; + constexpr uint32_t kProcessIntegrityLevel = 0x2000; + constexpr uint32_t kProcessExecuteFlags = 0x13579bdf; + constexpr uint32_t kProtectedProcess = 1; + constexpr uint32_t kTimeZoneId = 2; + constexpr int32_t kBias = 300; + static constexpr char kStandardName[] = "EST"; + constexpr int32_t kStandardBias = 0; + static constexpr char kDaylightName[] = "EDT"; + constexpr int32_t kDaylightBias = -60; + constexpr SYSTEMTIME kSystemTimeZero = {}; + static constexpr char kBuildString[] = "build string"; + static constexpr char kDebugBuildString[] = "debug build string"; misc_info_writer->SetProcessID(kProcessId); misc_info_writer->SetProcessTimes( @@ -711,14 +711,15 @@ TEST(MinidumpMiscInfoWriter, Everything) { TEST(MinidumpMiscInfoWriter, InitializeFromSnapshot) { MINIDUMP_MISC_INFO_4 expect_misc_info = {}; - const char kStandardTimeName[] = "EST"; - const char kDaylightTimeName[] = "EDT"; - const char kOSVersionFull[] = + static constexpr char kStandardTimeName[] = "EST"; + static constexpr char kDaylightTimeName[] = "EDT"; + static constexpr char kOSVersionFull[] = "Mac OS X 10.9.5 (13F34); " "Darwin 13.4.0 Darwin Kernel Version 13.4.0: " "Sun Aug 17 19:50:11 PDT 2014; " "root:xnu-2422.115.4~1/RELEASE_X86_64 x86_64"; - const char kMachineDescription[] = "MacBookPro11,3 (Mac-2BD1B31983FE1663)"; + static constexpr char kMachineDescription[] = + "MacBookPro11,3 (Mac-2BD1B31983FE1663)"; base::string16 standard_time_name_utf16 = base::UTF8ToUTF16(kStandardTimeName); base::string16 daylight_time_name_utf16 = @@ -772,7 +773,7 @@ TEST(MinidumpMiscInfoWriter, InitializeFromSnapshot) { process_snapshot.SetProcessCPUTimes(kUserCPUTime, kSystemCPUTime); auto system_snapshot = base::WrapUnique(new TestSystemSnapshot()); - const uint64_t kHzPerMHz = static_cast(1E6); + constexpr uint64_t kHzPerMHz = static_cast(1E6); system_snapshot->SetCPUFrequency( expect_misc_info.ProcessorCurrentMhz * kHzPerMHz, expect_misc_info.ProcessorMaxMhz * kHzPerMHz); diff --git a/minidump/minidump_module_crashpad_info_writer_test.cc b/minidump/minidump_module_crashpad_info_writer_test.cc index 74451843..b5aa0054 100644 --- a/minidump/minidump_module_crashpad_info_writer_test.cc +++ b/minidump/minidump_module_crashpad_info_writer_test.cc @@ -108,10 +108,10 @@ TEST(MinidumpModuleCrashpadInfoWriter, EmptyModule) { } TEST(MinidumpModuleCrashpadInfoWriter, FullModule) { - const uint32_t kMinidumpModuleListIndex = 1; - const char kKey[] = "key"; - const char kValue[] = "value"; - const char kEntry[] = "entry"; + constexpr uint32_t kMinidumpModuleListIndex = 1; + static constexpr char kKey[] = "key"; + static constexpr char kValue[] = "value"; + static constexpr char kEntry[] = "entry"; std::vector vector(1, std::string(kEntry)); StringFile string_file; @@ -194,15 +194,15 @@ TEST(MinidumpModuleCrashpadInfoWriter, FullModule) { } TEST(MinidumpModuleCrashpadInfoWriter, ThreeModules) { - const uint32_t kMinidumpModuleListIndex0 = 0; - const char kKey0[] = "key"; - const char kValue0[] = "value"; - const uint32_t kMinidumpModuleListIndex1 = 2; - const uint32_t kMinidumpModuleListIndex2 = 5; - const char kKey2A[] = "K"; - const char kValue2A[] = "VVV"; - const char kKey2B[] = "river"; - const char kValue2B[] = "hudson"; + constexpr uint32_t kMinidumpModuleListIndex0 = 0; + static constexpr char kKey0[] = "key"; + static constexpr char kValue0[] = "value"; + constexpr uint32_t kMinidumpModuleListIndex1 = 2; + constexpr uint32_t kMinidumpModuleListIndex2 = 5; + static constexpr char kKey2A[] = "K"; + static constexpr char kValue2A[] = "VVV"; + static constexpr char kKey2B[] = "river"; + static constexpr char kValue2B[] = "hudson"; StringFile string_file; @@ -339,14 +339,14 @@ TEST(MinidumpModuleCrashpadInfoWriter, ThreeModules) { } TEST(MinidumpModuleCrashpadInfoWriter, InitializeFromSnapshot) { - const char kKey0A[] = "k"; - const char kValue0A[] = "value"; - const char kKey0B[] = "hudson"; - const char kValue0B[] = "estuary"; - const char kKey2[] = "k"; - const char kValue2[] = "different_value"; - const char kEntry3A[] = "list"; - const char kEntry3B[] = "erine"; + static constexpr char kKey0A[] = "k"; + static constexpr char kValue0A[] = "value"; + static constexpr char kKey0B[] = "hudson"; + static constexpr char kValue0B[] = "estuary"; + static constexpr char kKey2[] = "k"; + static constexpr char kValue2[] = "different_value"; + static constexpr char kEntry3A[] = "list"; + static constexpr char kEntry3B[] = "erine"; std::vector module_snapshots; diff --git a/minidump/minidump_module_writer_test.cc b/minidump/minidump_module_writer_test.cc index b2478d5a..47ad7f78 100644 --- a/minidump/minidump_module_writer_test.cc +++ b/minidump/minidump_module_writer_test.cc @@ -42,10 +42,10 @@ namespace { void GetModuleListStream(const std::string& file_contents, const MINIDUMP_MODULE_LIST** module_list) { - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kModuleListStreamOffset = + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kModuleListStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); - const size_t kModulesOffset = + constexpr size_t kModulesOffset = kModuleListStreamOffset + sizeof(MINIDUMP_MODULE_LIST); ASSERT_GE(file_contents.size(), kModulesOffset); @@ -271,7 +271,7 @@ TEST(MinidumpModuleWriter, EmptyModule) { MinidumpFileWriter minidump_file_writer; auto module_list_writer = base::WrapUnique(new MinidumpModuleListWriter()); - const char kModuleName[] = "test_executable"; + static constexpr char kModuleName[] = "test_executable"; auto module_writer = base::WrapUnique(new MinidumpModuleWriter()); module_writer->SetName(kModuleName); @@ -310,32 +310,32 @@ TEST(MinidumpModuleWriter, OneModule) { MinidumpFileWriter minidump_file_writer; auto module_list_writer = base::WrapUnique(new MinidumpModuleListWriter()); - const char kModuleName[] = "statically_linked"; - const uint64_t kModuleBase = 0x10da69000; - const uint32_t kModuleSize = 0x1000; - const uint32_t kChecksum = 0x76543210; - const time_t kTimestamp = 0x386d4380; - const uint32_t kFileVersionMS = 0x00010002; - const uint32_t kFileVersionLS = 0x00030004; - const uint32_t kProductVersionMS = 0x00050006; - const uint32_t kProductVersionLS = 0x00070008; - const uint32_t kFileFlagsMask = VS_FF_DEBUG | VS_FF_PRERELEASE | - VS_FF_PATCHED | VS_FF_PRIVATEBUILD | - VS_FF_INFOINFERRED | VS_FF_SPECIALBUILD; - const uint32_t kFileFlags = VS_FF_PRIVATEBUILD | VS_FF_SPECIALBUILD; - const uint32_t kFileOS = VOS_DOS; - const uint32_t kFileType = VFT_DRV; - const uint32_t kFileSubtype = VFT2_DRV_KEYBOARD; - const char kPDBName[] = "statical.pdb"; - const uint8_t kPDBUUIDBytes[16] = + static constexpr char kModuleName[] = "statically_linked"; + constexpr uint64_t kModuleBase = 0x10da69000; + constexpr uint32_t kModuleSize = 0x1000; + constexpr uint32_t kChecksum = 0x76543210; + constexpr time_t kTimestamp = 0x386d4380; + constexpr uint32_t kFileVersionMS = 0x00010002; + constexpr uint32_t kFileVersionLS = 0x00030004; + constexpr uint32_t kProductVersionMS = 0x00050006; + constexpr uint32_t kProductVersionLS = 0x00070008; + constexpr uint32_t kFileFlagsMask = VS_FF_DEBUG | VS_FF_PRERELEASE | + VS_FF_PATCHED | VS_FF_PRIVATEBUILD | + VS_FF_INFOINFERRED | VS_FF_SPECIALBUILD; + constexpr uint32_t kFileFlags = VS_FF_PRIVATEBUILD | VS_FF_SPECIALBUILD; + constexpr uint32_t kFileOS = VOS_DOS; + constexpr uint32_t kFileType = VFT_DRV; + constexpr uint32_t kFileSubtype = VFT2_DRV_KEYBOARD; + static constexpr char kPDBName[] = "statical.pdb"; + static constexpr uint8_t kPDBUUIDBytes[16] = {0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x08, 0x19, 0x2a, 0x3b, 0x4c, 0x5d, 0x6e, 0x7f}; UUID pdb_uuid; pdb_uuid.InitializeFromBytes(kPDBUUIDBytes); - const uint32_t kPDBAge = 1; - const uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME; - const char kDebugName[] = "statical.dbg"; - const bool kDebugUTF16 = false; + constexpr uint32_t kPDBAge = 1; + constexpr uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME; + static constexpr char kDebugName[] = "statical.dbg"; + constexpr bool kDebugUTF16 = false; auto module_writer = base::WrapUnique(new MinidumpModuleWriter()); module_writer->SetName(kModuleName); @@ -419,13 +419,13 @@ TEST(MinidumpModuleWriter, OneModule_CodeViewUsesPDB20_MiscUsesUTF16) { MinidumpFileWriter minidump_file_writer; auto module_list_writer = base::WrapUnique(new MinidumpModuleListWriter()); - const char kModuleName[] = "dinosaur"; - const char kPDBName[] = "d1n05.pdb"; - const time_t kPDBTimestamp = 0x386d4380; - const uint32_t kPDBAge = 1; - const uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME; - const char kDebugName[] = "d1n05.dbg"; - const bool kDebugUTF16 = true; + static constexpr char kModuleName[] = "dinosaur"; + static constexpr char kPDBName[] = "d1n05.pdb"; + constexpr time_t kPDBTimestamp = 0x386d4380; + constexpr uint32_t kPDBAge = 1; + constexpr uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME; + static constexpr char kDebugName[] = "d1n05.dbg"; + constexpr bool kDebugUTF16 = true; auto module_writer = base::WrapUnique(new MinidumpModuleWriter()); module_writer->SetName(kModuleName); @@ -480,27 +480,27 @@ TEST(MinidumpModuleWriter, ThreeModules) { MinidumpFileWriter minidump_file_writer; auto module_list_writer = base::WrapUnique(new MinidumpModuleListWriter()); - const char kModuleName0[] = "main"; - const uint64_t kModuleBase0 = 0x100101000; - const uint32_t kModuleSize0 = 0xf000; - const char kPDBName0[] = "main"; - const uint8_t kPDBUUIDBytes0[16] = + static constexpr char kModuleName0[] = "main"; + constexpr uint64_t kModuleBase0 = 0x100101000; + constexpr uint32_t kModuleSize0 = 0xf000; + static constexpr char kPDBName0[] = "main"; + static constexpr uint8_t kPDBUUIDBytes0[16] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99}; UUID pdb_uuid_0; pdb_uuid_0.InitializeFromBytes(kPDBUUIDBytes0); - const uint32_t kPDBAge0 = 0; + constexpr uint32_t kPDBAge0 = 0; - const char kModuleName1[] = "ld.so"; - const uint64_t kModuleBase1 = 0x200202000; - const uint32_t kModuleSize1 = 0x1e000; + static constexpr char kModuleName1[] = "ld.so"; + constexpr uint64_t kModuleBase1 = 0x200202000; + constexpr uint32_t kModuleSize1 = 0x1e000; - const char kModuleName2[] = "libc.so"; - const uint64_t kModuleBase2 = 0x300303000; - const uint32_t kModuleSize2 = 0x2d000; - const char kPDBName2[] = "libc.so"; - const time_t kPDBTimestamp2 = 0x386d4380; - const uint32_t kPDBAge2 = 2; + static constexpr char kModuleName2[] = "libc.so"; + constexpr uint64_t kModuleBase2 = 0x300303000; + constexpr uint32_t kModuleSize2 = 0x2d000; + static constexpr char kPDBName2[] = "libc.so"; + constexpr time_t kPDBTimestamp2 = 0x386d4380; + constexpr uint32_t kPDBAge2 = 2; auto module_writer_0 = base::WrapUnique(new MinidumpModuleWriter()); module_writer_0->SetName(kModuleName0); @@ -668,7 +668,7 @@ TEST(MinidumpModuleWriter, InitializeFromSnapshot) { expect_modules[0].VersionInfo.dwFileType = VFT_APP; module_paths[0] = "/usr/bin/true"; module_pdbs[0] = "true"; - const uint8_t kUUIDBytes0[16] = + static constexpr uint8_t kUUIDBytes0[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; uuids[0].InitializeFromBytes(kUUIDBytes0); @@ -684,7 +684,7 @@ TEST(MinidumpModuleWriter, InitializeFromSnapshot) { expect_modules[1].VersionInfo.dwFileType = VFT_DLL; module_paths[1] = "/usr/lib/libSystem.B.dylib"; module_pdbs[1] = "libSystem.B.dylib.pdb"; - const uint8_t kUUIDBytes1[16] = + static constexpr uint8_t kUUIDBytes1[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; uuids[1].InitializeFromBytes(kUUIDBytes1); @@ -700,7 +700,7 @@ TEST(MinidumpModuleWriter, InitializeFromSnapshot) { expect_modules[2].VersionInfo.dwFileType = VFT_UNKNOWN; module_paths[2] = "/usr/lib/dyld"; module_pdbs[2] = "/usr/lib/dyld.pdb"; - const uint8_t kUUIDBytes2[16] = + static constexpr uint8_t kUUIDBytes2[16] = {0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0}; uuids[2].InitializeFromBytes(kUUIDBytes2); diff --git a/minidump/minidump_rva_list_writer_test.cc b/minidump/minidump_rva_list_writer_test.cc index 1d26b27b..dcd8c3b0 100644 --- a/minidump/minidump_rva_list_writer_test.cc +++ b/minidump/minidump_rva_list_writer_test.cc @@ -57,7 +57,7 @@ TEST(MinidumpRVAListWriter, Empty) { TEST(MinidumpRVAListWriter, OneChild) { TestMinidumpRVAListWriter list_writer; - const uint32_t kValue = 0; + constexpr uint32_t kValue = 0; list_writer.AddChild(kValue); StringFile string_file; @@ -76,7 +76,7 @@ TEST(MinidumpRVAListWriter, OneChild) { TEST(MinidumpRVAListWriter, ThreeChildren) { TestMinidumpRVAListWriter list_writer; - const uint32_t kValues[] = { 0x80000000, 0x55555555, 0x66006600 }; + static constexpr uint32_t kValues[] = {0x80000000, 0x55555555, 0x66006600}; list_writer.AddChild(kValues[0]); list_writer.AddChild(kValues[1]); diff --git a/minidump/minidump_string_writer_test.cc b/minidump/minidump_string_writer_test.cc index 6a1e5c64..25aedf13 100644 --- a/minidump/minidump_string_writer_test.cc +++ b/minidump/minidump_string_writer_test.cc @@ -49,7 +49,7 @@ TEST(MinidumpStringWriter, MinidumpUTF16StringWriter) { base::string16()); } - const struct { + static constexpr struct { size_t input_length; const char* input_string; size_t output_length; @@ -106,7 +106,7 @@ TEST(MinidumpStringWriter, MinidumpUTF16StringWriter) { TEST(MinidumpStringWriter, ConvertInvalidUTF8ToUTF16) { StringFile string_file; - const char* kTestData[] = { + static constexpr const char* kTestData[] = { "\200", // continuation byte "\300", // start byte followed by EOF "\310\177", // start byte without continuation @@ -160,7 +160,7 @@ TEST(MinidumpStringWriter, MinidumpUTF8StringWriter) { std::string()); } - const struct { + static constexpr struct { size_t length; const char* string; } kTestData[] = { diff --git a/minidump/minidump_system_info_writer_test.cc b/minidump/minidump_system_info_writer_test.cc index 0dd90559..6fc62b9d 100644 --- a/minidump/minidump_system_info_writer_test.cc +++ b/minidump/minidump_system_info_writer_test.cc @@ -46,10 +46,10 @@ void GetSystemInfoStream(const std::string& file_contents, const size_t kCSDVersionBytesWithNUL = kCSDVersionBytes + sizeof(tmp.Buffer[0]); - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kSystemInfoStreamOffset = + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kSystemInfoStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); - const size_t kCSDVersionOffset = + constexpr size_t kCSDVersionOffset = kSystemInfoStreamOffset + sizeof(MINIDUMP_SYSTEM_INFO); const size_t kFileSize = kCSDVersionOffset + sizeof(MINIDUMP_STRING) + kCSDVersionBytesWithNUL; @@ -120,21 +120,22 @@ TEST(MinidumpSystemInfoWriter, X86_Win) { MinidumpFileWriter minidump_file_writer; auto system_info_writer = base::WrapUnique(new MinidumpSystemInfoWriter()); - const MinidumpCPUArchitecture kCPUArchitecture = kMinidumpCPUArchitectureX86; - const uint16_t kCPULevel = 0x0010; - const uint16_t kCPURevision = 0x0602; - const uint8_t kCPUCount = 1; - const MinidumpOS kOS = kMinidumpOSWin32NT; - const MinidumpOSType kOSType = kMinidumpOSTypeWorkstation; - const uint32_t kOSVersionMajor = 6; - const uint32_t kOSVersionMinor = 1; - const uint32_t kOSVersionBuild = 7601; - const char kCSDVersion[] = "Service Pack 1"; - const uint16_t kSuiteMask = VER_SUITE_SINGLEUSERTS; - const char kCPUVendor[] = "AuthenticAMD"; - const uint32_t kCPUVersion = 0x00100f62; - const uint32_t kCPUFeatures = 0x078bfbff; - const uint32_t kAMDFeatures = 0xefd3fbff; + constexpr MinidumpCPUArchitecture kCPUArchitecture = + kMinidumpCPUArchitectureX86; + constexpr uint16_t kCPULevel = 0x0010; + constexpr uint16_t kCPURevision = 0x0602; + constexpr uint8_t kCPUCount = 1; + constexpr MinidumpOS kOS = kMinidumpOSWin32NT; + constexpr MinidumpOSType kOSType = kMinidumpOSTypeWorkstation; + constexpr uint32_t kOSVersionMajor = 6; + constexpr uint32_t kOSVersionMinor = 1; + constexpr uint32_t kOSVersionBuild = 7601; + static constexpr char kCSDVersion[] = "Service Pack 1"; + constexpr uint16_t kSuiteMask = VER_SUITE_SINGLEUSERTS; + static constexpr char kCPUVendor[] = "AuthenticAMD"; + constexpr uint32_t kCPUVersion = 0x00100f62; + constexpr uint32_t kCPUFeatures = 0x078bfbff; + constexpr uint32_t kAMDFeatures = 0xefd3fbff; uint32_t cpu_vendor_registers[3]; ASSERT_EQ(strlen(kCPUVendor), sizeof(cpu_vendor_registers)); @@ -190,18 +191,18 @@ TEST(MinidumpSystemInfoWriter, AMD64_Mac) { MinidumpFileWriter minidump_file_writer; auto system_info_writer = base::WrapUnique(new MinidumpSystemInfoWriter()); - const MinidumpCPUArchitecture kCPUArchitecture = + constexpr MinidumpCPUArchitecture kCPUArchitecture = kMinidumpCPUArchitectureAMD64; - const uint16_t kCPULevel = 0x0006; - const uint16_t kCPURevision = 0x3a09; - const uint8_t kCPUCount = 8; - const MinidumpOS kOS = kMinidumpOSMacOSX; - const MinidumpOSType kOSType = kMinidumpOSTypeWorkstation; - const uint32_t kOSVersionMajor = 10; - const uint32_t kOSVersionMinor = 9; - const uint32_t kOSVersionBuild = 4; - const char kCSDVersion[] = "13E28"; - const uint64_t kCPUFeatures[2] = {0x10427f4c, 0x00000000}; + constexpr uint16_t kCPULevel = 0x0006; + constexpr uint16_t kCPURevision = 0x3a09; + constexpr uint8_t kCPUCount = 8; + constexpr MinidumpOS kOS = kMinidumpOSMacOSX; + constexpr MinidumpOSType kOSType = kMinidumpOSTypeWorkstation; + constexpr uint32_t kOSVersionMajor = 10; + constexpr uint32_t kOSVersionMinor = 9; + constexpr uint32_t kOSVersionBuild = 4; + static constexpr char kCSDVersion[] = "13E28"; + static constexpr uint64_t kCPUFeatures[2] = {0x10427f4c, 0x00000000}; system_info_writer->SetCPUArchitecture(kCPUArchitecture); system_info_writer->SetCPULevelAndRevision(kCPULevel, kCPURevision); @@ -247,8 +248,9 @@ TEST(MinidumpSystemInfoWriter, X86_CPUVendorFromRegisters) { MinidumpFileWriter minidump_file_writer; auto system_info_writer = base::WrapUnique(new MinidumpSystemInfoWriter()); - const MinidumpCPUArchitecture kCPUArchitecture = kMinidumpCPUArchitectureX86; - const uint32_t kCPUVendor[] = {'uneG', 'Ieni', 'letn'}; + constexpr MinidumpCPUArchitecture kCPUArchitecture = + kMinidumpCPUArchitectureX86; + static constexpr uint32_t kCPUVendor[] = {'uneG', 'Ieni', 'letn'}; system_info_writer->SetCPUArchitecture(kCPUArchitecture); system_info_writer->SetCPUX86Vendor( @@ -277,9 +279,9 @@ TEST(MinidumpSystemInfoWriter, X86_CPUVendorFromRegisters) { TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_X86) { MINIDUMP_SYSTEM_INFO expect_system_info = {}; - const uint16_t kCPUFamily = 6; - const uint8_t kCPUModel = 70; - const uint8_t kCPUStepping = 1; + constexpr uint16_t kCPUFamily = 6; + constexpr uint8_t kCPUModel = 70; + constexpr uint8_t kCPUStepping = 1; const uint8_t kCPUBasicFamily = static_cast(std::min(kCPUFamily, static_cast(15))); @@ -291,12 +293,12 @@ TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_X86) { EXPECT_LE(kCPUStepping, 15); EXPECT_TRUE(kCPUBasicFamily == 6 || kCPUBasicFamily == 15 || kCPUModel <= 15); - const uint8_t kCPUBasicModel = kCPUModel & 0xf; - const uint8_t kCPUExtendedModel = kCPUModel >> 4; + constexpr uint8_t kCPUBasicModel = kCPUModel & 0xf; + constexpr uint8_t kCPUExtendedModel = kCPUModel >> 4; const uint32_t kCPUSignature = (kCPUExtendedFamily << 20) | (kCPUExtendedModel << 16) | (kCPUBasicFamily << 8) | (kCPUBasicModel << 4) | kCPUStepping; - const uint64_t kCPUX86Features = 0x7ffafbffbfebfbff; + constexpr uint64_t kCPUX86Features = 0x7ffafbffbfebfbff; expect_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureX86; expect_system_info.ProcessorLevel = kCPUFamily; expect_system_info.ProcessorRevision = (kCPUModel << 8) | kCPUStepping; @@ -313,8 +315,8 @@ TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_X86) { expect_system_info.Cpu.X86CpuInfo.VersionInformation = kCPUSignature; expect_system_info.Cpu.X86CpuInfo.FeatureInformation = kCPUX86Features & 0xffffffff; - const char kCPUVendor[] = "GenuineIntel"; - const char kOSVersionBuild[] = "13F34"; + static constexpr char kCPUVendor[] = "GenuineIntel"; + static constexpr char kOSVersionBuild[] = "13F34"; TestSystemSnapshot system_snapshot; system_snapshot.SetCPUArchitecture(kCPUArchitectureX86); @@ -379,9 +381,9 @@ TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_X86) { TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_AMD64) { MINIDUMP_SYSTEM_INFO expect_system_info = {}; - const uint8_t kCPUFamily = 6; - const uint8_t kCPUModel = 70; - const uint8_t kCPUStepping = 1; + constexpr uint8_t kCPUFamily = 6; + constexpr uint8_t kCPUModel = 70; + constexpr uint8_t kCPUStepping = 1; expect_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureAMD64; expect_system_info.ProcessorLevel = kCPUFamily; expect_system_info.ProcessorRevision = (kCPUModel << 8) | kCPUStepping; @@ -408,7 +410,7 @@ TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_AMD64) { (1 << PF_RDRAND_INSTRUCTION_AVAILABLE) | (UINT64_C(1) << PF_RDTSCP_INSTRUCTION_AVAILABLE); expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0; - const char kOSVersionBuild[] = "13F34"; + static constexpr char kOSVersionBuild[] = "13F34"; TestSystemSnapshot system_snapshot; system_snapshot.SetCPUArchitecture(kCPUArchitectureX86_64); diff --git a/minidump/minidump_thread_writer_test.cc b/minidump/minidump_thread_writer_test.cc index 0fa511ba..d4747ff3 100644 --- a/minidump/minidump_thread_writer_test.cc +++ b/minidump/minidump_thread_writer_test.cc @@ -46,7 +46,7 @@ namespace { void GetThreadListStream(const std::string& file_contents, const MINIDUMP_THREAD_LIST** thread_list, const MINIDUMP_MEMORY_LIST** memory_list) { - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); const uint32_t kExpectedStreams = memory_list ? 2 : 1; const size_t kThreadListStreamOffset = kDirectoryOffset + kExpectedStreams * sizeof(MINIDUMP_DIRECTORY); @@ -142,12 +142,12 @@ TEST(MinidumpThreadWriter, OneThread_x86_NoStack) { MinidumpFileWriter minidump_file_writer; auto thread_list_writer = base::WrapUnique(new MinidumpThreadListWriter()); - const uint32_t kThreadID = 0x11111111; - const uint32_t kSuspendCount = 1; - const uint32_t kPriorityClass = 0x20; - const uint32_t kPriority = 10; - const uint64_t kTEB = 0x55555555; - const uint32_t kSeed = 123; + constexpr uint32_t kThreadID = 0x11111111; + constexpr uint32_t kSuspendCount = 1; + constexpr uint32_t kPriorityClass = 0x20; + constexpr uint32_t kPriority = 10; + constexpr uint64_t kTEB = 0x55555555; + constexpr uint32_t kSeed = 123; auto thread_writer = base::WrapUnique(new MinidumpThreadWriter()); thread_writer->SetThreadID(kThreadID); @@ -201,15 +201,15 @@ TEST(MinidumpThreadWriter, OneThread_AMD64_Stack) { MinidumpFileWriter minidump_file_writer; auto thread_list_writer = base::WrapUnique(new MinidumpThreadListWriter()); - const uint32_t kThreadID = 0x22222222; - const uint32_t kSuspendCount = 2; - const uint32_t kPriorityClass = 0x30; - const uint32_t kPriority = 20; - const uint64_t kTEB = 0x5555555555555555; - const uint64_t kMemoryBase = 0x765432100000; - const size_t kMemorySize = 32; - const uint8_t kMemoryValue = 99; - const uint32_t kSeed = 456; + constexpr uint32_t kThreadID = 0x22222222; + constexpr uint32_t kSuspendCount = 2; + constexpr uint32_t kPriorityClass = 0x30; + constexpr uint32_t kPriority = 20; + constexpr uint64_t kTEB = 0x5555555555555555; + constexpr uint64_t kMemoryBase = 0x765432100000; + constexpr size_t kMemorySize = 32; + constexpr uint8_t kMemoryValue = 99; + constexpr uint32_t kSeed = 456; auto thread_writer = base::WrapUnique(new MinidumpThreadWriter()); thread_writer->SetThreadID(kThreadID); @@ -282,15 +282,15 @@ TEST(MinidumpThreadWriter, ThreeThreads_x86_MemoryList) { auto memory_list_writer = base::WrapUnique(new MinidumpMemoryListWriter()); thread_list_writer->SetMemoryListWriter(memory_list_writer.get()); - const uint32_t kThreadID0 = 1111111; - const uint32_t kSuspendCount0 = 111111; - const uint32_t kPriorityClass0 = 11111; - const uint32_t kPriority0 = 1111; - const uint64_t kTEB0 = 111; - const uint64_t kMemoryBase0 = 0x1110; - const size_t kMemorySize0 = 16; - const uint8_t kMemoryValue0 = 11; - const uint32_t kSeed0 = 1; + constexpr uint32_t kThreadID0 = 1111111; + constexpr uint32_t kSuspendCount0 = 111111; + constexpr uint32_t kPriorityClass0 = 11111; + constexpr uint32_t kPriority0 = 1111; + constexpr uint64_t kTEB0 = 111; + constexpr uint64_t kMemoryBase0 = 0x1110; + constexpr size_t kMemorySize0 = 16; + constexpr uint8_t kMemoryValue0 = 11; + constexpr uint32_t kSeed0 = 1; auto thread_writer_0 = base::WrapUnique(new MinidumpThreadWriter()); thread_writer_0->SetThreadID(kThreadID0); @@ -309,15 +309,15 @@ TEST(MinidumpThreadWriter, ThreeThreads_x86_MemoryList) { thread_list_writer->AddThread(std::move(thread_writer_0)); - const uint32_t kThreadID1 = 2222222; - const uint32_t kSuspendCount1 = 222222; - const uint32_t kPriorityClass1 = 22222; - const uint32_t kPriority1 = 2222; - const uint64_t kTEB1 = 222; - const uint64_t kMemoryBase1 = 0x2220; - const size_t kMemorySize1 = 32; - const uint8_t kMemoryValue1 = 22; - const uint32_t kSeed1 = 2; + constexpr uint32_t kThreadID1 = 2222222; + constexpr uint32_t kSuspendCount1 = 222222; + constexpr uint32_t kPriorityClass1 = 22222; + constexpr uint32_t kPriority1 = 2222; + constexpr uint64_t kTEB1 = 222; + constexpr uint64_t kMemoryBase1 = 0x2220; + constexpr size_t kMemorySize1 = 32; + constexpr uint8_t kMemoryValue1 = 22; + constexpr uint32_t kSeed1 = 2; auto thread_writer_1 = base::WrapUnique(new MinidumpThreadWriter()); thread_writer_1->SetThreadID(kThreadID1); @@ -336,15 +336,15 @@ TEST(MinidumpThreadWriter, ThreeThreads_x86_MemoryList) { thread_list_writer->AddThread(std::move(thread_writer_1)); - const uint32_t kThreadID2 = 3333333; - const uint32_t kSuspendCount2 = 333333; - const uint32_t kPriorityClass2 = 33333; - const uint32_t kPriority2 = 3333; - const uint64_t kTEB2 = 333; - const uint64_t kMemoryBase2 = 0x3330; - const size_t kMemorySize2 = 48; - const uint8_t kMemoryValue2 = 33; - const uint32_t kSeed2 = 3; + constexpr uint32_t kThreadID2 = 3333333; + constexpr uint32_t kSuspendCount2 = 333333; + constexpr uint32_t kPriorityClass2 = 33333; + constexpr uint32_t kPriority2 = 3333; + constexpr uint64_t kTEB2 = 333; + constexpr uint64_t kMemoryBase2 = 0x3330; + constexpr size_t kMemorySize2 = 48; + constexpr uint8_t kMemoryValue2 = 33; + constexpr uint32_t kSeed2 = 3; auto thread_writer_2 = base::WrapUnique(new MinidumpThreadWriter()); thread_writer_2->SetThreadID(kThreadID2); @@ -533,7 +533,7 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) { uint32_t context_seeds[arraysize(expect_threads)] = {}; MINIDUMP_MEMORY_DESCRIPTOR tebs[arraysize(expect_threads)] = {}; - const size_t kTebSize = 1024; + constexpr size_t kTebSize = 1024; expect_threads[0].ThreadId = 1; expect_threads[0].SuspendCount = 2; diff --git a/minidump/minidump_unloaded_module_writer_test.cc b/minidump/minidump_unloaded_module_writer_test.cc index a6de044f..61c7a745 100644 --- a/minidump/minidump_unloaded_module_writer_test.cc +++ b/minidump/minidump_unloaded_module_writer_test.cc @@ -46,10 +46,10 @@ void ExpectUnloadedModule(const MINIDUMP_UNLOADED_MODULE* expected, void GetUnloadedModuleListStream( const std::string& file_contents, const MINIDUMP_UNLOADED_MODULE_LIST** unloaded_module_list) { - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kUnloadedModuleListStreamOffset = + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kUnloadedModuleListStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); - const size_t kUnloadedModulesOffset = + constexpr size_t kUnloadedModulesOffset = kUnloadedModuleListStreamOffset + sizeof(MINIDUMP_UNLOADED_MODULE_LIST); ASSERT_GE(file_contents.size(), kUnloadedModulesOffset); @@ -74,7 +74,7 @@ TEST(MinidumpUnloadedModuleWriter, EmptyModule) { auto unloaded_module_list_writer = base::WrapUnique(new MinidumpUnloadedModuleListWriter()); - const char kModuleName[] = "test_dll"; + static constexpr char kModuleName[] = "test_dll"; auto unloaded_module_writer = base::WrapUnique(new MinidumpUnloadedModuleWriter()); @@ -113,11 +113,11 @@ TEST(MinidumpUnloadedModuleWriter, OneModule) { auto unloaded_module_list_writer = base::WrapUnique(new MinidumpUnloadedModuleListWriter()); - const char kModuleName[] = "statically_linked"; - const uint64_t kModuleBase = 0x10da69000; - const uint32_t kModuleSize = 0x1000; - const uint32_t kChecksum = 0x76543210; - const time_t kTimestamp = 0x386d4380; + static constexpr char kModuleName[] = "statically_linked"; + constexpr uint64_t kModuleBase = 0x10da69000; + constexpr uint32_t kModuleSize = 0x1000; + constexpr uint32_t kChecksum = 0x76543210; + constexpr time_t kTimestamp = 0x386d4380; auto unloaded_module_writer = base::WrapUnique(new MinidumpUnloadedModuleWriter()); diff --git a/minidump/minidump_user_stream_writer_test.cc b/minidump/minidump_user_stream_writer_test.cc index e6627e62..86723046 100644 --- a/minidump/minidump_user_stream_writer_test.cc +++ b/minidump/minidump_user_stream_writer_test.cc @@ -35,8 +35,8 @@ void GetUserStream(const std::string& file_contents, MINIDUMP_LOCATION_DESCRIPTOR* user_stream_location, uint32_t stream_type, size_t stream_size) { - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kUserStreamOffset = + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kUserStreamOffset = kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); const MINIDUMP_DIRECTORY* directory; @@ -45,7 +45,7 @@ void GetUserStream(const std::string& file_contents, ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); ASSERT_TRUE(directory); - const size_t kDirectoryIndex = 0; + constexpr size_t kDirectoryIndex = 0; ASSERT_EQ(directory[kDirectoryIndex].StreamType, stream_type); EXPECT_EQ(directory[kDirectoryIndex].Location.Rva, kUserStreamOffset); @@ -100,7 +100,7 @@ TEST(MinidumpUserStreamWriter, InitializeFromSnapshotOneStream) { TestMemorySnapshot* test_data = new TestMemorySnapshot(); test_data->SetAddress(97865); - const size_t kStreamSize = 128; + constexpr size_t kStreamSize = 128; test_data->SetSize(kStreamSize); test_data->SetValue('c'); auto stream = @@ -125,7 +125,7 @@ TEST(MinidumpUserStreamWriter, InitializeFromSnapshotOneStream) { TEST(MinidumpUserStreamWriter, InitializeFromBufferOneStream) { MinidumpFileWriter minidump_file_writer; - const size_t kStreamSize = 128; + constexpr size_t kStreamSize = 128; std::vector data(kStreamSize, 'c'); auto data_source = base::WrapUnique(new test::BufferExtensionStreamDataSource( kTestStreamId, &data[0], data.size())); diff --git a/minidump/minidump_writable.cc b/minidump/minidump_writable.cc index 8b739060..d2b58f5b 100644 --- a/minidump/minidump_writable.cc +++ b/minidump/minidump_writable.cc @@ -16,15 +16,13 @@ #include -#include - #include "base/logging.h" #include "util/file/file_writer.h" #include "util/numeric/safe_assignment.h" namespace { -const size_t kMaximumAlignment = 16; +constexpr size_t kMaximumAlignment = 16; } // namespace @@ -82,9 +80,6 @@ void MinidumpWritable::RegisterLocationDescriptor( registered_location_descriptors_.push_back(location_descriptor); } -const size_t MinidumpWritable::kInvalidSize = - std::numeric_limits::max(); - MinidumpWritable::MinidumpWritable() : registered_rvas_(), registered_location_descriptors_(), @@ -248,7 +243,7 @@ bool MinidumpWritable::WritePaddingAndObject(FileWriterInterface* file_writer) { // The number of elements in kZeroes must be at least one less than the // maximum Alignment() ever encountered. - const uint8_t kZeroes[kMaximumAlignment - 1] = {}; + static constexpr uint8_t kZeroes[kMaximumAlignment - 1] = {}; DCHECK_LE(leading_pad_bytes_, arraysize(kZeroes)); if (leading_pad_bytes_) { diff --git a/minidump/minidump_writable.h b/minidump/minidump_writable.h index 98273751..3518e6a9 100644 --- a/minidump/minidump_writable.h +++ b/minidump/minidump_writable.h @@ -19,6 +19,7 @@ #include #include +#include #include #include "base/macros.h" @@ -134,7 +135,7 @@ class MinidumpWritable { //! \brief A size value used to signal failure by methods that return //! `size_t`. - static const size_t kInvalidSize; + static constexpr size_t kInvalidSize = std::numeric_limits::max(); MinidumpWritable(); diff --git a/minidump/test/minidump_memory_writer_test_util.cc b/minidump/test/minidump_memory_writer_test_util.cc index a2fa8b63..03a0e18a 100644 --- a/minidump/test/minidump_memory_writer_test_util.cc +++ b/minidump/test/minidump_memory_writer_test_util.cc @@ -37,7 +37,7 @@ void ExpectMinidumpMemoryDescriptor( EXPECT_EQ(observed->StartOfMemoryRange, expected->StartOfMemoryRange); EXPECT_EQ(observed->Memory.DataSize, expected->Memory.DataSize); if (expected->Memory.Rva != 0) { - const uint32_t kMemoryAlignment = 16; + constexpr uint32_t kMemoryAlignment = 16; EXPECT_EQ(observed->Memory.Rva, (expected->Memory.Rva + kMemoryAlignment - 1) & ~(kMemoryAlignment - 1)); diff --git a/snapshot/capture_memory.cc b/snapshot/capture_memory.cc index eba6729c..c860285a 100644 --- a/snapshot/capture_memory.cc +++ b/snapshot/capture_memory.cc @@ -28,7 +28,7 @@ namespace { void MaybeCaptureMemoryAround(CaptureMemory::Delegate* delegate, uint64_t address) { - const uint64_t non_address_offset = 0x10000; + constexpr uint64_t non_address_offset = 0x10000; if (address < non_address_offset) return; @@ -38,9 +38,9 @@ void MaybeCaptureMemoryAround(CaptureMemory::Delegate* delegate, if (address > max_address - non_address_offset) return; - const uint64_t kRegisterByteOffset = 128; + constexpr uint64_t kRegisterByteOffset = 128; const uint64_t target = address - kRegisterByteOffset; - const uint64_t size = 512; + constexpr uint64_t size = 512; static_assert(kRegisterByteOffset <= size / 2, "negative offset too large"); auto ranges = diff --git a/snapshot/linux/cpu_context_linux.cc b/snapshot/linux/cpu_context_linux.cc new file mode 100644 index 00000000..d4b09a70 --- /dev/null +++ b/snapshot/linux/cpu_context_linux.cc @@ -0,0 +1,108 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/linux/cpu_context_linux.h" + +#include +#include + +#include "base/logging.h" + +namespace crashpad { +namespace internal { + +#if defined(ARCH_CPU_X86_FAMILY) + +void InitializeCPUContextX86(const ThreadContext::t32_t& thread_context, + const FloatContext::f32_t& float_context, + CPUContextX86* context) { + context->eax = thread_context.eax; + context->ebx = thread_context.ebx; + context->ecx = thread_context.ecx; + context->edx = thread_context.edx; + context->edi = thread_context.edi; + context->esi = thread_context.esi; + context->ebp = thread_context.ebp; + context->esp = thread_context.esp; + context->eip = thread_context.eip; + context->eflags = thread_context.eflags; + context->cs = thread_context.xcs; + context->ds = thread_context.xds; + context->es = thread_context.xes; + context->fs = thread_context.xfs; + context->gs = thread_context.xgs; + context->ss = thread_context.xss; + + static_assert(sizeof(context->fxsave) == sizeof(float_context.fxsave), + "fxsave size mismatch"); + memcpy(&context->fxsave, &float_context.fxsave, sizeof(context->fxsave)); + + // TODO(jperaza): debug registers + context->dr0 = 0; + context->dr1 = 0; + context->dr2 = 0; + context->dr3 = 0; + context->dr4 = 0; + context->dr5 = 0; + context->dr6 = 0; + context->dr7 = 0; + +} + +void InitializeCPUContextX86_64(const ThreadContext::t64_t& thread_context, + const FloatContext::f64_t& float_context, + CPUContextX86_64* context) { + context->rax = thread_context.rax; + context->rbx = thread_context.rbx; + context->rcx = thread_context.rcx; + context->rdx = thread_context.rdx; + context->rdi = thread_context.rdi; + context->rsi = thread_context.rsi; + context->rbp = thread_context.rbp; + context->rsp = thread_context.rsp; + context->r8 = thread_context.r8; + context->r9 = thread_context.r9; + context->r10 = thread_context.r10; + context->r11 = thread_context.r11; + context->r12 = thread_context.r12; + context->r13 = thread_context.r13; + context->r14 = thread_context.r14; + context->r15 = thread_context.r15; + context->rip = thread_context.rip; + context->rflags = thread_context.eflags; + context->cs = thread_context.cs; + context->fs = thread_context.fs; + context->gs = thread_context.gs; + + static_assert(sizeof(context->fxsave) == sizeof(float_context.fxsave), + "fxsave size mismatch"); + memcpy(&context->fxsave, &float_context.fxsave, sizeof(context->fxsave)); + + // TODO(jperaza): debug registers. + context->dr0 = 0; + context->dr1 = 0; + context->dr2 = 0; + context->dr3 = 0; + context->dr4 = 0; + context->dr5 = 0; + context->dr6 = 0; + context->dr7 = 0; +} + +#else +#error Port. +#endif // ARCH_CPU_X86_FAMILY || DOXYGEN + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/linux/cpu_context_linux.h b/snapshot/linux/cpu_context_linux.h new file mode 100644 index 00000000..0ce5d5f4 --- /dev/null +++ b/snapshot/linux/cpu_context_linux.h @@ -0,0 +1,53 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_CPU_CONTEXT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_CPU_CONTEXT_LINUX_H_ + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "util/linux/thread_info.h" + +namespace crashpad { +namespace internal { + +#if defined(ARCH_CPU_X86_FAMILY) || DOXYGEN + +//! \brief Initializes a CPUContextX86 structure from native context structures +//! on Linux. +//! +//! \param[in] thread_context The native thread context. +//! \param[in] float_context The native float context. +//! \param[out] context The CPUContextX86 structure to initialize. +void InitializeCPUContextX86(const ThreadContext::t32_t& thread_context, + const FloatContext::f32_t& float_context, + CPUContextX86* context); + +//! \brief Initializes a CPUContextX86_64 structure from native context +//! structures on Linux. +//! +//! \param[in] thread_context The native thread context. +//! \param[in] float_context The native float context. +//! \param[out] context The CPUContextX86_64 structure to initialize. +void InitializeCPUContextX86_64(const ThreadContext::t64_t& thread_context, + const FloatContext::f64_t& float_context, + CPUContextX86_64* context); +#else +#error Port. // TODO(jperaza): ARM +#endif // ARCH_CPU_X86_FAMILY || DOXYGEN + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_CPU_CONTEXT_LINUX_H_ diff --git a/snapshot/linux/debug_rendezvous.cc b/snapshot/linux/debug_rendezvous.cc new file mode 100644 index 00000000..e47fc0c6 --- /dev/null +++ b/snapshot/linux/debug_rendezvous.cc @@ -0,0 +1,143 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/linux/debug_rendezvous.h" + +#include + +#include + +#include "base/logging.h" + +namespace crashpad { + +namespace { + +struct Traits32 { + using Integer = int32_t; + using Address = uint32_t; +}; + +struct Traits64 { + using Integer = int64_t; + using Address = uint64_t; +}; + +template +struct DebugRendezvousSpecific { + typename Traits::Integer r_version; + typename Traits::Address r_map; + typename Traits::Address r_brk; + typename Traits::Integer r_state; + typename Traits::Address r_ldbase; +}; + +template +struct LinkEntrySpecific { + typename Traits::Address l_addr; + typename Traits::Address l_name; + typename Traits::Address l_ld; + typename Traits::Address l_next; + typename Traits::Address l_prev; +}; + +template +bool ReadLinkEntry(const ProcessMemoryRange& memory, + LinuxVMAddress* address, + DebugRendezvous::LinkEntry* entry_out) { + LinkEntrySpecific entry; + if (!memory.Read(*address, sizeof(entry), &entry)) { + return false; + } + + std::string name; + if (!memory.ReadCStringSizeLimited(entry.l_name, 4096, &name)) { + return false; + } + + entry_out->load_bias = entry.l_addr; + entry_out->dynamic_array = entry.l_ld; + entry_out->name.swap(name); + + *address = entry.l_next; + return true; +} + +} // namespace + +DebugRendezvous::LinkEntry::LinkEntry() + : name(), load_bias(0), dynamic_array(0) {} + +DebugRendezvous::DebugRendezvous() + : modules_(), executable_(), initialized_() {} + +DebugRendezvous::~DebugRendezvous() {} + +bool DebugRendezvous::Initialize(const ProcessMemoryRange& memory, + LinuxVMAddress address) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + if (!(memory.Is64Bit() ? InitializeSpecific(memory, address) + : InitializeSpecific(memory, address))) { + return false; + } + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const DebugRendezvous::LinkEntry* DebugRendezvous::Executable() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &executable_; +} + +const std::vector& DebugRendezvous::Modules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return modules_; +} + +template +bool DebugRendezvous::InitializeSpecific(const ProcessMemoryRange& memory, + LinuxVMAddress address) { + DebugRendezvousSpecific debug; + if (!memory.Read(address, sizeof(debug), &debug)) { + return false; + } + if (debug.r_version != 1) { + LOG(ERROR) << "unexpected version " << debug.r_version; + return false; + } + + LinuxVMAddress link_entry_address = debug.r_map; + if (!ReadLinkEntry(memory, &link_entry_address, &executable_)) { + return false; + } + + std::set visited; + while (link_entry_address) { + if (!visited.insert(link_entry_address).second) { + LOG(ERROR) << "cycle at address 0x" << std::hex << link_entry_address; + return false; + } + + LinkEntry entry; + if (!ReadLinkEntry(memory, &link_entry_address, &entry)) { + return false; + } + modules_.push_back(entry); + } + + return true; +} + +} // namespace crashpad diff --git a/snapshot/linux/debug_rendezvous.h b/snapshot/linux/debug_rendezvous.h new file mode 100644 index 00000000..d9b90f68 --- /dev/null +++ b/snapshot/linux/debug_rendezvous.h @@ -0,0 +1,88 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_DEBUG_RENDEZVOUS_H_ +#define CRASHPAD_SNAPSHOT_LINUX_DEBUG_RENDEZVOUS_H_ + +#include +#include + +#include "base/macros.h" +#include "util/linux/address_types.h" +#include "util/linux/process_memory_range.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +//! \brief Reads an `r_debug` struct defined in `` via +//! ProcessMemoryRange. +class DebugRendezvous { + public: + //! \brief An entry in the dynamic linker's list of loaded objects. + //! + //! All of these values should be checked before use. Whether and how they are + //! populated may vary by dynamic linker. + struct LinkEntry { + LinkEntry(); + + //! \brief A filename identifying the object. + std::string name; + + //! \brief The difference between the preferred load address in the ELF file + //! and the actual loaded address in memory. + LinuxVMOffset load_bias; + + //! \brief The address of the dynamic array for this object. + LinuxVMAddress dynamic_array; + }; + + DebugRendezvous(); + ~DebugRendezvous(); + + //! \brief Initializes this object by reading an `r_debug` struct from a + //! target process. + //! + //! This method must be called successfully prior to calling any other method + //! in this class. + //! + //! \param[in] memory A memory reader for the remote process. + //! \param[in] address The address of an `r_debug` struct in the remote + //! process. + //! \return `true` on success. `false` on failure with a message logged. + bool Initialize(const ProcessMemoryRange& memory, LinuxVMAddress address); + + //! \brief Returns the LinkEntry for the main executable. + const LinkEntry* Executable() const; + + //! \brief Returns a vector of modules found in the link map. + //! + //! This list excludes the entry for the executable and may include entries + //! for the VDSO and loader. + const std::vector& Modules() const; + + private: + template + bool InitializeSpecific(const ProcessMemoryRange& memory, + LinuxVMAddress address); + + std::vector modules_; + LinkEntry executable_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(DebugRendezvous); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_DEBUG_RENDEZVOUS_H_ diff --git a/snapshot/linux/debug_rendezvous_test.cc b/snapshot/linux/debug_rendezvous_test.cc new file mode 100644 index 00000000..5d50f8a4 --- /dev/null +++ b/snapshot/linux/debug_rendezvous_test.cc @@ -0,0 +1,220 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/linux/debug_rendezvous.h" + +#include +#include + +#include + +#include "base/format_macros.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "snapshot/linux/elf_image_reader.h" +#include "test/multiprocess.h" +#include "util/linux/address_types.h" +#include "util/linux/auxiliary_vector.h" +#include "util/linux/memory_map.h" +#include "util/linux/process_memory.h" +#include "util/linux/process_memory_range.h" + +#if defined(OS_ANDROID) +#include +#endif + +namespace crashpad { +namespace test { +namespace { + +#if defined(OS_ANDROID) +int AndroidRuntimeAPI() { + char api_string[PROP_VALUE_MAX]; + int length = __system_property_get("ro.build.version.sdk", api_string); + if (length <= 0) { + return -1; + } + + int api_level; + bool success = + base::StringToInt(base::StringPiece(api_string, length), &api_level); + return success ? api_level : -1; +} +#endif // OS_ANDROID + +void TestAgainstTarget(pid_t pid, bool is_64_bit) { + // Use ElfImageReader on the main executable which can tell us the debug + // address. glibc declares the symbol _r_debug in link.h which we can use to + // get the address, but Android does not. + AuxiliaryVector aux; + ASSERT_TRUE(aux.Initialize(pid, is_64_bit)); + + LinuxVMAddress phdrs; + ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs)); + + MemoryMap mappings; + ASSERT_TRUE(mappings.Initialize(pid)); + + const MemoryMap::Mapping* phdr_mapping = mappings.FindMapping(phdrs); + ASSERT_TRUE(phdr_mapping); + const MemoryMap::Mapping* exe_mapping = + mappings.FindFileMmapStart(*phdr_mapping); + LinuxVMAddress elf_address = exe_mapping->range.Base(); + + ProcessMemory memory; + ASSERT_TRUE(memory.Initialize(pid)); + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); + + ElfImageReader exe_reader; + ASSERT_TRUE(exe_reader.Initialize(range, elf_address)); + LinuxVMAddress debug_address; + ASSERT_TRUE(exe_reader.GetDebugAddress(&debug_address)); + + // start the actual tests + DebugRendezvous debug; + ASSERT_TRUE(debug.Initialize(range, debug_address)); + +#if defined(OS_ANDROID) + const int android_runtime_api = AndroidRuntimeAPI(); + ASSERT_GE(android_runtime_api, 1); + + EXPECT_NE(debug.Executable()->name.find("crashpad_snapshot_test"), + std::string::npos); + + // Android's loader never sets the dynamic array for the executable. + EXPECT_EQ(debug.Executable()->dynamic_array, 0u); +#else + // glibc's loader implements most of the tested features that Android's was + // missing but has since gained. + const int android_runtime_api = std::numeric_limits::max(); + + // glibc's loader does not set the name for the executable. + EXPECT_TRUE(debug.Executable()->name.empty()); + CheckedLinuxAddressRange exe_range( + is_64_bit, exe_reader.Address(), exe_reader.Size()); + EXPECT_TRUE(exe_range.ContainsValue(debug.Executable()->dynamic_array)); +#endif // OS_ANDROID + + // Android's loader doesn't set the load bias until Android 4.3 (API 18). + if (android_runtime_api >= 18) { + EXPECT_EQ(debug.Executable()->load_bias, exe_reader.GetLoadBias()); + } else { + EXPECT_EQ(debug.Executable()->load_bias, 0); + } + + for (const DebugRendezvous::LinkEntry& module : debug.Modules()) { + SCOPED_TRACE(base::StringPrintf("name %s, load_bias 0x%" PRIx64 + ", dynamic_array 0x%" PRIx64, + module.name.c_str(), + module.load_bias, + module.dynamic_array)); + const bool is_android_loader = (module.name == "/system/bin/linker" || + module.name == "/system/bin/linker64"); + + // Android's loader doesn't set its own dynamic array until Android 4.2 + // (API 17). + if (is_android_loader && android_runtime_api < 17) { + EXPECT_EQ(module.dynamic_array, 0u); + EXPECT_EQ(module.load_bias, 0); + continue; + } + + ASSERT_TRUE(module.dynamic_array); + const MemoryMap::Mapping* dyn_mapping = + mappings.FindMapping(module.dynamic_array); + ASSERT_TRUE(dyn_mapping); + + const MemoryMap::Mapping* module_mapping = + mappings.FindFileMmapStart(*dyn_mapping); + ASSERT_TRUE(module_mapping); + +#if defined(OS_ANDROID) + EXPECT_FALSE(module.name.empty()); +#else + // glibc's loader doesn't set the name in the link map for the vdso. + EXPECT_PRED4( + [](const std::string mapping_name, + int device, + int inode, + const std::string& module_name) { + return module_name.empty() == + (device == 0 && inode == 0 && mapping_name == "[vdso]"); + }, + module_mapping->name, + module_mapping->device, + module_mapping->inode, + module.name); +#endif // OS_ANDROID + + ElfImageReader module_reader; + ASSERT_TRUE(module_reader.Initialize(range, module_mapping->range.Base())); + + // Android's loader stops setting its own load bias after Android 4.4.4 + // (API 20) until Android 6.0 (API 23). + if (is_android_loader && android_runtime_api > 20 && + android_runtime_api < 23) { + EXPECT_EQ(module.load_bias, 0); + } else { + EXPECT_EQ(module.load_bias, module_reader.GetLoadBias()); + } + + CheckedLinuxAddressRange module_range( + is_64_bit, module_reader.Address(), module_reader.Size()); + EXPECT_TRUE(module_range.ContainsValue(module.dynamic_array)); + } +} + +TEST(DebugRendezvous, Self) { +#if defined(ARCH_CPU_64_BITS) + constexpr bool is_64_bit = true; +#else + constexpr bool is_64_bit = false; +#endif + + TestAgainstTarget(getpid(), is_64_bit); +} + +class ChildTest : public Multiprocess { + public: + ChildTest() {} + ~ChildTest() {} + + private: + void MultiprocessParent() { +#if defined(ARCH_CPU_64_BITS) + constexpr bool is_64_bit = true; +#else + constexpr bool is_64_bit = false; +#endif + + TestAgainstTarget(ChildPID(), is_64_bit); + } + + void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } + + DISALLOW_COPY_AND_ASSIGN(ChildTest); +}; + +TEST(DebugRendezvous, Child) { + ChildTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/snapshot/linux/elf_dynamic_array_reader.cc b/snapshot/linux/elf_dynamic_array_reader.cc new file mode 100644 index 00000000..c4a7e616 --- /dev/null +++ b/snapshot/linux/elf_dynamic_array_reader.cc @@ -0,0 +1,75 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/linux/elf_dynamic_array_reader.h" + +#include + +#include "util/stdlib/map_insert.h" + +namespace crashpad { + +namespace { + +template +bool Read(const ProcessMemoryRange& memory, + LinuxVMAddress address, + LinuxVMSize size, + std::map* values) { + std::map local_values; + + while (size > 0) { + DynType entry; + if (!memory.Read(address, sizeof(entry), &entry)) { + return false; + } + size -= sizeof(entry); + address += sizeof(entry); + + switch (entry.d_tag) { + case DT_NULL: + values->swap(local_values); + if (size != 0) { + LOG(WARNING) << size << " trailing bytes not read"; + } + return true; + case DT_NEEDED: + // Skip these entries for now. + break; + default: + if (!MapInsertOrReplace( + &local_values, entry.d_tag, entry.d_un.d_val, nullptr)) { + LOG(ERROR) << "duplicate dynamic array entry"; + return false; + } + } + } + LOG(ERROR) << "missing DT_NULL"; + return false; +} + +} // namespace + +ElfDynamicArrayReader::ElfDynamicArrayReader() : values_() {} + +ElfDynamicArrayReader::~ElfDynamicArrayReader() {} + +bool ElfDynamicArrayReader::Initialize(const ProcessMemoryRange& memory, + LinuxVMAddress address, + LinuxVMSize size) { + return memory.Is64Bit() ? Read(memory, address, size, &values_) + : Read(memory, address, size, &values_); +} + +} // namespace crashpad diff --git a/snapshot/linux/elf_dynamic_array_reader.h b/snapshot/linux/elf_dynamic_array_reader.h new file mode 100644 index 00000000..df06113c --- /dev/null +++ b/snapshot/linux/elf_dynamic_array_reader.h @@ -0,0 +1,73 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_ELF_DYNAMIC_ARRAY_READER_H_ +#define CRASHPAD_SNAPSHOT_LINUX_ELF_DYNAMIC_ARRAY_READER_H_ + +#include + +#include + +#include "base/logging.h" +#include "base/macros.h" +#include "util/linux/address_types.h" +#include "util/linux/process_memory_range.h" +#include "util/misc/reinterpret_bytes.h" + +namespace crashpad { + +//! \brief A reader for ELF dynamic arrays mapped into another process. +class ElfDynamicArrayReader { + public: + ElfDynamicArrayReader(); + ~ElfDynamicArrayReader(); + + //! \brief Initializes the reader. + //! + //! This method must be called once on an object and must be successfully + //! called before any other method in this class may be called. + //! + //! \param[in] memory A memory reader for the remote process. + //! \param[in] address The address in the remote process' address space where + //! the ELF dynamic table is loaded. + //! \param[in] size The maximum number of bytes to read. + bool Initialize(const ProcessMemoryRange& memory, + LinuxVMAddress address, + LinuxVMSize size); + + //! \brief Retrieve a value from the array. + //! + //! \param[in] tag Specifies which value should be retrieved. The possible + //! values for this parameter are the `DT_*` values from ``. + //! \param[out] value The value, casted to an appropriate type, if found. + //! \return `true` if the value is found. + template + bool GetValue(uint64_t tag, V* value) { + auto iter = values_.find(tag); + if (iter == values_.end()) { + LOG(ERROR) << "tag not found"; + return false; + } + return ReinterpretBytes(iter->second, value); + } + + private: + std::map values_; + + DISALLOW_COPY_AND_ASSIGN(ElfDynamicArrayReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_ELF_DYNAMIC_ARRAY_READER_H_ diff --git a/snapshot/linux/elf_image_reader.cc b/snapshot/linux/elf_image_reader.cc new file mode 100644 index 00000000..fbc0667b --- /dev/null +++ b/snapshot/linux/elf_image_reader.cc @@ -0,0 +1,471 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/linux/elf_image_reader.h" + +#include + +#include +#include + +#include "base/logging.h" +#include "build/build_config.h" + +namespace crashpad { + +class ElfImageReader::ProgramHeaderTable { + public: + virtual ~ProgramHeaderTable() {} + + virtual bool VerifyLoadSegments() const = 0; + virtual size_t Size() const = 0; + virtual bool GetDynamicSegment(LinuxVMAddress* address, + LinuxVMSize* size) const = 0; + virtual bool GetPreferredElfHeaderAddress(LinuxVMAddress* address) const = 0; + virtual bool GetPreferredLoadedMemoryRange(LinuxVMAddress* address, + LinuxVMSize* size) const = 0; + + protected: + ProgramHeaderTable() {} +}; + +template +class ElfImageReader::ProgramHeaderTableSpecific + : public ElfImageReader::ProgramHeaderTable { + public: + ProgramHeaderTableSpecific() {} + ~ProgramHeaderTableSpecific() {} + + bool Initialize(const ProcessMemoryRange& memory, + LinuxVMAddress address, + LinuxVMSize num_segments) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + table_.resize(num_segments); + if (!memory.Read(address, sizeof(PhdrType) * num_segments, table_.data())) { + return false; + } + + if (!VerifyLoadSegments()) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; + } + + bool VerifyLoadSegments() const override { + constexpr bool is_64_bit = std::is_same::value; + LinuxVMAddress last_vaddr; + bool load_found = false; + for (const auto& header : table_) { + if (header.p_type == PT_LOAD) { + CheckedLinuxAddressRange load_range( + is_64_bit, header.p_vaddr, header.p_memsz); + + if (!load_range.IsValid()) { + LOG(ERROR) << "bad load range"; + return false; + } + + if (load_found && header.p_vaddr <= last_vaddr) { + LOG(ERROR) << "out of order load segments"; + return false; + } + load_found = true; + last_vaddr = header.p_vaddr; + } + } + return true; + } + + size_t Size() const override { return sizeof(PhdrType) * table_.size(); } + + bool GetPreferredElfHeaderAddress(LinuxVMAddress* address) const override { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + for (const auto& header : table_) { + if (header.p_type == PT_LOAD && header.p_offset == 0) { + *address = header.p_vaddr; + return true; + } + } + LOG(ERROR) << "no preferred header address"; + return false; + } + + bool GetPreferredLoadedMemoryRange(LinuxVMAddress* base, + LinuxVMSize* size) const override { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + LinuxVMAddress preferred_base = 0; + LinuxVMAddress preferred_end = 0; + bool load_found = false; + for (const auto& header : table_) { + if (header.p_type == PT_LOAD) { + if (!load_found) { + preferred_base = header.p_vaddr; + load_found = true; + } + preferred_end = header.p_vaddr + header.p_memsz; + } + } + if (load_found) { + *base = preferred_base; + *size = preferred_end - preferred_base; + return true; + } + LOG(ERROR) << "no load segments"; + return false; + } + + bool GetDynamicSegment(LinuxVMAddress* address, + LinuxVMSize* size) const override { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + const PhdrType* phdr; + if (!GetProgramHeader(PT_DYNAMIC, &phdr)) { + return false; + } + *address = phdr->p_vaddr; + *size = phdr->p_memsz; + return true; + } + + bool GetProgramHeader(uint32_t type, const PhdrType** header_out) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + for (const auto& header : table_) { + if (header.p_type == type) { + *header_out = &header; + return true; + } + } + return false; + } + + private: + std::vector table_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProgramHeaderTableSpecific); +}; + +ElfImageReader::ElfImageReader() + : header_64_(), + ehdr_address_(0), + load_bias_(0), + memory_(), + program_headers_(), + dynamic_array_(), + symbol_table_(), + initialized_(), + dynamic_array_initialized_(), + symbol_table_initialized_() {} + +ElfImageReader::~ElfImageReader() {} + +bool ElfImageReader::Initialize(const ProcessMemoryRange& memory, + LinuxVMAddress address) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + ehdr_address_ = address; + if (!memory_.Initialize(memory)) { + return false; + } + + uint8_t e_ident[EI_NIDENT]; + if (!memory_.Read(ehdr_address_, EI_NIDENT, e_ident)) { + return false; + } + + if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 || + e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) { + LOG(ERROR) << "Incorrect ELF magic number"; + return false; + } + + if (!(memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS64) && + !(!memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS32)) { + LOG(ERROR) << "unexpected bitness"; + return false; + } + +#if defined(ARCH_CPU_LITTLE_ENDIAN) + constexpr uint8_t expected_encoding = ELFDATA2LSB; +#elif defined(ARCH_CPU_BIG_ENDIAN) + constexpr uint8_t expected_encoding = ELFDATA2MSB; +#endif + if (e_ident[EI_DATA] != expected_encoding) { + LOG(ERROR) << "unexpected encoding"; + return false; + } + + if (e_ident[EI_VERSION] != EV_CURRENT) { + LOG(ERROR) << "unexpected version"; + return false; + } + + if (!(memory_.Is64Bit() + ? memory_.Read(ehdr_address_, sizeof(header_64_), &header_64_) + : memory_.Read(ehdr_address_, sizeof(header_32_), &header_32_))) { + return false; + } + +#define VERIFY_HEADER(header) \ + do { \ + if (header.e_type != ET_EXEC && header.e_type != ET_DYN) { \ + LOG(ERROR) << "unexpected image type"; \ + return false; \ + } \ + if (header.e_version != EV_CURRENT) { \ + LOG(ERROR) << "unexpected version"; \ + return false; \ + } \ + if (header.e_ehsize != sizeof(header)) { \ + LOG(ERROR) << "unexpected header size"; \ + return false; \ + } \ + } while (false); + + if (memory_.Is64Bit()) { + VERIFY_HEADER(header_64_); + } else { + VERIFY_HEADER(header_32_); + } + + if (!InitializeProgramHeaders()) { + return false; + } + + LinuxVMAddress preferred_ehdr_address; + if (!program_headers_.get()->GetPreferredElfHeaderAddress( + &preferred_ehdr_address)) { + return false; + } + load_bias_ = ehdr_address_ - preferred_ehdr_address; + + LinuxVMAddress base_address; + LinuxVMSize loaded_size; + if (!program_headers_.get()->GetPreferredLoadedMemoryRange(&base_address, + &loaded_size)) { + return false; + } + base_address += load_bias_; + + if (!memory_.RestrictRange(base_address, loaded_size)) { + return false; + } + + LinuxVMSize ehdr_size; + LinuxVMAddress phdr_address; + if (memory_.Is64Bit()) { + ehdr_size = sizeof(header_64_); + phdr_address = ehdr_address_ + header_64_.e_phoff; + } else { + ehdr_size = sizeof(header_32_); + phdr_address = ehdr_address_ + header_32_.e_phoff; + } + + CheckedLinuxAddressRange range(memory_.Is64Bit(), base_address, loaded_size); + if (!range.ContainsRange(CheckedLinuxAddressRange( + memory_.Is64Bit(), ehdr_address_, ehdr_size))) { + LOG(ERROR) << "ehdr out of range"; + return false; + } + if (!range.ContainsRange(CheckedLinuxAddressRange( + memory.Is64Bit(), phdr_address, program_headers_->Size()))) { + LOG(ERROR) << "phdrs out of range"; + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +uint16_t ElfImageReader::FileType() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return memory_.Is64Bit() ? header_64_.e_type : header_32_.e_type; +} + +bool ElfImageReader::GetDynamicSymbol(const std::string& name, + LinuxVMAddress* address, + LinuxVMSize* size) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!InitializeDynamicSymbolTable()) { + return false; + } + + ElfSymbolTableReader::SymbolInformation info; + if (!symbol_table_->GetSymbol(name, &info)) { + return false; + } + if (info.shndx == SHN_UNDEF || info.shndx == SHN_COMMON) { + return false; + } + + switch (info.binding) { + case STB_GLOBAL: + case STB_WEAK: + break; + + case STB_LOCAL: + default: + return false; + } + + switch (info.type) { + case STT_OBJECT: + case STT_FUNC: + break; + + case STT_COMMON: + case STT_NOTYPE: + case STT_SECTION: + case STT_FILE: + case STT_TLS: + default: + return false; + } + + if (info.shndx != SHN_ABS) { + info.address += GetLoadBias(); + } + + *address = info.address; + *size = info.size; + return true; +} + +bool ElfImageReader::ReadDynamicStringTableAtOffset(LinuxVMSize offset, + std::string* string) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!InitializeDynamicArray()) { + return false; + } + + LinuxVMAddress string_table_address; + LinuxVMSize string_table_size; + if (!GetAddressFromDynamicArray(DT_STRTAB, &string_table_address) || + !dynamic_array_->GetValue(DT_STRSZ, &string_table_size)) { + LOG(ERROR) << "missing string table info"; + return false; + } + if (offset >= string_table_size) { + LOG(ERROR) << "bad offset"; + return false; + } + + if (!memory_.ReadCStringSizeLimited( + string_table_address + offset, string_table_size - offset, string)) { + LOG(ERROR) << "missing nul-terminator"; + return false; + } + return true; +} + +bool ElfImageReader::GetDebugAddress(LinuxVMAddress* debug) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!InitializeDynamicArray()) { + return false; + } + return GetAddressFromDynamicArray(DT_DEBUG, debug); +} + +bool ElfImageReader::InitializeProgramHeaders() { +#define INITIALIZE_PROGRAM_HEADERS(PhdrType, header) \ + do { \ + if (header.e_phentsize != sizeof(PhdrType)) { \ + LOG(ERROR) << "unexpected phdr size"; \ + return false; \ + } \ + auto phdrs = new ProgramHeaderTableSpecific(); \ + program_headers_.reset(phdrs); \ + if (!phdrs->Initialize( \ + memory_, ehdr_address_ + header.e_phoff, header.e_phnum)) { \ + return false; \ + } \ + } while (false); + + if (memory_.Is64Bit()) { + INITIALIZE_PROGRAM_HEADERS(Elf64_Phdr, header_64_); + } else { + INITIALIZE_PROGRAM_HEADERS(Elf32_Phdr, header_32_); + } + return true; +} + +bool ElfImageReader::InitializeDynamicArray() { + if (dynamic_array_initialized_.is_valid()) { + return true; + } + if (!dynamic_array_initialized_.is_uninitialized()) { + return false; + } + dynamic_array_initialized_.set_invalid(); + + LinuxVMAddress dyn_segment_address; + LinuxVMSize dyn_segment_size; + if (!program_headers_.get()->GetDynamicSegment(&dyn_segment_address, + &dyn_segment_size)) { + LOG(ERROR) << "no dynamic segment"; + return false; + } + dyn_segment_address += GetLoadBias(); + + dynamic_array_.reset(new ElfDynamicArrayReader()); + if (!dynamic_array_->Initialize( + memory_, dyn_segment_address, dyn_segment_size)) { + return false; + } + dynamic_array_initialized_.set_valid(); + return true; +} + +bool ElfImageReader::InitializeDynamicSymbolTable() { + if (symbol_table_initialized_.is_valid()) { + return true; + } + if (!symbol_table_initialized_.is_uninitialized()) { + return false; + } + symbol_table_initialized_.set_invalid(); + + if (!InitializeDynamicArray()) { + return false; + } + + LinuxVMAddress symbol_table_address; + if (!GetAddressFromDynamicArray(DT_SYMTAB, &symbol_table_address)) { + LOG(ERROR) << "no symbol table"; + return false; + } + + symbol_table_.reset( + new ElfSymbolTableReader(&memory_, this, symbol_table_address)); + symbol_table_initialized_.set_valid(); + return true; +} + +bool ElfImageReader::GetAddressFromDynamicArray(uint64_t tag, + LinuxVMAddress* address) { + if (!dynamic_array_->GetValue(tag, address)) { + return false; + } +#if defined(OS_ANDROID) + // The GNU loader updates the dynamic array according to the load bias while + // the Android loader only updates the debug address. + if (tag != DT_DEBUG) { + *address += GetLoadBias(); + } +#endif // OS_ANDROID + return true; +} + +} // namespace crashpad diff --git a/snapshot/linux/elf_image_reader.h b/snapshot/linux/elf_image_reader.h new file mode 100644 index 00000000..8820e243 --- /dev/null +++ b/snapshot/linux/elf_image_reader.h @@ -0,0 +1,133 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_ELF_IMAGE_READER_H_ +#define CRASHPAD_SNAPSHOT_LINUX_ELF_IMAGE_READER_H_ + +#include +#include + +#include +#include + +#include "base/macros.h" +#include "util/linux/address_types.h" +#include "snapshot/linux/elf_dynamic_array_reader.h" +#include "snapshot/linux/elf_symbol_table_reader.h" +#include "util/linux/process_memory_range.h" +#include "util/misc/initialization_state.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +//! \brief A reader for ELF images mapped into another process. +//! +//! This class is capable of reading both 32-bit and 64-bit images. +class ElfImageReader { + public: + ElfImageReader(); + ~ElfImageReader(); + + //! \brief Initializes the reader. + //! + //! This method must be called once on an object and must be successfully + //! called before any other method in this class may be called. + //! + //! \param[in] memory A memory reader for the remote process. + //! \param[in] address The address in the remote process' address space where + //! the ELF image is loaded. + bool Initialize(const ProcessMemoryRange& memory, LinuxVMAddress address); + + //! \brief Returns the base address of the image's memory range. + //! + //! This may differ from the address passed to Initialize() if the ELF header + //! is not loaded at the start of the first `PT_LOAD` segment. + LinuxVMAddress Address() const { return memory_.Base(); } + + //! \brief Returns the size of the range containing all loaded segments for + //! this image. + //! + //! The size may include memory that is unmapped or mapped to other objects if + //! this image's `PT_LOAD` segments are not contiguous. + LinuxVMSize Size() const { return memory_.Size(); } + + //! \brief Returns the file type for the image. + //! + //! Possible values include `ET_EXEC` or `ET_DYN` from ``. + uint16_t FileType() const; + + //! \brief Returns the load bias for the image. + //! + //! The load bias is the actual load address minus the preferred load address. + LinuxVMOffset GetLoadBias() const { return load_bias_; } + + //! \brief Reads information from the dynamic symbol table about the symbol + //! identified by \a name. + //! + //! \param[in] name The name of the symbol to search for. + //! \param[out] address The address of the symbol in the target process' + //! address space, if found. + //! \param[out] size The size of the symbol, if found. + //! \return `true` if the symbol was found. + bool GetDynamicSymbol(const std::string& name, + LinuxVMAddress* address, + LinuxVMSize* size); + + //! \brief Reads a `NUL`-terminated C string from this image's dynamic string + //! table. + //! + //! \param[in] offset the byte offset in the string table to start reading. + //! \param[out] string the string read. + //! \return `true` on success. Otherwise `false` with a message logged. + bool ReadDynamicStringTableAtOffset(LinuxVMSize offset, std::string* string); + + //! \brief Determine the debug address. + //! + //! The debug address is a pointer to an `r_debug` struct defined in + //! ``. + //! + //! \param[out] debug the debug address, if found. + //! \return `true` if the debug address was found. + bool GetDebugAddress(LinuxVMAddress* debug); + + private: + class ProgramHeaderTable; + template + class ProgramHeaderTableSpecific; + + bool InitializeProgramHeaders(); + bool InitializeDynamicArray(); + bool InitializeDynamicSymbolTable(); + bool GetAddressFromDynamicArray(uint64_t tag, LinuxVMAddress* address); + + union { + Elf32_Ehdr header_32_; + Elf64_Ehdr header_64_; + }; + LinuxVMAddress ehdr_address_; + LinuxVMOffset load_bias_; + ProcessMemoryRange memory_; + std::unique_ptr program_headers_; + std::unique_ptr dynamic_array_; + std::unique_ptr symbol_table_; + InitializationStateDcheck initialized_; + InitializationState dynamic_array_initialized_; + InitializationState symbol_table_initialized_; + + DISALLOW_COPY_AND_ASSIGN(ElfImageReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_ELF_IMAGE_READER_H_ diff --git a/snapshot/linux/elf_image_reader_test.cc b/snapshot/linux/elf_image_reader_test.cc new file mode 100644 index 00000000..0f3f5848 --- /dev/null +++ b/snapshot/linux/elf_image_reader_test.cc @@ -0,0 +1,157 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/linux/elf_image_reader.h" + +#include +#include + +#include "base/logging.h" +#include "gtest/gtest.h" +#include "test/multiprocess.h" +#include "util/file/file_io.h" +#include "util/linux/address_types.h" +#include "util/linux/auxiliary_vector.h" +#include "util/linux/memory_map.h" +#include "util/misc/from_pointer_cast.h" + +extern "C" { +__attribute__((visibility("default"))) void +ElfImageReaderTestExportedSymbol(){}; +} // extern "C" + +namespace crashpad { +namespace test { +namespace { + +void LocateExecutable(pid_t pid, bool is_64_bit, LinuxVMAddress* elf_address) { + AuxiliaryVector aux; + ASSERT_TRUE(aux.Initialize(pid, is_64_bit)); + + LinuxVMAddress phdrs; + ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs)); + + MemoryMap memory_map; + ASSERT_TRUE(memory_map.Initialize(pid)); + const MemoryMap::Mapping* phdr_mapping = memory_map.FindMapping(phdrs); + ASSERT_TRUE(phdr_mapping); + const MemoryMap::Mapping* exe_mapping = + memory_map.FindFileMmapStart(*phdr_mapping); + ASSERT_TRUE(exe_mapping); + *elf_address = exe_mapping->range.Base(); +} + +void ExpectElfImageWithSymbol(pid_t pid, + LinuxVMAddress address, + bool is_64_bit, + std::string symbol_name, + LinuxVMAddress expected_symbol_address) { + ProcessMemory memory; + ASSERT_TRUE(memory.Initialize(pid)); + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); + + ElfImageReader reader; + ASSERT_TRUE(reader.Initialize(range, address)); + + LinuxVMAddress symbol_address; + LinuxVMSize symbol_size; + ASSERT_TRUE( + reader.GetDynamicSymbol(symbol_name, &symbol_address, &symbol_size)); + EXPECT_EQ(symbol_address, expected_symbol_address); + + EXPECT_FALSE( + reader.GetDynamicSymbol("notasymbol", &symbol_address, &symbol_size)); +} + +void ReadThisExecutableInTarget(pid_t pid) { +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif // ARCH_CPU_64_BITS + + LinuxVMAddress elf_address; + LocateExecutable(pid, am_64_bit, &elf_address); + + ExpectElfImageWithSymbol( + pid, + elf_address, + am_64_bit, + "ElfImageReaderTestExportedSymbol", + FromPointerCast(ElfImageReaderTestExportedSymbol)); +} + +// Assumes that libc is loaded at the same address in this process as in the +// target, which it is for the fork test below. +void ReadLibcInTarget(pid_t pid) { +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif // ARCH_CPU_64_BITS + + Dl_info info; + ASSERT_TRUE(dladdr(reinterpret_cast(getpid), &info)) << "dladdr:" + << dlerror(); + LinuxVMAddress elf_address = FromPointerCast(info.dli_fbase); + + ExpectElfImageWithSymbol(pid, + elf_address, + am_64_bit, + "getpid", + FromPointerCast(getpid)); +} + +class ReadExecutableChildTest : public Multiprocess { + public: + ReadExecutableChildTest() : Multiprocess() {} + ~ReadExecutableChildTest() {} + + private: + void MultiprocessParent() { ReadThisExecutableInTarget(ChildPID()); } + void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } +}; + +TEST(ElfImageReader, MainExecutableSelf) { + ReadThisExecutableInTarget(getpid()); +} + +TEST(ElfImageReader, MainExecutableChild) { + ReadExecutableChildTest test; + test.Run(); +} + +TEST(ElfImageReader, OneModuleSelf) { + ReadLibcInTarget(getpid()); +} + +class ReadLibcChildTest : public Multiprocess { + public: + ReadLibcChildTest() : Multiprocess() {} + ~ReadLibcChildTest() {} + + private: + void MultiprocessParent() { ReadLibcInTarget(ChildPID()); } + void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } +}; + +TEST(ElfImageReader, OneModuleChild) { + ReadLibcChildTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/snapshot/linux/elf_symbol_table_reader.cc b/snapshot/linux/elf_symbol_table_reader.cc new file mode 100644 index 00000000..d36e46ac --- /dev/null +++ b/snapshot/linux/elf_symbol_table_reader.cc @@ -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. + +#include "snapshot/linux/elf_symbol_table_reader.h" + +#include + +#include "base/logging.h" +#include "snapshot/linux/elf_image_reader.h" + +namespace crashpad { + +namespace { + +uint8_t GetBinding(const Elf32_Sym& sym) { + return ELF32_ST_BIND(sym.st_info); +} + +uint8_t GetBinding(const Elf64_Sym& sym) { + return ELF64_ST_BIND(sym.st_info); +} + +uint8_t GetType(const Elf32_Sym& sym) { + return ELF32_ST_TYPE(sym.st_info); +} + +uint8_t GetType(const Elf64_Sym& sym) { + return ELF64_ST_TYPE(sym.st_info); +} + +uint8_t GetVisibility(const Elf32_Sym& sym) { + return ELF32_ST_VISIBILITY(sym.st_other); +} + +uint8_t GetVisibility(const Elf64_Sym& sym) { + return ELF64_ST_VISIBILITY(sym.st_other); +} + +} // namespace + +ElfSymbolTableReader::ElfSymbolTableReader(const ProcessMemoryRange* memory, + ElfImageReader* elf_reader, + LinuxVMAddress address) + : memory_(memory), elf_reader_(elf_reader), base_address_(address) {} + +ElfSymbolTableReader::~ElfSymbolTableReader() {} + +bool ElfSymbolTableReader::GetSymbol(const std::string& name, + SymbolInformation* info) { + return memory_->Is64Bit() ? ScanSymbolTable(name, info) + : ScanSymbolTable(name, info); +} + +template +bool ElfSymbolTableReader::ScanSymbolTable(const std::string& name, + SymbolInformation* info_out) { + LinuxVMAddress address = base_address_; + SymEnt entry; + std::string string; + while (memory_->Read(address, sizeof(entry), &entry) && + elf_reader_->ReadDynamicStringTableAtOffset(entry.st_name, &string)) { + if (string == name) { + info_out->address = entry.st_value; + info_out->size = entry.st_size; + info_out->shndx = entry.st_shndx; + info_out->binding = GetBinding(entry); + info_out->type = GetType(entry); + info_out->visibility = GetVisibility(entry); + return true; + } + address += sizeof(entry); + } + return false; +} + +} // namespace crashpad diff --git a/snapshot/linux/elf_symbol_table_reader.h b/snapshot/linux/elf_symbol_table_reader.h new file mode 100644 index 00000000..1eda29b5 --- /dev/null +++ b/snapshot/linux/elf_symbol_table_reader.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_SNAPSHOT_LINUX_ELF_SYMBOL_TABLE_READER_H_ +#define CRASHPAD_SNAPSHOT_LINUX_ELF_SYMBOL_TABLE_READER_H_ + +#include + +#include + +#include "base/macros.h" +#include "util/linux/address_types.h" +#include "util/linux/process_memory_range.h" + +namespace crashpad { + +class ElfImageReader; + +//! \brief A reader for symbol tables in ELF images mapped into another process. +class ElfSymbolTableReader { + public: + //! \brief Information about a symbol in a module's symbol table. + struct SymbolInformation { + //! \brief The address of the symbol as it exists in the symbol table, not + //! adjusted for any load bias. + LinuxVMAddress address; + + //! \brief The size of the symbol. + LinuxVMSize size; + + //! \brief The section index that the symbol definition is in relation to. + uint16_t shndx; + + //! \brief Specifies the type of symbol. Possible values include + //! `STT_OBJECT`, `STT_FUNC`, etc. + uint8_t type; + + //! \brief Specifies the default scope at which a symbol takes precedence. + //! Possible values include `STB_LOCAL`, `STB_GLOBAL`, `STB_WEAK`, or + //! OS/processor specific values. + uint8_t binding; + + //! \brief Together with binding, can limit the visibility of a symbol to + //! the module that defines it. Possible values include `STV_DEFAULT`, + //! `STV_INTERNAL`, `STV_HIDDEN`, and `STV_PROTECTED`. + uint8_t visibility; + }; + + // TODO(jperaza): Support using .hash and .gnu.hash sections to improve symbol + // lookup. + ElfSymbolTableReader(const ProcessMemoryRange* memory, + ElfImageReader* elf_reader, + LinuxVMAddress address); + ~ElfSymbolTableReader(); + + //! \brief Lookup information about a symbol. + //! + //! \param[in] name The name of the symbol to search for. + //! \param[out] info The symbol information, if found. + //! \return `true` if the symbol is found. + bool GetSymbol(const std::string& name, SymbolInformation* info); + + private: + template + bool ScanSymbolTable(const std::string& name, SymbolInformation* info); + + const ProcessMemoryRange* const memory_; // weak + ElfImageReader* const elf_reader_; // weak + const LinuxVMAddress base_address_; + + DISALLOW_COPY_AND_ASSIGN(ElfSymbolTableReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_ELF_SYMBOL_TABLE_READER_H_ diff --git a/snapshot/linux/memory_snapshot_linux.cc b/snapshot/linux/memory_snapshot_linux.cc new file mode 100644 index 00000000..f328fefc --- /dev/null +++ b/snapshot/linux/memory_snapshot_linux.cc @@ -0,0 +1,68 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/linux/memory_snapshot_linux.h" + +#include + +namespace crashpad { +namespace internal { + +MemorySnapshotLinux::MemorySnapshotLinux() + : MemorySnapshot(), + process_reader_(nullptr), + address_(0), + size_(0), + initialized_() { +} + +MemorySnapshotLinux::~MemorySnapshotLinux() { +} + +void MemorySnapshotLinux::Initialize(ProcessReader* process_reader, + LinuxVMAddress address, + size_t size) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + process_reader_ = process_reader; + address_ = address; + size_ = size; + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +uint64_t MemorySnapshotLinux::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return address_; +} + +size_t MemorySnapshotLinux::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return size_; +} + +bool MemorySnapshotLinux::Read(Delegate* delegate) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (size_ == 0) { + return delegate->MemorySnapshotDelegateRead(nullptr, size_); + } + + std::unique_ptr buffer(new uint8_t[size_]); + if (!process_reader_->Memory()->Read(address_, size_, buffer.get())) { + return false; + } + return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/linux/memory_snapshot_linux.h b/snapshot/linux/memory_snapshot_linux.h new file mode 100644 index 00000000..3c7c8391 --- /dev/null +++ b/snapshot/linux/memory_snapshot_linux.h @@ -0,0 +1,69 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_MEMORY_SNAPSHOT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_MEMORY_SNAPSHOT_LINUX_H_ + +#include +#include + +#include "base/macros.h" +#include "snapshot/linux/process_reader.h" +#include "snapshot/memory_snapshot.h" +#include "util/linux/address_types.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A MemorySnapshot of a memory region in a process on the running +//! system, when the system runs Linux. +class MemorySnapshotLinux final : public MemorySnapshot { + public: + MemorySnapshotLinux(); + ~MemorySnapshotLinux() override; + + //! \brief Initializes the object. + //! + //! Memory is read lazily. No attempt is made to read the memory snapshot data + //! until Read() is called, and the memory snapshot data is discared when + //! Read() returns. + //! + //! \param[in] process_reader A reader for the process being snapshotted. + //! \param[in] address The base address of the memory region to snapshot, in + //! the snapshot process’ address space. + //! \param[in] size The size of the memory region to snapshot. + void Initialize(ProcessReader* process_reader, + LinuxVMAddress address, + size_t size); + + // MemorySnapshot: + + uint64_t Address() const override; + size_t Size() const override; + bool Read(Delegate* delegate) const override; + + private: + ProcessReader* process_reader_; // weak + uint64_t address_; + size_t size_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(MemorySnapshotLinux); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_MEMORY_SNAPSHOT_LINUX_H_ diff --git a/snapshot/linux/process_reader.cc b/snapshot/linux/process_reader.cc new file mode 100644 index 00000000..d126cfa7 --- /dev/null +++ b/snapshot/linux/process_reader.cc @@ -0,0 +1,322 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/linux/process_reader.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "build/build_config.h" +#include "util/linux/proc_stat_reader.h" +#include "util/posix/scoped_dir.h" + +namespace crashpad { + +namespace { + +bool ShouldMergeStackMappings(const MemoryMap::Mapping& stack_mapping, + const MemoryMap::Mapping& adj_mapping) { + DCHECK(stack_mapping.readable); + return adj_mapping.readable && stack_mapping.device == adj_mapping.device && + stack_mapping.inode == adj_mapping.inode; +} + +} // namespace + +ProcessReader::Thread::Thread() + : thread_context(), + float_context(), + thread_specific_data_address(0), + stack_region_address(0), + stack_region_size(0), + tid(-1), + static_priority(-1), + nice_value(-1) {} + +ProcessReader::Thread::~Thread() {} + +bool ProcessReader::Thread::InitializePtrace() { + ThreadInfo thread_info; + if (!thread_info.Initialize(tid)) { + return false; + } + + thread_info.GetGeneralPurposeRegisters(&thread_context); + + if (!thread_info.GetFloatingPointRegisters(&float_context)) { + return false; + } + + if (!thread_info.GetThreadArea(&thread_specific_data_address)) { + return false; + } + + // TODO(jperaza): Starting with Linux 3.14, scheduling policy, static + // priority, and nice value can be collected all in one call with + // sched_getattr(). + int res = sched_getscheduler(tid); + if (res < 0) { + PLOG(ERROR) << "sched_getscheduler"; + return false; + } + sched_policy = res; + + sched_param param; + if (sched_getparam(tid, ¶m) != 0) { + PLOG(ERROR) << "sched_getparam"; + return false; + } + static_priority = param.sched_priority; + + errno = 0; + res = getpriority(PRIO_PROCESS, tid); + if (res == -1 && errno) { + PLOG(ERROR) << "getpriority"; + return false; + } + nice_value = res; + + return true; +} + +void ProcessReader::Thread::InitializeStack(ProcessReader* reader) { + LinuxVMAddress stack_pointer; +#if defined(ARCH_CPU_X86_FAMILY) + stack_pointer = + reader->Is64Bit() ? thread_context.t64.rsp : thread_context.t32.esp; +#elif defined(ARCH_CPU_ARM_FAMILY) + stack_pointer = + reader->Is64Bit() ? thread_context.t64.sp : thread_context.t32.sp; +#else +#error Port. +#endif + + const MemoryMap* memory_map = reader->GetMemoryMap(); + + // If we can't find the mapping, it's probably a bad stack pointer + const MemoryMap::Mapping* mapping = memory_map->FindMapping(stack_pointer); + if (!mapping) { + LOG(WARNING) << "no stack mapping"; + return; + } + LinuxVMAddress stack_region_start = stack_pointer; + + // We've hit what looks like a guard page; skip to the end and check for a + // mapped stack region. + if (!mapping->readable) { + stack_region_start = mapping->range.End(); + mapping = memory_map->FindMapping(stack_region_start); + if (!mapping) { + LOG(WARNING) << "no stack mapping"; + return; + } + } else { +#if defined(ARCH_CPU_X86_FAMILY) + // Adjust start address to include the red zone + if (reader->Is64Bit()) { + constexpr LinuxVMSize kRedZoneSize = 128; + LinuxVMAddress red_zone_base = + stack_region_start - std::min(kRedZoneSize, stack_region_start); + + // Only include the red zone if it is part of a valid mapping + if (red_zone_base >= mapping->range.Base()) { + stack_region_start = red_zone_base; + } else { + const MemoryMap::Mapping* rz_mapping = + memory_map->FindMapping(red_zone_base); + if (rz_mapping && ShouldMergeStackMappings(*mapping, *rz_mapping)) { + stack_region_start = red_zone_base; + } else { + stack_region_start = mapping->range.Base(); + } + } + } +#endif + } + stack_region_address = stack_region_start; + + // If there are more mappings at the end of this one, they may be a + // continuation of the stack. + LinuxVMAddress stack_end = mapping->range.End(); + const MemoryMap::Mapping* next_mapping; + while ((next_mapping = memory_map->FindMapping(stack_end)) && + ShouldMergeStackMappings(*mapping, *next_mapping)) { + stack_end = next_mapping->range.End(); + } + + // The main thread should have an entry in the maps file just for its stack, + // so we'll assume the base of the stack is at the end of the region. Other + // threads' stacks may not have their own entries in the maps file if they + // were user-allocated within a larger mapping, but pthreads places the TLS + // at the high-address end of the stack so we can try using that to shrink + // the stack region. + stack_region_size = stack_end - stack_region_address; + if (tid != reader->ProcessID() && + thread_specific_data_address > stack_region_address && + thread_specific_data_address < stack_end) { + stack_region_size = thread_specific_data_address - stack_region_address; + } +} + +ProcessReader::ProcessReader() + : process_info_(), + memory_map_(), + threads_(), + process_memory_(), + is_64_bit_(false), + initialized_threads_(false), + initialized_() {} + +ProcessReader::~ProcessReader() {} + +bool ProcessReader::Initialize(pid_t pid) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + if (!process_info_.Initialize(pid)) { + return false; + } + + if (!memory_map_.Initialize(pid)) { + return false; + } + + process_memory_.reset(new ProcessMemory()); + if (!process_memory_->Initialize(pid)) { + return false; + } + + if (!process_info_.Is64Bit(&is_64_bit_)) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessReader::StartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_info_.StartTime(start_time); +} + +bool ProcessReader::CPUTimes(timeval* user_time, timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + timerclear(user_time); + timerclear(system_time); + + timeval local_user_time; + timerclear(&local_user_time); + timeval local_system_time; + timerclear(&local_system_time); + + for (const Thread& thread : threads_) { + ProcStatReader stat; + if (!stat.Initialize(thread.tid)) { + return false; + } + + timeval thread_user_time; + if (!stat.UserCPUTime(&thread_user_time)) { + return false; + } + + timeval thread_system_time; + if (!stat.SystemCPUTime(&thread_system_time)) { + return false; + } + + timeradd(&local_user_time, &thread_user_time, &local_user_time); + timeradd(&local_system_time, &thread_system_time, &local_system_time); + } + + *user_time = local_user_time; + *system_time = local_system_time; + return true; +} + +const std::vector& ProcessReader::Threads() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!initialized_threads_) { + InitializeThreads(); + } + return threads_; +} + +void ProcessReader::InitializeThreads() { + DCHECK(threads_.empty()); + + pid_t pid = ProcessID(); + if (pid == getpid()) { + // TODO(jperaza): ptrace can't be used on threads in the same thread group. + // Using clone to create a new thread in it's own thread group doesn't work + // because glibc doesn't support threads it didn't create via pthreads. + // Fork a new process to snapshot us and copy the data back? + LOG(ERROR) << "not implemented"; + return; + } + + char path[32]; + snprintf(path, arraysize(path), "/proc/%d/task", pid); + DIR* dir = opendir(path); + if (!dir) { + PLOG(ERROR) << "opendir"; + return; + } + ScopedDIR scoped_dir(dir); + + Thread main_thread; + main_thread.tid = pid; + if (main_thread.InitializePtrace()) { + main_thread.InitializeStack(this); + threads_.push_back(main_thread); + } else { + LOG(WARNING) << "Couldn't initialize main thread."; + } + + bool main_thread_found = false; + dirent* dir_entry; + while ((dir_entry = readdir(scoped_dir.get()))) { + if (strncmp(dir_entry->d_name, ".", arraysize(dir_entry->d_name)) == 0 || + strncmp(dir_entry->d_name, "..", arraysize(dir_entry->d_name)) == 0) { + continue; + } + pid_t tid; + if (!base::StringToInt(dir_entry->d_name, &tid)) { + LOG(ERROR) << "format error"; + continue; + } + + if (tid == pid) { + DCHECK(!main_thread_found); + main_thread_found = true; + continue; + } + + Thread thread; + thread.tid = tid; + if (thread.InitializePtrace()) { + thread.InitializeStack(this); + threads_.push_back(thread); + } + } + DCHECK(main_thread_found); +} + +} // namespace crashpad diff --git a/snapshot/linux/process_reader.h b/snapshot/linux/process_reader.h new file mode 100644 index 00000000..9f398f34 --- /dev/null +++ b/snapshot/linux/process_reader.h @@ -0,0 +1,127 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_H_ +#define CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_H_ + +#include +#include + +#include +#include + +#include "base/macros.h" +#include "util/linux/address_types.h" +#include "util/linux/memory_map.h" +#include "util/linux/process_memory.h" +#include "util/linux/thread_info.h" +#include "util/posix/process_info.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +//! \brief Accesses information about another process, identified by a process +//! ID. +class ProcessReader { + public: + //! \brief Contains information about a thread that belongs to a process. + struct Thread { + Thread(); + ~Thread(); + + ThreadContext thread_context; + FloatContext float_context; + LinuxVMAddress thread_specific_data_address; + LinuxVMAddress stack_region_address; + LinuxVMSize stack_region_size; + pid_t tid; + int sched_policy; + int static_priority; + int nice_value; + + private: + friend class ProcessReader; + + bool InitializePtrace(); + void InitializeStack(ProcessReader* reader); + }; + + ProcessReader(); + ~ProcessReader(); + + //! \brief Initializes this object. + //! + //! This method must be successfully called before calling any other method in + //! this class. + //! + //! \param[in] pid The process ID of the target process. + //! \return `true` on success. `false` on failure with a message logged. + bool Initialize(pid_t pid); + + //! \brief Return `true` if the target task is a 64-bit process. + bool Is64Bit() const { return is_64_bit_; } + + //! \brief Return the target process' process ID. + pid_t ProcessID() const { return process_info_.ProcessID(); } + + //! \brief Return the target process' parent process ID. + pid_t ParentProcessID() const { return process_info_.ParentProcessID(); } + + //! \brief Return a memory reader for the target process. + ProcessMemory* Memory() { return process_memory_.get(); } + + //! \brief Return a memory map of the target process. + MemoryMap* GetMemoryMap() { return &memory_map_; } + + //! \brief Determines the target process’ start time. + //! + //! \param[out] start_time The time that the process started. + //! \return `true` on success with \a start_time set. Otherwise `false` with a + //! message logged. + bool StartTime(timeval* start_time) const; + + //! \brief Determines the target process’ execution time. + //! + //! \param[out] user_time The amount of time the process has executed code in + //! user mode. + //! \param[out] system_time The amount of time the process has executed code + //! in system mode. + //! + //! \return `true` on success, `false` on failure, with a warning logged. On + //! failure, \a user_time and \a system_time will be set to represent no + //! time spent executing code in user or system mode. + bool CPUTimes(timeval* user_time, timeval* system_time) const; + + //! \brief Return a vector of threads that are in the task process. If the + //! main thread is able to be identified and traced, it will be placed at + //! index `0`. + const std::vector& Threads(); + + private: + void InitializeThreads(); + + ProcessInfo process_info_; + class MemoryMap memory_map_; + std::vector threads_; + std::unique_ptr process_memory_; + bool is_64_bit_; + bool initialized_threads_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcessReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_H_ diff --git a/snapshot/linux/process_reader_test.cc b/snapshot/linux/process_reader_test.cc new file mode 100644 index 00000000..e3743bed --- /dev/null +++ b/snapshot/linux/process_reader_test.cc @@ -0,0 +1,450 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/linux/process_reader.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "base/format_macros.h" +#include "base/memory/free_deleter.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/multiprocess.h" +#include "util/file/file_io.h" +#include "util/misc/from_pointer_cast.h" +#include "util/stdlib/pointer_container.h" +#include "util/synchronization/semaphore.h" + +namespace crashpad { +namespace test { +namespace { + +pid_t gettid() { + return syscall(SYS_gettid); +} + +LinuxVMAddress GetTLS() { + LinuxVMAddress tls; +#if defined(ARCH_CPU_ARMEL) + // 0xffff0fe0 is the address of the kernel user helper __kuser_get_tls(). + auto kuser_get_tls = reinterpret_cast(0xffff0fe0); + tls = FromPointerCast(kuser_get_tls()); +#elif defined(ARCH_CPU_ARM64) + // Linux/aarch64 places the tls address in system register tpidr_el0. + asm("mrs %0, tpidr_el0" : "=r"(tls)); +#elif defined(ARCH_CPU_X86) + uint32_t tls_32; + asm("movl %%gs:0x0, %0" : "=r"(tls_32)); + tls = tls_32; +#elif defined(ARCH_CPU_X86_64) + asm("movq %%fs:0x0, %0" : "=r"(tls)); +#else +#error Port. +#endif // ARCH_CPU_ARMEL + + return tls; +} + +TEST(ProcessReader, SelfBasic) { + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(getpid())); + +#if !defined(ARCH_CPU_64_BITS) + EXPECT_FALSE(process_reader.Is64Bit()); +#else + EXPECT_TRUE(process_reader.Is64Bit()); +#endif + + EXPECT_EQ(process_reader.ProcessID(), getpid()); + EXPECT_EQ(process_reader.ParentProcessID(), getppid()); + + static constexpr char kTestMemory[] = "Some test memory"; + char buffer[arraysize(kTestMemory)]; + ASSERT_TRUE(process_reader.Memory()->Read( + reinterpret_cast(kTestMemory), + sizeof(kTestMemory), + &buffer)); + EXPECT_STREQ(kTestMemory, buffer); +} + +constexpr char kTestMemory[] = "Read me from another process"; + +class BasicChildTest : public Multiprocess { + public: + BasicChildTest() : Multiprocess() {} + ~BasicChildTest() {} + + private: + void MultiprocessParent() override { + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildPID())); + +#if !defined(ARCH_CPU_64_BITS) + EXPECT_FALSE(process_reader.Is64Bit()); +#else + EXPECT_TRUE(process_reader.Is64Bit()); +#endif + + EXPECT_EQ(process_reader.ParentProcessID(), getpid()); + EXPECT_EQ(process_reader.ProcessID(), ChildPID()); + + std::string read_string; + ASSERT_TRUE(process_reader.Memory()->ReadCString( + reinterpret_cast(kTestMemory), &read_string)); + EXPECT_EQ(read_string, kTestMemory); + } + + void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); } + + DISALLOW_COPY_AND_ASSIGN(BasicChildTest); +}; + +TEST(ProcessReader, ChildBasic) { + BasicChildTest test; + test.Run(); +} + +class TestThreadPool { + public: + struct ThreadExpectation { + LinuxVMAddress tls = 0; + LinuxVMAddress stack_address = 0; + LinuxVMSize max_stack_size = 0; + int sched_policy = 0; + int static_priority = 0; + int nice_value = 0; + }; + + TestThreadPool() : threads_() {} + + ~TestThreadPool() { + for (Thread* thread : threads_) { + thread->exit_semaphore.Signal(); + } + + for (const Thread* thread : threads_) { + EXPECT_EQ(pthread_join(thread->pthread, nullptr), 0) + << ErrnoMessage("pthread_join"); + } + } + + void StartThreads(size_t thread_count, size_t stack_size = 0) { + for (size_t thread_index = 0; thread_index < thread_count; ++thread_index) { + Thread* thread = new Thread(); + threads_.push_back(thread); + + pthread_attr_t attr; + ASSERT_EQ(pthread_attr_init(&attr), 0) + << ErrnoMessage("pthread_attr_init"); + + if (stack_size > 0) { + void* stack_ptr; + errno = posix_memalign(&stack_ptr, getpagesize(), stack_size); + ASSERT_EQ(errno, 0) << ErrnoMessage("posix_memalign"); + + thread->stack.reset(reinterpret_cast(stack_ptr)); + + ASSERT_EQ(pthread_attr_setstack(&attr, thread->stack.get(), stack_size), + 0) + << ErrnoMessage("pthread_attr_setstack"); + thread->expectation.max_stack_size = stack_size; + } + + ASSERT_EQ(pthread_attr_setschedpolicy(&attr, SCHED_OTHER), 0) + << ErrnoMessage("pthread_attr_setschedpolicy"); + thread->expectation.sched_policy = SCHED_OTHER; + + sched_param param; + param.sched_priority = 0; + ASSERT_EQ(pthread_attr_setschedparam(&attr, ¶m), 0) + << ErrnoMessage("pthread_attr_setschedparam"); + thread->expectation.static_priority = 0; + + thread->expectation.nice_value = thread_index % 20; + + ASSERT_EQ(pthread_create(&thread->pthread, &attr, ThreadMain, thread), 0) + << ErrnoMessage("pthread_create"); + } + + for (Thread* thread : threads_) { + thread->ready_semaphore.Wait(); + } + } + + pid_t GetThreadExpectation(size_t thread_index, + ThreadExpectation* expectation) { + CHECK_LT(thread_index, threads_.size()); + + const Thread* thread = threads_[thread_index]; + *expectation = thread->expectation; + return thread->tid; + } + + private: + struct Thread { + Thread() + : pthread(), + expectation(), + ready_semaphore(0), + exit_semaphore(0), + tid(-1) {} + ~Thread() {} + + pthread_t pthread; + ThreadExpectation expectation; + std::unique_ptr stack; + Semaphore ready_semaphore; + Semaphore exit_semaphore; + pid_t tid; + }; + + static void* ThreadMain(void* argument) { + Thread* thread = static_cast(argument); + + CHECK_EQ(setpriority(PRIO_PROCESS, 0, thread->expectation.nice_value), 0) + << ErrnoMessage("setpriority"); + + thread->expectation.tls = GetTLS(); + thread->expectation.stack_address = + reinterpret_cast(&thread); + thread->tid = gettid(); + + thread->ready_semaphore.Signal(); + thread->exit_semaphore.Wait(); + + CHECK_EQ(pthread_self(), thread->pthread); + + return nullptr; + } + + PointerVector threads_; + + DISALLOW_COPY_AND_ASSIGN(TestThreadPool); +}; + +using ThreadMap = std::map; + +void ExpectThreads(const ThreadMap& thread_map, + const std::vector& threads, + const pid_t pid) { + ASSERT_EQ(threads.size(), thread_map.size()); + MemoryMap memory_map; + ASSERT_TRUE(memory_map.Initialize(pid)); + + for (const auto& thread : threads) { + SCOPED_TRACE(base::StringPrintf("Thread id %d, tls 0x%" PRIx64 + ", stack addr 0x%" PRIx64 + ", stack size 0x%" PRIx64, + thread.tid, + thread.thread_specific_data_address, + thread.stack_region_address, + thread.stack_region_size)); + + const auto& iterator = thread_map.find(thread.tid); + ASSERT_NE(iterator, thread_map.end()); + + EXPECT_EQ(thread.thread_specific_data_address, iterator->second.tls); + + ASSERT_TRUE(memory_map.FindMapping(thread.stack_region_address)); + EXPECT_LE(thread.stack_region_address, iterator->second.stack_address); + + ASSERT_TRUE(memory_map.FindMapping(thread.stack_region_address + + thread.stack_region_size - 1)); + EXPECT_GE(thread.stack_region_address + thread.stack_region_size, + iterator->second.stack_address); + if (iterator->second.max_stack_size) { + EXPECT_LT(thread.stack_region_size, iterator->second.max_stack_size); + } + + EXPECT_EQ(thread.sched_policy, iterator->second.sched_policy); + EXPECT_EQ(thread.static_priority, iterator->second.static_priority); + EXPECT_EQ(thread.nice_value, iterator->second.nice_value); + } +} + +class ChildThreadTest : public Multiprocess { + public: + ChildThreadTest(size_t stack_size = 0) + : Multiprocess(), stack_size_(stack_size) {} + ~ChildThreadTest() {} + + private: + void MultiprocessParent() override { + ThreadMap thread_map; + for (size_t thread_index = 0; thread_index < kThreadCount + 1; + ++thread_index) { + pid_t tid; + TestThreadPool::ThreadExpectation expectation; + + CheckedReadFileExactly(ReadPipeHandle(), &tid, sizeof(tid)); + CheckedReadFileExactly( + ReadPipeHandle(), &expectation, sizeof(expectation)); + thread_map[tid] = expectation; + } + + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildPID())); + const std::vector& threads = + process_reader.Threads(); + ExpectThreads(thread_map, threads, ChildPID()); + } + + void MultiprocessChild() override { + TestThreadPool thread_pool; + thread_pool.StartThreads(kThreadCount, stack_size_); + + TestThreadPool::ThreadExpectation expectation; + expectation.tls = GetTLS(); + expectation.stack_address = reinterpret_cast(&thread_pool); + + int res = sched_getscheduler(0); + ASSERT_GE(res, 0) << ErrnoMessage("sched_getscheduler"); + expectation.sched_policy = res; + + sched_param param; + ASSERT_EQ(sched_getparam(0, ¶m), 0) << ErrnoMessage("sched_getparam"); + expectation.static_priority = param.sched_priority; + + errno = 0; + res = getpriority(PRIO_PROCESS, 0); + ASSERT_FALSE(res == -1 && errno) << ErrnoMessage("getpriority"); + expectation.nice_value = res; + + pid_t tid = gettid(); + + CheckedWriteFile(WritePipeHandle(), &tid, sizeof(tid)); + CheckedWriteFile(WritePipeHandle(), &expectation, sizeof(expectation)); + + for (size_t thread_index = 0; thread_index < kThreadCount; ++thread_index) { + tid = thread_pool.GetThreadExpectation(thread_index, &expectation); + CheckedWriteFile(WritePipeHandle(), &tid, sizeof(tid)); + CheckedWriteFile(WritePipeHandle(), &expectation, sizeof(expectation)); + } + + CheckedReadFileAtEOF(ReadPipeHandle()); + } + + static constexpr size_t kThreadCount = 3; + const size_t stack_size_; + + DISALLOW_COPY_AND_ASSIGN(ChildThreadTest); +}; + +TEST(ProcessReader, ChildWithThreads) { + ChildThreadTest test; + test.Run(); +} + +TEST(ProcessReader, ChildThreadsWithSmallUserStacks) { + ChildThreadTest test(PTHREAD_STACK_MIN); + test.Run(); +} + +// Tests a thread with a stack that spans multiple mappings. +class ChildWithSplitStackTest : public Multiprocess { + public: + ChildWithSplitStackTest() : Multiprocess(), page_size_(getpagesize()) {} + ~ChildWithSplitStackTest() {} + + private: + void MultiprocessParent() override { + LinuxVMAddress stack_addr1; + LinuxVMAddress stack_addr2; + LinuxVMAddress stack_addr3; + + CheckedReadFileExactly(ReadPipeHandle(), &stack_addr1, sizeof(stack_addr1)); + CheckedReadFileExactly(ReadPipeHandle(), &stack_addr2, sizeof(stack_addr2)); + CheckedReadFileExactly(ReadPipeHandle(), &stack_addr3, sizeof(stack_addr3)); + + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildPID())); + + const std::vector& threads = + process_reader.Threads(); + ASSERT_EQ(threads.size(), 1u); + + LinuxVMAddress thread_stack_start = threads[0].stack_region_address; + EXPECT_LE(thread_stack_start, stack_addr1); + EXPECT_LE(thread_stack_start, stack_addr2); + EXPECT_LE(thread_stack_start, stack_addr3); + + LinuxVMAddress thread_stack_end = + thread_stack_start + threads[0].stack_region_size; + EXPECT_GE(thread_stack_end, stack_addr1); + EXPECT_GE(thread_stack_end, stack_addr2); + EXPECT_GE(thread_stack_end, stack_addr3); + } + + void MultiprocessChild() override { + const LinuxVMSize stack_size = page_size_ * 3; + GrowStack(stack_size, reinterpret_cast(&stack_size)); + } + + void GrowStack(LinuxVMSize stack_size, LinuxVMAddress bottom_of_stack) { + char stack_contents[4096]; + auto stack_address = reinterpret_cast(&stack_contents); + + if (bottom_of_stack - stack_address < stack_size) { + GrowStack(stack_size, bottom_of_stack); + } else { + // Write-protect a page on our stack to split up the mapping + LinuxVMAddress page_addr = + stack_address - (stack_address % page_size_) + page_size_; + ASSERT_EQ( + mprotect(reinterpret_cast(page_addr), page_size_, PROT_READ), + 0) + << ErrnoMessage("mprotect"); + + CheckedWriteFile( + WritePipeHandle(), &bottom_of_stack, sizeof(bottom_of_stack)); + CheckedWriteFile(WritePipeHandle(), &page_addr, sizeof(page_addr)); + CheckedWriteFile( + WritePipeHandle(), &stack_address, sizeof(stack_address)); + + // Wait for parent to read us + CheckedReadFileAtEOF(ReadPipeHandle()); + + ASSERT_EQ(mprotect(reinterpret_cast(page_addr), + page_size_, + PROT_READ | PROT_WRITE), + 0) + << ErrnoMessage("mprotect"); + } + } + + const size_t page_size_; + + DISALLOW_COPY_AND_ASSIGN(ChildWithSplitStackTest); +}; + +TEST(ProcessReader, ChildWithSplitStack) { + ChildWithSplitStackTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/snapshot/linux/test_exported_symbols.sym b/snapshot/linux/test_exported_symbols.sym new file mode 100644 index 00000000..e0785c6e --- /dev/null +++ b/snapshot/linux/test_exported_symbols.sym @@ -0,0 +1,18 @@ +# Copyright 2017 The Crashpad Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This symbol is used by elf_image_reader_test.cc. +{ + ElfImageReaderTestExportedSymbol; +}; diff --git a/snapshot/linux/thread_snapshot_linux.cc b/snapshot/linux/thread_snapshot_linux.cc new file mode 100644 index 00000000..dd902905 --- /dev/null +++ b/snapshot/linux/thread_snapshot_linux.cc @@ -0,0 +1,204 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/linux/thread_snapshot_linux.h" + +#include + +#include "base/logging.h" +#include "snapshot/linux/cpu_context_linux.h" +#include "util/misc/reinterpret_bytes.h" + +namespace crashpad { +namespace internal { + +ThreadSnapshotLinux::ThreadSnapshotLinux() + : ThreadSnapshot(), + context_union_(), + context_(), + stack_(), + thread_specific_data_address_(0), + thread_id_(-1), + priority_(-1), + initialized_() { +} + +ThreadSnapshotLinux::~ThreadSnapshotLinux() { +} + +bool ThreadSnapshotLinux::Initialize( + ProcessReader* process_reader, + const ProcessReader::Thread& thread) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + if (process_reader->Is64Bit()) { + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_union_.x86_64; + InitializeCPUContextX86_64(thread.thread_context.t64, + thread.float_context.f64, + context_.x86_64); + } else { + context_.architecture = kCPUArchitectureX86; + context_.x86 = &context_union_.x86; + InitializeCPUContextX86(thread.thread_context.t32, + thread.float_context.f32, + context_.x86); + } +#else +#error Port. +#endif + + stack_.Initialize(process_reader, + thread.stack_region_address, + thread.stack_region_size); + + thread_specific_data_address_ = + thread.thread_specific_data_address; + + thread_id_ = thread.tid; + + // Map Linux scheduling policy, static priority, and nice value into a single + // int value. + // + // The possible policies in order of approximate priority (low to high) are + // SCHED_IDLE + // SCHED_BATCH + // SCHED_OTHER + // SCHED_RR + // SCHED_FIFO + // + // static_priority is not used for OTHER, BATCH, or IDLE and should be 0. + // For FIFO and RR, static_priority should range from 1 to 99 with 99 being + // the highest priority. + // + // nice value ranges from -20 to 19, with -20 being highest priority + + enum class Policy : uint8_t { + kUnknown = 0, + kIdle, + kBatch, + kOther, + kRR, + kFIFO + }; + + struct LinuxPriority { +#if defined(ARCH_CPU_LITTLE_ENDIAN) + // nice values affect how dynamic priorities are updated, which only matters + // for threads with the same static priority. + uint8_t nice_value = 0; + + // The scheduling policy also affects how threads with the same static + // priority are ordered, but has greater impact than nice value. + Policy policy = Policy::kUnknown; + + // The static priority is the most significant in determining overall + // priority. + uint8_t static_priority = 0; + + // Put this in the most significant byte position to prevent negative + // priorities. + uint8_t unused = 0; +#elif defined(ARCH_CPU_BIG_ENDIAN) + uint8_t unused = 0; + uint8_t static_priority = 0; + Policy policy = Policy::kUnknown; + uint8_t nice_value = 0; +#endif // ARCH_CPU_LITTLE_ENDIAN + }; + static_assert(sizeof(LinuxPriority) <= sizeof(int), "priority is too large"); + + LinuxPriority prio; + + // Lower nice values have higher priority, so negate them and add 20 to put + // them in the range 1-40 with 40 being highest priority. + if (thread.nice_value < -20 || thread.nice_value > 19) { + LOG(WARNING) << "invalid nice value " << thread.nice_value; + prio.nice_value = 0; + } else { + prio.nice_value = -1 * thread.nice_value + 20; + } + + switch (thread.sched_policy) { + case SCHED_IDLE: + prio.policy = Policy::kIdle; + break; + case SCHED_BATCH: + prio.policy = Policy::kBatch; + break; + case SCHED_OTHER: + prio.policy = Policy::kOther; + break; + case SCHED_RR: + prio.policy = Policy::kRR; + break; + case SCHED_FIFO: + prio.policy = Policy::kFIFO; + break; + default: + prio.policy = Policy::kUnknown; + LOG(WARNING) << "Unknown scheduling policy " << thread.sched_policy; + } + + if (thread.static_priority < 0 || thread.static_priority > 99) { + LOG(WARNING) << "invalid static priority " << thread.static_priority; + } + prio.static_priority = thread.static_priority; + + if (!ReinterpretBytes(prio, &priority_)) { + LOG(ERROR) << "Couldn't set priority"; + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ThreadSnapshotLinux::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +const MemorySnapshot* ThreadSnapshotLinux::Stack() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &stack_; +} + +uint64_t ThreadSnapshotLinux::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +int ThreadSnapshotLinux::SuspendCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return 0; +} + +int ThreadSnapshotLinux::Priority() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return priority_; +} + +uint64_t ThreadSnapshotLinux::ThreadSpecificDataAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_specific_data_address_; +} + +std::vector ThreadSnapshotLinux::ExtraMemory() const { + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/linux/thread_snapshot_linux.h b/snapshot/linux/thread_snapshot_linux.h new file mode 100644 index 00000000..e0ce7adb --- /dev/null +++ b/snapshot/linux/thread_snapshot_linux.h @@ -0,0 +1,82 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_THREAD_SNAPSHOT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_THREAD_SNAPSHOT_LINUX_H_ + +#include + +#include "base/macros.h" +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/linux/memory_snapshot_linux.h" +#include "snapshot/linux/process_reader.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A ThreadSnapshot of a thread on a Linux system. +class ThreadSnapshotLinux final : public ThreadSnapshot { + public: + ThreadSnapshotLinux(); + ~ThreadSnapshotLinux() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReader for the process containing the + //! thread. + //! \param[in] thread The thread within the ProcessReader for + //! which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! a message logged. + bool Initialize(ProcessReader* process_reader, + const ProcessReader::Thread& thread); + + // ThreadSnapshot: + + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector ExtraMemory() const override; + + private: +#if defined(ARCH_CPU_X86_FAMILY) + union { + CPUContextX86 x86; + CPUContextX86_64 x86_64; + } context_union_; +#else +#error Port. +#endif // ARCH_CPU_X86_FAMILY + CPUContext context_; + MemorySnapshotLinux stack_; + LinuxVMAddress thread_specific_data_address_; + pid_t thread_id_; + int priority_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotLinux); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_THREAD_SNAPSHOT_LINUX_H_ diff --git a/snapshot/mac/mach_o_image_annotations_reader.cc b/snapshot/mac/mach_o_image_annotations_reader.cc index b67a0898..df8bebad 100644 --- a/snapshot/mac/mach_o_image_annotations_reader.cc +++ b/snapshot/mac/mach_o_image_annotations_reader.cc @@ -91,7 +91,7 @@ void MachOImageAnnotationsReader::ReadCrashReporterClientAnnotations( // This number was totally made up out of nowhere, but it seems prudent to // enforce some limit. - const size_t kMaxMessageSize = 1024; + constexpr size_t kMaxMessageSize = 1024; if (crash_info.message) { std::string message; if (process_reader_->Memory()->ReadCStringSizeLimited( diff --git a/snapshot/mac/mach_o_image_annotations_reader_test.cc b/snapshot/mac/mach_o_image_annotations_reader_test.cc index c73eebbf..68d6c4e0 100644 --- a/snapshot/mac/mach_o_image_annotations_reader_test.cc +++ b/snapshot/mac/mach_o_image_annotations_reader_test.cc @@ -212,7 +212,8 @@ class TestMachOImageAnnotationsReader final // dyld exposes its error_string at least as far back as Mac OS X 10.4. if (test_type_ == kCrashDyld) { - const char kExpectedAnnotation[] = "could not load inserted library"; + static constexpr char kExpectedAnnotation[] = + "could not load inserted library"; size_t expected_annotation_length = strlen(kExpectedAnnotation); bool found = false; for (const std::string& annotation : all_annotations_vector) { diff --git a/snapshot/mac/mach_o_image_reader.cc b/snapshot/mac/mach_o_image_reader.cc index f16aa554..7cb4040f 100644 --- a/snapshot/mac/mach_o_image_reader.cc +++ b/snapshot/mac/mach_o_image_reader.cc @@ -33,7 +33,7 @@ namespace { -const uint32_t kInvalidSegmentIndex = std::numeric_limits::max(); +constexpr uint32_t kInvalidSegmentIndex = std::numeric_limits::max(); } // namespace diff --git a/snapshot/mac/mach_o_image_reader_test.cc b/snapshot/mac/mach_o_image_reader_test.cc index 6d6e49d0..d6b801f8 100644 --- a/snapshot/mac/mach_o_image_reader_test.cc +++ b/snapshot/mac/mach_o_image_reader_test.cc @@ -47,16 +47,16 @@ namespace { // are different. #if defined(ARCH_CPU_64_BITS) using MachHeader = mach_header_64; -const uint32_t kMachMagic = MH_MAGIC_64; +constexpr uint32_t kMachMagic = MH_MAGIC_64; using SegmentCommand = segment_command_64; -const uint32_t kSegmentCommand = LC_SEGMENT_64; +constexpr uint32_t kSegmentCommand = LC_SEGMENT_64; using Section = section_64; using Nlist = nlist_64; #else using MachHeader = mach_header; -const uint32_t kMachMagic = MH_MAGIC; +constexpr uint32_t kMachMagic = MH_MAGIC; using SegmentCommand = segment_command; -const uint32_t kSegmentCommand = LC_SEGMENT; +constexpr uint32_t kSegmentCommand = LC_SEGMENT; using Section = section; // This needs to be called “struct nlist” because “nlist” without the struct @@ -65,9 +65,9 @@ using Nlist = struct nlist; #endif #if defined(ARCH_CPU_X86_64) -const int kCPUType = CPU_TYPE_X86_64; +constexpr int kCPUType = CPU_TYPE_X86_64; #elif defined(ARCH_CPU_X86) -const int kCPUType = CPU_TYPE_X86; +constexpr int kCPUType = CPU_TYPE_X86; #endif // Verifies that |expect_section| and |actual_section| agree. @@ -327,7 +327,8 @@ void ExpectSegmentCommands(const MachHeader* expect_image, // In some cases, the expected slide value for an image is unknown, because no // reasonable API to return it is provided. When this happens, use kSlideUnknown // to avoid checking the actual slide value against anything. -const mach_vm_size_t kSlideUnknown = std::numeric_limits::max(); +constexpr mach_vm_size_t kSlideUnknown = + std::numeric_limits::max(); // Verifies that |expect_image| is a vaild Mach-O header for the current system // by checking its |magic| and |cputype| fields. Then, verifies that the diff --git a/snapshot/mac/mach_o_image_segment_reader_test.cc b/snapshot/mac/mach_o_image_segment_reader_test.cc index 6e4d415a..2da97e85 100644 --- a/snapshot/mac/mach_o_image_segment_reader_test.cc +++ b/snapshot/mac/mach_o_image_segment_reader_test.cc @@ -53,7 +53,7 @@ TEST(MachOImageSegmentReader, SegmentNameString) { // Segment names defined in . All of these should come // through SegmentNameString() cleanly and without truncation. - const char* kSegmentTestData[] = { + static constexpr const char* kSegmentTestData[] = { SEG_TEXT, SEG_DATA, SEG_OBJC, @@ -91,7 +91,7 @@ TEST(MachOImageSegmentReader, SectionNameString) { // Section names defined in . All of these should come // through SectionNameString() cleanly and without truncation. - const char* kSectionTestData[] = { + static constexpr const char* kSectionTestData[] = { SECT_TEXT, SECT_FVMLIB_INIT0, SECT_FVMLIB_INIT1, @@ -115,12 +115,11 @@ TEST(MachOImageSegmentReader, SectionNameString) { } TEST(MachOImageSegmentReader, SegmentAndSectionNameString) { - struct SegmentAndSectionTestData { + static constexpr struct { const char* segment; const char* section; const char* output; - }; - const SegmentAndSectionTestData kSegmentAndSectionTestData[] = { + } kSegmentAndSectionTestData[] = { {"segment", "section", "segment,section"}, {"Segment", "Section", "Segment,Section"}, {"SEGMENT", "SECTION", "SEGMENT,SECTION"}, @@ -172,7 +171,7 @@ TEST(MachOImageSegmentReader, SegmentAndSectionNameString) { for (size_t index = 0; index < arraysize(kSegmentAndSectionTestData); ++index) { - const SegmentAndSectionTestData& test = kSegmentAndSectionTestData[index]; + const auto& test = kSegmentAndSectionTestData[index]; EXPECT_EQ(MachOImageSegmentReader::SegmentAndSectionNameString( test.segment, test.section), test.output) diff --git a/snapshot/mac/process_reader.cc b/snapshot/mac/process_reader.cc index 5e1f1c7b..43957977 100644 --- a/snapshot/mac/process_reader.cc +++ b/snapshot/mac/process_reader.cc @@ -611,7 +611,7 @@ mach_vm_address_t ProcessReader::CalculateStackRegion( // Regardless of whether the ABI requires a red zone, capture up to // kExtraCaptureSize additional bytes of stack, but only if present in the // region that was already found. - const mach_vm_size_t kExtraCaptureSize = 128; + constexpr mach_vm_size_t kExtraCaptureSize = 128; start_address = std::max(start_address >= kExtraCaptureSize ? start_address - kExtraCaptureSize : start_address, @@ -620,7 +620,7 @@ mach_vm_address_t ProcessReader::CalculateStackRegion( // Align start_address to a 16-byte boundary, which can help readers by // ensuring that data is aligned properly. This could page-align instead, // but that might be wasteful. - const mach_vm_size_t kDesiredAlignment = 16; + constexpr mach_vm_size_t kDesiredAlignment = 16; start_address &= ~(kDesiredAlignment - 1); DCHECK_GE(start_address, region_base); } @@ -685,7 +685,7 @@ void ProcessReader::LocateRedZone(mach_vm_address_t* const start_address, // x86_64 has a red zone. See AMD64 ABI 0.99.6, // http://www.x86-64.org/documentation/abi.pdf, section 3.2.2, “The Stack // Frame”. - const mach_vm_size_t kRedZoneSize = 128; + constexpr mach_vm_size_t kRedZoneSize = 128; mach_vm_address_t red_zone_base = *start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0; bool red_zone_ok = false; diff --git a/snapshot/mac/process_reader_test.cc b/snapshot/mac/process_reader_test.cc index 3a6a1c8b..6d675057 100644 --- a/snapshot/mac/process_reader_test.cc +++ b/snapshot/mac/process_reader_test.cc @@ -58,7 +58,7 @@ namespace crashpad { namespace test { namespace { -const char kDyldPath[] = "/usr/lib/dyld"; +constexpr char kDyldPath[] = "/usr/lib/dyld"; TEST(ProcessReader, SelfBasic) { ProcessReader process_reader; @@ -73,7 +73,7 @@ TEST(ProcessReader, SelfBasic) { EXPECT_EQ(process_reader.ProcessID(), getpid()); EXPECT_EQ(process_reader.ParentProcessID(), getppid()); - const char kTestMemory[] = "Some test memory"; + static constexpr char kTestMemory[] = "Some test memory"; char buffer[arraysize(kTestMemory)]; ASSERT_TRUE(process_reader.Memory()->Read( FromPointerCast(kTestMemory), @@ -82,7 +82,7 @@ TEST(ProcessReader, SelfBasic) { EXPECT_STREQ(kTestMemory, buffer); } -const char kTestMemory[] = "Read me from another process"; +constexpr char kTestMemory[] = "Read me from another process"; class ProcessReaderChild final : public MachMultiprocess { public: @@ -384,7 +384,7 @@ TEST(ProcessReader, SelfSeveralThreads) { ASSERT_TRUE(process_reader.Initialize(mach_task_self())); TestThreadPool thread_pool; - const size_t kChildThreads = 16; + constexpr size_t kChildThreads = 16; ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(kChildThreads)); // Build a map of all expected threads, keyed by each thread’s ID. The values @@ -523,13 +523,13 @@ class ProcessReaderThreadedChild final : public MachMultiprocess { TEST(ProcessReader, ChildOneThread) { // The main thread plus zero child threads equals one thread. - const size_t kChildThreads = 0; + constexpr size_t kChildThreads = 0; ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads); process_reader_threaded_child.Run(); } TEST(ProcessReader, ChildSeveralThreads) { - const size_t kChildThreads = 64; + constexpr size_t kChildThreads = 64; ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads); process_reader_threaded_child.Run(); } diff --git a/snapshot/mac/process_types/custom.cc b/snapshot/mac/process_types/custom.cc index 7839b403..25ecf3da 100644 --- a/snapshot/mac/process_types/custom.cc +++ b/snapshot/mac/process_types/custom.cc @@ -65,7 +65,7 @@ bool ReadIntoVersioned(ProcessReader* process_reader, template size_t dyld_all_image_infos::ExpectedSizeForVersion( decltype(dyld_all_image_infos::version) version) { - const size_t kSizeForVersion[] = { + static constexpr size_t kSizeForVersion[] = { offsetof(dyld_all_image_infos, infoArrayCount), // 0 offsetof(dyld_all_image_infos, libSystemInitialized), // 1 offsetof(dyld_all_image_infos, jitInfo), // 2 diff --git a/snapshot/mac/system_snapshot_mac.cc b/snapshot/mac/system_snapshot_mac.cc index 135fe98e..2bafb274 100644 --- a/snapshot/mac/system_snapshot_mac.cc +++ b/snapshot/mac/system_snapshot_mac.cc @@ -364,9 +364,9 @@ void SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status, // no transitions to or from daylight saving time occurred or will occur // within a year of the current date. Arizona, which last observed daylight // saving time in 1967, is an example. - const int kMonthDeltas[] = - { 0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, - 7, -7, 8, -8, 9, -9, 10, -10, 11, -11, 12, -12 }; + static constexpr int kMonthDeltas[] = + {0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, + 7, -7, 8, -8, 9, -9, 10, -10, 11, -11, 12, -12}; for (size_t index = 0; index < arraysize(kMonthDeltas) && !found_transition; ++index) { diff --git a/snapshot/mac/system_snapshot_mac_test.cc b/snapshot/mac/system_snapshot_mac_test.cc index f21d9a27..fb4166f9 100644 --- a/snapshot/mac/system_snapshot_mac_test.cc +++ b/snapshot/mac/system_snapshot_mac_test.cc @@ -210,7 +210,7 @@ TEST_F(SystemSnapshotMacTest, TimeZone) { // standard_name and daylight_name can be nullptr where no name exists to // verify, as may happen when some versions of the timezone database carry // invented names and others do not. - const struct { + static constexpr struct { const char* tz; bool observes_dst; float standard_offset_hours; diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index 667ca2cc..bd6c3d57 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -40,6 +40,22 @@ 'exception_snapshot.h', 'handle_snapshot.cc', 'handle_snapshot.h', + 'linux/cpu_context_linux.cc', + 'linux/cpu_context_linux.h', + 'linux/debug_rendezvous.cc', + 'linux/debug_rendezvous.h', + 'linux/elf_dynamic_array_reader.cc', + 'linux/elf_dynamic_array_reader.h', + 'linux/elf_image_reader.cc', + 'linux/elf_image_reader.h', + 'linux/elf_symbol_table_reader.cc', + 'linux/elf_symbol_table_reader.h', + 'linux/memory_snapshot_linux.cc', + 'linux/memory_snapshot_linux.h', + 'linux/process_reader.cc', + 'linux/process_reader.h', + 'linux/thread_snapshot_linux.cc', + 'linux/thread_snapshot_linux.h', 'mac/cpu_context_mac.cc', 'mac/cpu_context_mac.h', 'mac/exception_snapshot_mac.cc', @@ -130,7 +146,20 @@ ], }, }], - ] + ['OS=="linux" or OS=="android"', { + 'sources!': [ + 'capture_memory.cc', + 'capture_memory.h', + ], + }], + ], + 'target_conditions': [ + ['OS=="android"', { + 'sources/': [ + ['include', '^linux/'], + ], + }], + ], }, { 'variables': { diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index b31b0601..d1f1935b 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -70,6 +70,9 @@ 'cpu_context_test.cc', 'crashpad_info_client_options_test.cc', 'api/module_annotations_win_test.cc', + 'linux/debug_rendezvous_test.cc', + 'linux/elf_image_reader_test.cc', + 'linux/process_reader_test.cc', 'mac/cpu_context_mac_test.cc', 'mac/mach_o_image_annotations_reader_test.cc', 'mac/mach_o_image_reader_test.cc', @@ -107,6 +110,32 @@ 'crashpad_snapshot_test_image_reader_module', ], }], + ['OS=="linux" or OS=="android"', { + 'sources!': [ + 'crashpad_info_client_options_test.cc', + ], + 'copies': [{ + 'destination': '<(PRODUCT_DIR)', + 'files': [ + 'linux/test_exported_symbols.sym', + ], + }], + 'ldflags': [ + '-Wl,--dynamic-list=test_exported_symbols.sym', + ], + 'link_settings': { + 'libraries': [ + '-ldl', + ], + }, + }], + ], + 'target_conditions': [ + ['OS=="android"', { + 'sources/': [ + ['include', '^linux/'], + ], + }], ], }, { diff --git a/snapshot/win/exception_snapshot_win_test.cc b/snapshot/win/exception_snapshot_win_test.cc index 286e7a6f..cc8dd9d4 100644 --- a/snapshot/win/exception_snapshot_win_test.cc +++ b/snapshot/win/exception_snapshot_win_test.cc @@ -102,7 +102,7 @@ class CrashingDelegate : public ExceptionHandlerServer::Delegate { // Verify the exception happened at the expected location with a bit of // slop space to allow for reading the current PC before the exception // happens. See TestCrashingChild(). - const uint64_t kAllowedOffset = 64; + constexpr uint64_t kAllowedOffset = 64; EXPECT_GT(snapshot.Exception()->ExceptionAddress(), break_near_); EXPECT_LT(snapshot.Exception()->ExceptionAddress(), break_near_ + kAllowedOffset); @@ -205,7 +205,7 @@ class SimulateDelegate : public ExceptionHandlerServer::Delegate { // Verify the dump was captured at the expected location with some slop // space. - const uint64_t kAllowedOffset = 64; + constexpr uint64_t kAllowedOffset = 64; EXPECT_GT(snapshot.Exception()->Context()->InstructionPointer(), dump_near_); EXPECT_LT(snapshot.Exception()->Context()->InstructionPointer(), diff --git a/snapshot/win/pe_image_reader_test.cc b/snapshot/win/pe_image_reader_test.cc index 041d72a2..bf53279d 100644 --- a/snapshot/win/pe_image_reader_test.cc +++ b/snapshot/win/pe_image_reader_test.cc @@ -128,7 +128,7 @@ TEST(PEImageReader, VSFixedFileInfo_OneModule) { ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), ProcessSuspensionState::kRunning)); - const wchar_t kModuleName[] = L"kernel32.dll"; + static constexpr wchar_t kModuleName[] = L"kernel32.dll"; const HMODULE module_handle = GetModuleHandle(kModuleName); ASSERT_TRUE(module_handle) << ErrorMessage("GetModuleHandle"); diff --git a/snapshot/win/process_reader_win_test.cc b/snapshot/win/process_reader_win_test.cc index 87e72a94..9e62e118 100644 --- a/snapshot/win/process_reader_win_test.cc +++ b/snapshot/win/process_reader_win_test.cc @@ -41,14 +41,14 @@ TEST(ProcessReaderWin, SelfBasic) { EXPECT_EQ(process_reader.GetProcessInfo().ProcessID(), GetCurrentProcessId()); - const char kTestMemory[] = "Some test memory"; + static constexpr char kTestMemory[] = "Some test memory"; char buffer[arraysize(kTestMemory)]; ASSERT_TRUE(process_reader.ReadMemory( reinterpret_cast(kTestMemory), sizeof(kTestMemory), &buffer)); EXPECT_STREQ(kTestMemory, buffer); } -const char kTestMemory[] = "Read me from another process"; +constexpr char kTestMemory[] = "Read me from another process"; class ProcessReaderChild final : public WinMultiprocess { public: diff --git a/snapshot/win/process_snapshot_win.cc b/snapshot/win/process_snapshot_win.cc index f580fd09..5e3b8be4 100644 --- a/snapshot/win/process_snapshot_win.cc +++ b/snapshot/win/process_snapshot_win.cc @@ -528,7 +528,7 @@ WinVMSize ProcessSnapshotWin::DetermineSizeOfEnvironmentBlock( &env_block[0]); env_block.resize( static_cast(bytes_read / sizeof(env_block[0]))); - const wchar_t terminator[] = { 0, 0 }; + static constexpr wchar_t terminator[] = {0, 0}; size_t at = env_block.find(std::wstring(terminator, arraysize(terminator))); if (at != std::wstring::npos) env_block.resize(at + arraysize(terminator)); @@ -554,7 +554,7 @@ void ProcessSnapshotWin::ReadLock( AddMemorySnapshot( start, sizeof(process_types::RTL_CRITICAL_SECTION), into); - const decltype(critical_section.DebugInfo) kInvalid = + constexpr decltype(critical_section.DebugInfo) kInvalid = static_cast(-1); if (critical_section.DebugInfo == kInvalid) return; diff --git a/snapshot/win/system_snapshot_win.cc b/snapshot/win/system_snapshot_win.cc index a8f6fcb1..4e4114ff 100644 --- a/snapshot/win/system_snapshot_win.cc +++ b/snapshot/win/system_snapshot_win.cc @@ -98,7 +98,7 @@ void SystemSnapshotWin::Initialize(ProcessReaderWin* process_reader) { os_server_ = version_info.wProductType != VER_NT_WORKSTATION; } - const wchar_t kSystemDll[] = L"kernel32.dll"; + static constexpr wchar_t kSystemDll[] = L"kernel32.dll"; VS_FIXEDFILEINFO ffi; if (GetModuleVersionAndType(base::FilePath(kSystemDll), &ffi)) { std::string flags_string = GetStringForFileFlags(ffi.dwFileFlags); @@ -192,7 +192,7 @@ void SystemSnapshotWin::CPUFrequency(uint64_t* current_hz, *max_hz = 0; return; } - const uint64_t kMhzToHz = static_cast(1E6); + constexpr uint64_t kMhzToHz = static_cast(1E6); *current_hz = std::max_element(info.begin(), info.end(), [](const PROCESSOR_POWER_INFORMATION& a, diff --git a/test/file.cc b/test/file.cc index 75af744d..04b4625f 100644 --- a/test/file.cc +++ b/test/file.cc @@ -28,11 +28,11 @@ bool FileExists(const base::FilePath& path) { #if defined(OS_POSIX) struct stat st; int rv = lstat(path.value().c_str(), &st); - const char stat_function[] = "lstat"; + static constexpr char stat_function[] = "lstat"; #elif defined(OS_WIN) struct _stat st; int rv = _wstat(path.value().c_str(), &st); - const char stat_function[] = "_wstat"; + static constexpr char stat_function[] = "_wstat"; #else #error "Not implemented" #endif @@ -48,11 +48,11 @@ FileOffset FileSize(const base::FilePath& path) { #if defined(OS_POSIX) struct stat st; int rv = lstat(path.value().c_str(), &st); - const char stat_function[] = "lstat"; + static constexpr char stat_function[] = "lstat"; #elif defined(OS_WIN) struct _stati64 st; int rv = _wstati64(path.value().c_str(), &st); - const char stat_function[] = "_wstati64"; + static constexpr char stat_function[] = "_wstati64"; #else #error "Not implemented" #endif diff --git a/test/hex_string_test.cc b/test/hex_string_test.cc index 1915a856..68745e6f 100644 --- a/test/hex_string_test.cc +++ b/test/hex_string_test.cc @@ -24,7 +24,7 @@ namespace { TEST(HexString, HexString) { EXPECT_EQ(BytesToHexString(nullptr, 0), ""); - const char kBytes[] = "Abc123xyz \x0a\x7f\xf0\x9f\x92\xa9_"; + static constexpr char kBytes[] = "Abc123xyz \x0a\x7f\xf0\x9f\x92\xa9_"; EXPECT_EQ(BytesToHexString(kBytes, arraysize(kBytes)), "41626331323378797a200a7ff09f92a95f00"); } diff --git a/test/scoped_temp_dir_win.cc b/test/scoped_temp_dir_win.cc index 0413ec42..d9707af6 100644 --- a/test/scoped_temp_dir_win.cc +++ b/test/scoped_temp_dir_win.cc @@ -38,7 +38,7 @@ base::FilePath GenerateCandidateName() { return system_temp_dir.Append(new_dir_name); } -const int kRetries = 50; +constexpr int kRetries = 50; } // namespace diff --git a/test/win/win_child_process.cc b/test/win/win_child_process.cc index 4c821a0c..44177f63 100644 --- a/test/win/win_child_process.cc +++ b/test/win/win_child_process.cc @@ -35,7 +35,7 @@ namespace test { namespace { -const char kIsMultiprocessChild[] = "--is-multiprocess-child"; +constexpr char kIsMultiprocessChild[] = "--is-multiprocess-child"; bool GetSwitch(const char* switch_name, std::string* value) { int num_args; diff --git a/third_party/zlib/zlib.gyp b/third_party/zlib/zlib.gyp index f92cdf26..b6a6bc91 100644 --- a/third_party/zlib/zlib.gyp +++ b/third_party/zlib/zlib.gyp @@ -103,10 +103,20 @@ 'zlib_crashpad.h', ], 'conditions': [ - ['target_arch=="x86" or target_arch=="amd64"', { + ['target_arch=="ia32" or target_arch=="x64"', { 'sources!': [ 'zlib/simd_stub.c', ], + 'cflags': [ + '-msse4.2', + '-mpclmul', + ], + 'xcode_settings': { + 'OTHER_CFLAGS': [ + '-msse4.2', + '-mpclmul', + ], + }, }, { 'sources!': [ 'zlib/crc_folding.c', diff --git a/tools/crashpad_database_util.cc b/tools/crashpad_database_util.cc index b2c4cb49..cc21698b 100644 --- a/tools/crashpad_database_util.cc +++ b/tools/crashpad_database_util.cc @@ -92,7 +92,7 @@ struct Options { // performed. Various string representations of a boolean are recognized // case-insensitively. bool StringToBool(const char* string, bool* boolean) { - const char* const kFalseWords[] = { + static constexpr const char* kFalseWords[] = { "0", "false", "no", @@ -100,7 +100,7 @@ bool StringToBool(const char* string, bool* boolean) { "disabled", "clear", }; - const char* const kTrueWords[] = { + static constexpr const char* kTrueWords[] = { "1", "true", "yes", @@ -153,7 +153,7 @@ bool StringToTime(const char* string, time_t* out_time, bool utc) { const char* end = string + strlen(string); - const char* const kFormats[] = { + static constexpr const char* kFormats[] = { "%Y-%m-%d %H:%M:%S %Z", "%Y-%m-%d %H:%M:%S", "%+", @@ -294,7 +294,7 @@ int DatabaseUtilMain(int argc, char* argv[]) { kOptionVersion = -3, }; - const option long_options[] = { + static constexpr option long_options[] = { {"create", no_argument, nullptr, kOptionCreate}, {"database", required_argument, nullptr, kOptionDatabase}, {"show-client-id", no_argument, nullptr, kOptionShowClientID}, diff --git a/tools/crashpad_http_upload.cc b/tools/crashpad_http_upload.cc index d4ec3f9c..f8a85c43 100644 --- a/tools/crashpad_http_upload.cc +++ b/tools/crashpad_http_upload.cc @@ -74,7 +74,7 @@ int HTTPUploadMain(int argc, char* argv[]) { } options = {}; options.upload_gzip = true; - const option long_options[] = { + static constexpr option long_options[] = { {"file", required_argument, nullptr, kOptionFile}, {"no-upload-gzip", no_argument, nullptr, kOptionNoUploadGzip}, {"output", required_argument, nullptr, kOptionOutput}, diff --git a/tools/generate_dump.cc b/tools/generate_dump.cc index b98b1f4f..956af81b 100644 --- a/tools/generate_dump.cc +++ b/tools/generate_dump.cc @@ -85,7 +85,7 @@ int GenerateDumpMain(int argc, char* argv[]) { } options = {}; options.suspend = true; - const option long_options[] = { + static constexpr option long_options[] = { {"no-suspend", no_argument, nullptr, kOptionNoSuspend}, {"output", required_argument, nullptr, kOptionOutput}, {"help", no_argument, nullptr, kOptionHelp}, diff --git a/tools/mac/catch_exception_tool.cc b/tools/mac/catch_exception_tool.cc index 3a535cb2..d4dc3c12 100644 --- a/tools/mac/catch_exception_tool.cc +++ b/tools/mac/catch_exception_tool.cc @@ -214,7 +214,7 @@ int CatchExceptionToolMain(int argc, char* argv[]) { Options options = {}; - const option long_options[] = { + static constexpr option long_options[] = { {"file", required_argument, nullptr, kOptionFile}, {"mach-service", required_argument, nullptr, kOptionMachService}, {"persistent", no_argument, nullptr, kOptionPersistent}, diff --git a/tools/mac/exception_port_tool.cc b/tools/mac/exception_port_tool.cc index 3faa00fe..a047e964 100644 --- a/tools/mac/exception_port_tool.cc +++ b/tools/mac/exception_port_tool.cc @@ -106,8 +106,8 @@ struct ExceptionHandlerDescription { std::string handler; }; -const char kHandlerNull[] = "NULL"; -const char kHandlerBootstrapColon[] = "bootstrap:"; +constexpr char kHandlerNull[] = "NULL"; +constexpr char kHandlerBootstrapColon[] = "bootstrap:"; // Populates |description| based on a textual representation in // |handler_string_ro|, returning true on success and false on failure (parse @@ -120,11 +120,11 @@ const char kHandlerBootstrapColon[] = "bootstrap:"; // SymbolicConstantsMach. bool ParseHandlerString(const char* handler_string_ro, ExceptionHandlerDescription* description) { - const char kTargetEquals[] = "target="; - const char kMaskEquals[] = "mask="; - const char kBehaviorEquals[] = "behavior="; - const char kFlavorEquals[] = "flavor="; - const char kHandlerEquals[] = "handler="; + static constexpr char kTargetEquals[] = "target="; + static constexpr char kMaskEquals[] = "mask="; + static constexpr char kBehaviorEquals[] = "behavior="; + static constexpr char kFlavorEquals[] = "flavor="; + static constexpr char kHandlerEquals[] = "handler="; std::string handler_string(handler_string_ro); char* handler_string_c = &handler_string[0]; @@ -400,7 +400,7 @@ int ExceptionPortToolMain(int argc, char* argv[]) { bool numeric; } options = {}; - const option long_options[] = { + static constexpr option long_options[] = { {"set-handler", required_argument, nullptr, kOptionSetPort}, {"show-bootstrap", required_argument, nullptr, kOptionShowBootstrap}, {"pid", required_argument, nullptr, kOptionPid}, diff --git a/tools/mac/on_demand_service_tool.mm b/tools/mac/on_demand_service_tool.mm index c6b69520..70de1b52 100644 --- a/tools/mac/on_demand_service_tool.mm +++ b/tools/mac/on_demand_service_tool.mm @@ -81,7 +81,7 @@ int OnDemandServiceToolMain(int argc, char* argv[]) { std::vector mach_services; } options = {}; - const option long_options[] = { + static constexpr option long_options[] = { {"load", no_argument, nullptr, kOptionLoadJob}, {"unload", no_argument, nullptr, kOptionUnloadJob}, {"label", required_argument, nullptr, kOptionJobLabel}, diff --git a/tools/mac/run_with_crashpad.cc b/tools/mac/run_with_crashpad.cc index 135d6d98..39095145 100644 --- a/tools/mac/run_with_crashpad.cc +++ b/tools/mac/run_with_crashpad.cc @@ -84,7 +84,7 @@ int RunWithCrashpadMain(int argc, char* argv[]) { kOptionVersion = -3, }; - const option long_options[] = { + static constexpr option long_options[] = { {"handler", required_argument, nullptr, kOptionHandler}, {"annotation", required_argument, nullptr, kOptionAnnotation}, {"database", required_argument, nullptr, kOptionDatabase}, diff --git a/util/file/delimited_file_reader_test.cc b/util/file/delimited_file_reader_test.cc index 84ecb846..79e331f7 100644 --- a/util/file/delimited_file_reader_test.cc +++ b/util/file/delimited_file_reader_test.cc @@ -257,7 +257,7 @@ TEST(DelimitedFileReader, ReallyLongMultiLineFile) { } TEST(DelimitedFileReader, EmbeddedNUL) { - const char kString[] = "embedded\0NUL\n"; + static constexpr char kString[] = "embedded\0NUL\n"; StringFile string_file; string_file.SetString(std::string(kString, arraysize(kString) - 1)); DelimitedFileReader delimited_file_reader(&string_file); @@ -275,7 +275,7 @@ TEST(DelimitedFileReader, EmbeddedNUL) { } TEST(DelimitedFileReader, NULDelimiter) { - const char kString[] = "aa\0b\0ccc\0"; + static constexpr char kString[] = "aa\0b\0ccc\0"; StringFile string_file; string_file.SetString(std::string(kString, arraysize(kString) - 1)); DelimitedFileReader delimited_file_reader(&string_file); @@ -299,7 +299,8 @@ TEST(DelimitedFileReader, NULDelimiter) { } TEST(DelimitedFileReader, EdgeCases) { - const size_t kSizes[] = {4094, 4095, 4096, 4097, 8190, 8191, 8192, 8193}; + static constexpr size_t kSizes[] = + {4094, 4095, 4096, 4097, 8190, 8191, 8192, 8193}; for (size_t index = 0; index < arraysize(kSizes); ++index) { size_t size = kSizes[index]; SCOPED_TRACE( diff --git a/util/file/file_io.h b/util/file/file_io.h index c70dff30..044a0a69 100644 --- a/util/file/file_io.h +++ b/util/file/file_io.h @@ -45,11 +45,12 @@ using FileOffset = off_t; //! \brief Scoped wrapper of a FileHandle. using ScopedFileHandle = base::ScopedFD; +//! \brief The return value of read and write calls. +using FileOperationResult = ssize_t; + //! \brief A value that can never be a valid FileHandle. const FileHandle kInvalidFileHandle = -1; -using FileOperationResult = ssize_t; - #elif defined(OS_WIN) using FileHandle = HANDLE; @@ -109,19 +110,28 @@ enum class StdioStream { namespace internal { +#if defined(OS_POSIX) || DOXYGEN + //! \brief The name of the native read function used by ReadFile(). //! //! This value may be useful for logging. //! //! \sa kNativeWriteFunctionName -extern const char kNativeReadFunctionName[]; +constexpr char kNativeReadFunctionName[] = "read"; //! \brief The name of the native write function used by WriteFile(). //! //! This value may be useful for logging. //! //! \sa kNativeReadFunctionName -extern const char kNativeWriteFunctionName[]; +constexpr char kNativeWriteFunctionName[] = "write"; + +#elif defined(OS_WIN) + +constexpr char kNativeReadFunctionName[] = "ReadFile"; +constexpr char kNativeWriteFunctionName[] = "WriteFile"; + +#endif //! \brief The internal implementation of ReadFileExactly() and its wrappers. //! diff --git a/util/file/file_io_posix.cc b/util/file/file_io_posix.cc index 49441244..60a36996 100644 --- a/util/file/file_io_posix.cc +++ b/util/file/file_io_posix.cc @@ -96,9 +96,6 @@ FileHandle OpenFileForOutput(int rdwr_or_wronly, namespace internal { -const char kNativeReadFunctionName[] = "read"; -const char kNativeWriteFunctionName[] = "write"; - FileOperationResult NativeWriteFile(FileHandle file, const void* buffer, size_t size) { diff --git a/util/file/file_io_test.cc b/util/file/file_io_test.cc index a784abd2..fdcf7e9a 100644 --- a/util/file/file_io_test.cc +++ b/util/file/file_io_test.cc @@ -398,7 +398,7 @@ void TestOpenFileForWrite(FileHandle (*opener)(const base::FilePath&, EXPECT_TRUE(FileExists(file_path_1)); EXPECT_EQ(FileSize(file_path_1), 0); - const char data = '%'; + constexpr char data = '%'; EXPECT_TRUE(LoggingWriteFile(file_handle.get(), &data, sizeof(data))); // Close file_handle to ensure that the write is flushed to disk. @@ -660,7 +660,7 @@ TEST(FileIO, FileSizeByHandle) { ASSERT_NE(file_handle.get(), kInvalidFileHandle); EXPECT_EQ(LoggingFileSizeByHandle(file_handle.get()), 0); - const char data[] = "zippyzap"; + static constexpr char data[] = "zippyzap"; ASSERT_TRUE(LoggingWriteFile(file_handle.get(), &data, sizeof(data))); EXPECT_EQ(LoggingFileSizeByHandle(file_handle.get()), 9); diff --git a/util/file/file_io_win.cc b/util/file/file_io_win.cc index 467fc3c8..7428034a 100644 --- a/util/file/file_io_win.cc +++ b/util/file/file_io_win.cc @@ -84,9 +84,6 @@ FileHandle OpenFileForOutput(DWORD access, namespace internal { -const char kNativeReadFunctionName[] = "ReadFile"; -const char kNativeWriteFunctionName[] = "WriteFile"; - FileOperationResult NativeWriteFile(FileHandle file, const void* buffer, size_t size) { diff --git a/util/linux/address_types.h b/util/linux/address_types.h index 8c8f13d7..fb17500d 100644 --- a/util/linux/address_types.h +++ b/util/linux/address_types.h @@ -27,6 +27,10 @@ using LinuxVMAddress = uint64_t; //! LinuxVMAddress), potentially across bitness. using LinuxVMSize = uint64_t; +//! \brief Type used to represent an offset from a LinuxVMAddress, potentially +//! across bitness. +using LinuxVMOffset = int64_t; + } // namespace crashpad #endif // CRASHPAD_UTIL_LINUX_ADDRESS_TYPES_H_ diff --git a/util/linux/memory_map.cc b/util/linux/memory_map.cc index 4283cb84..a393f0b0 100644 --- a/util/linux/memory_map.cc +++ b/util/linux/memory_map.cc @@ -109,9 +109,9 @@ ParseResult ParseMapsLine(DelimitedFileReader* maps_file_reader, // TODO(jperaza): set bitness properly #if defined(ARCH_CPU_64_BITS) - const bool is_64_bit = true; + constexpr bool is_64_bit = true; #else - const bool is_64_bit = false; + constexpr bool is_64_bit = false; #endif MemoryMap::Mapping mapping; @@ -202,6 +202,16 @@ MemoryMap::MemoryMap() : mappings_(), initialized_() {} MemoryMap::~MemoryMap() {} +bool MemoryMap::Mapping::Equals(const Mapping& other) const { + DCHECK_EQ(range.Is64Bit(), other.range.Is64Bit()); + return range.Base() == other.range.Base() && + range.Size() == other.range.Size() && name == other.name && + offset == other.offset && device == other.device && + inode == other.inode && readable == other.readable && + writable == other.writable && executable == other.executable && + shareable == other.shareable; +} + bool MemoryMap::Initialize(pid_t pid) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); @@ -267,4 +277,40 @@ const MemoryMap::Mapping* MemoryMap::FindMappingWithName( return nullptr; } +const MemoryMap::Mapping* MemoryMap::FindFileMmapStart( + const Mapping& mapping) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + size_t index = 0; + for (; index < mappings_.size(); ++index) { + if (mappings_[index].Equals(mapping)) { + break; + } + } + if (index >= mappings_.size()) { + LOG(ERROR) << "mapping not found"; + return nullptr; + } + + // If the mapping is anonymous, as is for the VDSO, there is no mapped file to + // find the start of, so just return the input mapping. + if (mapping.device == 0 && mapping.inode == 0) { + return &mappings_[index]; + } + + do { + // There may by anonymous mappings or other files mapped into the holes, + // so check that the mapping uses the same file as the input, but keep + // searching if it doesn't. + if (mappings_[index].device == mapping.device && + mappings_[index].inode == mapping.inode && + mappings_[index].offset == 0) { + return &mappings_[index]; + } + } while (index--); + + LOG(ERROR) << "mapping not found"; + return nullptr; +} + } // namespace crashpad diff --git a/util/linux/memory_map.h b/util/linux/memory_map.h index 22f4448b..6e5b40c8 100644 --- a/util/linux/memory_map.h +++ b/util/linux/memory_map.h @@ -37,6 +37,7 @@ class MemoryMap { //! \brief Information about a mapped region of memory. struct Mapping { Mapping(); + bool Equals(const Mapping& other) const; std::string name; CheckedLinuxAddressRange range; @@ -74,6 +75,21 @@ class MemoryMap { //! it was obtained from. const Mapping* FindMappingWithName(const std::string& name) const; + //! \brief Find the first Mapping in a series of mappings for the same file. + //! + //! Executables and libaries are typically loaded into several mappings with + //! varying permissions for different segments. This method searches for the + //! mapping with the highest address at or below \a mapping, which maps the + //! same file as \a mapping from file offset 0. + //! + //! If \a mapping is not found, `nullptr` is returned. If \a mapping is found + //! but does not map a file, \a mapping is returned. + //! + //! \param[in] mapping A Mapping whose series to find the start of. + //! \return The first Mapping in the series or `nullptr` on failure with a + //! message logged. + const Mapping* FindFileMmapStart(const Mapping& mapping) const; + private: std::vector mappings_; InitializationStateDcheck initialized_; diff --git a/util/linux/memory_map_test.cc b/util/linux/memory_map_test.cc index aab5c4ac..8578a7e5 100644 --- a/util/linux/memory_map_test.cc +++ b/util/linux/memory_map_test.cc @@ -22,6 +22,7 @@ #include "base/files/file_path.h" #include "base/strings/stringprintf.h" +#include "build/build_config.h" #include "gtest/gtest.h" #include "test/errors.h" #include "test/file.h" @@ -78,6 +79,18 @@ TEST(MemoryMap, SelfBasic) { EXPECT_TRUE(mapping->shareable); } +void InitializeFile(const base::FilePath& path, + size_t size, + ScopedFileHandle* handle) { + ASSERT_FALSE(FileExists(path)); + + handle->reset(LoggingOpenFileForReadAndWrite( + path, FileWriteMode::kReuseOrCreate, FilePermissions::kOwnerOnly)); + ASSERT_TRUE(handle->is_valid()); + std::string file_contents(size, std::string::value_type()); + CheckedWriteFile(handle->get(), file_contents.c_str(), file_contents.size()); +} + class MapChildTest : public Multiprocess { public: MapChildTest() : Multiprocess(), page_size_(getpagesize()) {} @@ -169,13 +182,8 @@ class MapChildTest : public Multiprocess { ScopedTempDir temp_dir; base::FilePath path = temp_dir.path().Append(FILE_PATH_LITERAL("test_file")); - ASSERT_FALSE(FileExists(path)); - std::string path_string = path.value(); - ScopedFileHandle handle(LoggingOpenFileForReadAndWrite( - path, FileWriteMode::kReuseOrCreate, FilePermissions::kOwnerOnly)); - ASSERT_TRUE(handle.is_valid()); - std::string file_contents(page_size_ * 2, std::string::value_type()); - CheckedWriteFile(handle.get(), file_contents.c_str(), file_contents.size()); + ScopedFileHandle handle; + ASSERT_NO_FATAL_FAILURE(InitializeFile(path, page_size_ * 2, &handle)); ScopedMmap file_mapping; ASSERT_TRUE(file_mapping.ResetMmap(nullptr, @@ -188,9 +196,9 @@ class MapChildTest : public Multiprocess { auto mapped_file_address = file_mapping.addr_as(); CheckedWriteFile( WritePipeHandle(), &mapped_file_address, sizeof(mapped_file_address)); - LinuxVMSize path_length = path_string.size(); + LinuxVMSize path_length = path.value().size(); CheckedWriteFile(WritePipeHandle(), &path_length, sizeof(path_length)); - CheckedWriteFile(WritePipeHandle(), path_string.c_str(), path_length); + CheckedWriteFile(WritePipeHandle(), path.value().c_str(), path_length); CheckedReadFileAtEOF(ReadPipeHandle()); } @@ -337,6 +345,113 @@ TEST(MemoryMap, MapRunningChild) { test.Run(); } +// Expects first and third pages from mapping_start to refer to the same mapped +// file. The second page should not. +void ExpectFindFileMmapStart(LinuxVMAddress mapping_start, + LinuxVMSize page_size) { + MemoryMap map; + ASSERT_TRUE(map.Initialize(getpid())); + + auto mapping1 = map.FindMapping(mapping_start); + ASSERT_TRUE(mapping1); + auto mapping2 = map.FindMapping(mapping_start + page_size); + ASSERT_TRUE(mapping2); + auto mapping3 = map.FindMapping(mapping_start + page_size * 2); + ASSERT_TRUE(mapping3); + + ASSERT_NE(mapping1, mapping2); + ASSERT_NE(mapping2, mapping3); + + EXPECT_EQ(map.FindFileMmapStart(*mapping1), mapping1); + EXPECT_EQ(map.FindFileMmapStart(*mapping2), mapping2); + EXPECT_EQ(map.FindFileMmapStart(*mapping3), mapping1); +} + +TEST(MemoryMap, FindFileMmapStart) { + const size_t page_size = getpagesize(); + + ScopedTempDir temp_dir; + base::FilePath path = + temp_dir.path().Append(FILE_PATH_LITERAL("FindFileMmapStartTestFile")); + ScopedFileHandle handle; + size_t file_length = page_size * 3; + ASSERT_NO_FATAL_FAILURE(InitializeFile(path, file_length, &handle)); + + ScopedMmap file_mapping; + ASSERT_TRUE(file_mapping.ResetMmap( + nullptr, file_length, PROT_READ, MAP_PRIVATE, handle.get(), 0)); + auto mapping_start = file_mapping.addr_as(); + + // Change the permissions on the second page to split the mapping into three + // parts. + ASSERT_EQ(mprotect(file_mapping.addr_as() + page_size, + page_size, + PROT_READ | PROT_WRITE), + 0); + + // Basic + { + MemoryMap map; + ASSERT_TRUE(map.Initialize(getpid())); + + auto mapping1 = map.FindMapping(mapping_start); + ASSERT_TRUE(mapping1); + auto mapping2 = map.FindMapping(mapping_start + page_size); + ASSERT_TRUE(mapping2); + auto mapping3 = map.FindMapping(mapping_start + page_size * 2); + ASSERT_TRUE(mapping3); + + ASSERT_NE(mapping1, mapping2); + ASSERT_NE(mapping2, mapping3); + + EXPECT_EQ(map.FindFileMmapStart(*mapping1), mapping1); + EXPECT_EQ(map.FindFileMmapStart(*mapping2), mapping1); + EXPECT_EQ(map.FindFileMmapStart(*mapping3), mapping1); + +#if defined(ARCH_CPU_64_BITS) + constexpr bool is_64_bit = true; +#else + constexpr bool is_64_bit = false; +#endif + MemoryMap::Mapping bad_mapping; + bad_mapping.range.SetRange(is_64_bit, 0, 1); + EXPECT_EQ(map.FindFileMmapStart(bad_mapping), nullptr); + } + + // Make the second page an anonymous mapping + file_mapping.ResetAddrLen(file_mapping.addr_as(), page_size); + ScopedMmap page2_mapping; + ASSERT_TRUE(page2_mapping.ResetMmap(file_mapping.addr_as() + page_size, + page_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, + 0)); + ScopedMmap page3_mapping; + ASSERT_TRUE( + page3_mapping.ResetMmap(file_mapping.addr_as() + page_size * 2, + page_size, + PROT_READ, + MAP_PRIVATE | MAP_FIXED, + handle.get(), + page_size * 2)); + ExpectFindFileMmapStart(mapping_start, page_size); + + // Map the second page to another file. + ScopedFileHandle handle2; + base::FilePath path2 = + temp_dir.path().Append(FILE_PATH_LITERAL("FindFileMmapStartTestFile2")); + ASSERT_NO_FATAL_FAILURE(InitializeFile(path2, page_size, &handle2)); + + page2_mapping.ResetMmap(file_mapping.addr_as() + page_size, + page_size, + PROT_READ, + MAP_PRIVATE | MAP_FIXED, + handle2.get(), + 0); + ExpectFindFileMmapStart(mapping_start, page_size); +} + } // namespace } // namespace test } // namespace crashpad diff --git a/util/linux/proc_stat_reader.cc b/util/linux/proc_stat_reader.cc new file mode 100644 index 00000000..b4cc3ffd --- /dev/null +++ b/util/linux/proc_stat_reader.cc @@ -0,0 +1,177 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util/linux/proc_stat_reader.h" + +#include +#include +#include + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "util/file/file_io.h" +#include "util/misc/lexing.h" + +namespace crashpad { + +namespace { + +void SubtractTimespec(const timespec& t1, + const timespec& t2, + timespec* result) { + result->tv_sec = t1.tv_sec - t2.tv_sec; + result->tv_nsec = t1.tv_nsec - t2.tv_nsec; + if (result->tv_nsec < 0) { + result->tv_sec -= 1; + result->tv_nsec += static_cast(1E9); + } +} + +void TimespecToTimeval(const timespec& ts, timeval* tv) { + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; +} + +long GetClockTicksPerSecond() { + long clock_ticks_per_s = sysconf(_SC_CLK_TCK); + if (clock_ticks_per_s <= 0) { + PLOG(ERROR) << "sysconf"; + } + return clock_ticks_per_s; +} + +} // namespace + +ProcStatReader::ProcStatReader() + : contents_(), third_column_position_(0), initialized_() {} + +ProcStatReader::~ProcStatReader() {} + +bool ProcStatReader::Initialize(pid_t tid) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + if (!ReadFile(tid)) { + return false; + } + + // The first column is process ID and the second column is the executable name + // in parentheses. This class only cares about columns after the second, so + // find the start of the third here and save it for later. + // The executable name may have parentheses itself, so find the end of the + // second column by working backwards to find the last closing parens. + size_t stat_pos = contents_.rfind(')'); + if (stat_pos == std::string::npos) { + LOG(ERROR) << "format error"; + return false; + } + + third_column_position_ = contents_.find(' ', stat_pos); + if (third_column_position_ == std::string::npos || + ++third_column_position_ >= contents_.size()) { + LOG(ERROR) << "format error"; + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcStatReader::UserCPUTime(timeval* user_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return ReadTimeAtIndex(13, user_time); +} + +bool ProcStatReader::SystemCPUTime(timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return ReadTimeAtIndex(14, system_time); +} + +bool ProcStatReader::StartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + timeval time_after_boot; + if (!ReadTimeAtIndex(21, &time_after_boot)) { + return false; + } + + timespec uptime; + if (clock_gettime(CLOCK_BOOTTIME, &uptime) != 0) { + PLOG(ERROR) << "clock_gettime"; + return false; + } + + timespec current_time; + if (clock_gettime(CLOCK_REALTIME, ¤t_time) != 0) { + PLOG(ERROR) << "clock_gettime"; + return false; + } + + timespec boot_time_ts; + SubtractTimespec(current_time, uptime, &boot_time_ts); + timeval boot_time_tv; + TimespecToTimeval(boot_time_ts, &boot_time_tv); + timeradd(&boot_time_tv, &time_after_boot, start_time); + + return true; +} + +bool ProcStatReader::ReadFile(pid_t tid) { + char path[32]; + snprintf(path, arraysize(path), "/proc/%d/stat", tid); + if (!LoggingReadEntireFile(base::FilePath(path), &contents_)) { + return false; + } + return true; +} + +bool ProcStatReader::FindColumn(int col_index, const char** column) const { + size_t position = third_column_position_; + for (int index = 2; index < col_index; ++index) { + position = contents_.find(' ', position); + if (position == std::string::npos) { + break; + } + ++position; + } + if (position >= contents_.size()) { + LOG(ERROR) << "format error"; + return false; + } + *column = &contents_[position]; + return true; +} + +bool ProcStatReader::ReadTimeAtIndex(int index, timeval* time_val) const { + const char* ticks_ptr; + if (!FindColumn(index, &ticks_ptr)) { + return false; + } + + uint64_t ticks; + if (!AdvancePastNumber(&ticks_ptr, &ticks)) { + LOG(ERROR) << "format error"; + return false; + } + + static long clock_ticks_per_s = GetClockTicksPerSecond(); + if (clock_ticks_per_s <= 0) { + return false; + } + + time_val->tv_sec = ticks / clock_ticks_per_s; + time_val->tv_usec = (ticks % clock_ticks_per_s) * + (static_cast(1E6) / clock_ticks_per_s); + return true; +} + +} // namespace crashpad diff --git a/util/linux/proc_stat_reader.h b/util/linux/proc_stat_reader.h new file mode 100644 index 00000000..4cad97c5 --- /dev/null +++ b/util/linux/proc_stat_reader.h @@ -0,0 +1,80 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_UTIL_LINUX_PROC_STAT_READER_H_ +#define CRASHPAD_UTIL_LINUX_PROC_STAT_READER_H_ + +#include +#include +#include + +#include + +#include "base/macros.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +//! \brief Reads the /proc/[pid]/stat file for a thread. +class ProcStatReader { + public: + ProcStatReader(); + ~ProcStatReader(); + + //! \brief Initializes the reader. + //! + //! This method must be successfully called before calling any other. + //! + //! \param[in] tid The thread ID to read the stat file for. + bool Initialize(pid_t tid); + + //! \brief Determines the time the thread has spent executing in user mode. + //! + //! \param[out] user_time The time spent executing in user mode. + //! + //! \return `true` on success, with \a user_time set. Otherwise, `false` with + //! a message logged. + bool UserCPUTime(timeval* user_time) const; + + //! \brief Determines the time the thread has spent executing in system mode. + //! + //! \param[out] system_time The time spent executing in system mode. + //! + //! \return `true` on success, with \a system_time set. Otherwise, `false` + //! with a message logged. + bool SystemCPUTime(timeval* system_time) const; + + //! \brief Determines the target thread’s start time. + //! + //! \param[out] start_time The time that the thread started. + //! + //! \return `true` on success, with \a start_time set. Otherwise, `false` with + //! a message logged. + bool StartTime(timeval* start_time) const; + + private: + bool ReadFile(pid_t tid); + bool FindColumn(int index, const char** column) const; + bool ReadTimeAtIndex(int index, timeval* time_val) const; + + std::string contents_; + size_t third_column_position_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcStatReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_LINUX_PROC_STAT_READER_H_ diff --git a/util/linux/proc_stat_reader_test.cc b/util/linux/proc_stat_reader_test.cc new file mode 100644 index 00000000..8189c2ea --- /dev/null +++ b/util/linux/proc_stat_reader_test.cc @@ -0,0 +1,95 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util/linux/proc_stat_reader.h" + +#include +#include +#include + +#include "base/logging.h" +#include "gtest/gtest.h" +#include "util/thread/thread.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(ProcStatReader, Basic) { + ProcStatReader stat; + ASSERT_TRUE(stat.Initialize(getpid())); + + timeval start_time; + ASSERT_TRUE(stat.StartTime(&start_time)); + + time_t now; + time(&now); + EXPECT_LE(start_time.tv_sec, now); + + time_t elapsed_sec = now - start_time.tv_sec; + + timeval user_time; + ASSERT_TRUE(stat.UserCPUTime(&user_time)); + EXPECT_LE(user_time.tv_sec, elapsed_sec); + + timeval system_time; + ASSERT_TRUE(stat.SystemCPUTime(&system_time)); + EXPECT_LE(system_time.tv_sec, elapsed_sec); +} + +pid_t gettid() { + return syscall(SYS_gettid); +} + +void GetStartTime(timeval* start_time) { + ProcStatReader stat; + ASSERT_TRUE(stat.Initialize(gettid())); + ASSERT_TRUE(stat.StartTime(start_time)); +} + +class StatTimeThread : public Thread { + public: + StatTimeThread(timeval* start_time) : start_time_(start_time) {} + + private: + void ThreadMain() override { GetStartTime(start_time_); } + timeval* start_time_; +}; + +TEST(ProcStatReader, Threads) { + timeval main_time; + ASSERT_NO_FATAL_FAILURE(GetStartTime(&main_time)); + + timeval thread_time; + StatTimeThread thread(&thread_time); + thread.Start(); + ASSERT_NO_FATAL_FAILURE(thread.Join()); + + EXPECT_PRED4( + [](time_t main_sec, + suseconds_t main_usec, + time_t thread_sec, + suseconds_t thread_usec) { + return (thread_sec > main_sec) || + (thread_sec == main_sec && thread_usec > main_usec); + }, + main_time.tv_sec, + main_time.tv_usec, + thread_time.tv_sec, + thread_time.tv_usec); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/util/linux/process_memory_range.cc b/util/linux/process_memory_range.cc new file mode 100644 index 00000000..bdf9adca --- /dev/null +++ b/util/linux/process_memory_range.cc @@ -0,0 +1,93 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util/linux/process_memory_range.h" + +#include +#include + +#include "base/logging.h" + +namespace crashpad { + +ProcessMemoryRange::ProcessMemoryRange() + : memory_(nullptr), range_(), initialized_() {} + +ProcessMemoryRange::~ProcessMemoryRange() {} + +bool ProcessMemoryRange::Initialize(const ProcessMemory* memory, + bool is_64_bit, + LinuxVMAddress base, + LinuxVMSize size) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + memory_ = memory; + range_.SetRange(is_64_bit, base, size); + if (!range_.IsValid()) { + LOG(ERROR) << "invalid range"; + return false; + } + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessMemoryRange::Initialize(const ProcessMemory* memory, + bool is_64_bit) { + LinuxVMSize max = is_64_bit ? std::numeric_limits::max() + : std::numeric_limits::max(); + return Initialize(memory, is_64_bit, 0, max); +} + +bool ProcessMemoryRange::Initialize(const ProcessMemoryRange& other) { + return Initialize(other.memory_, + other.range_.Is64Bit(), + other.range_.Base(), + other.range_.Size()); +} + +bool ProcessMemoryRange::RestrictRange(LinuxVMAddress base, LinuxVMSize size) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + CheckedLinuxAddressRange new_range(range_.Is64Bit(), base, size); + if (!new_range.IsValid() || !range_.ContainsRange(new_range)) { + LOG(ERROR) << "invalid range"; + return false; + } + range_ = new_range; + return true; +} + +bool ProcessMemoryRange::Read(LinuxVMAddress address, + size_t size, + void* buffer) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + CheckedLinuxAddressRange read_range(range_.Is64Bit(), address, size); + if (!read_range.IsValid() || !range_.ContainsRange(read_range)) { + LOG(ERROR) << "read out of range"; + return false; + } + return memory_->Read(address, size, buffer); +} + +bool ProcessMemoryRange::ReadCStringSizeLimited(LinuxVMAddress address, + size_t size, + std::string* string) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!range_.ContainsValue(address)) { + LOG(ERROR) << "read out of range"; + return false; + } + size = std::min(static_cast(size), range_.End() - address); + return memory_->ReadCStringSizeLimited(address, size, string); +} + +} // namespace crashpad diff --git a/util/linux/process_memory_range.h b/util/linux/process_memory_range.h new file mode 100644 index 00000000..f569ee84 --- /dev/null +++ b/util/linux/process_memory_range.h @@ -0,0 +1,129 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_UTIL_LINUX_PROCESS_MEMORY_RANGE_H_ +#define CRASHPAD_UTIL_LINUX_PROCESS_MEMORY_RANGE_H_ + +#include + +#include + +#include "base/macros.h" +#include "util/linux/address_types.h" +#include "util/linux/checked_linux_address_range.h" +#include "util/linux/process_memory.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +//! \brief Provides range protected access to the memory of another process. +class ProcessMemoryRange { + public: + ProcessMemoryRange(); + ~ProcessMemoryRange(); + + //! \brief Initializes this object. + //! + //! One of the Initialize methods must be successfully called on this object + //! before calling any other. + //! + //! \param[in] memory The memory reader to delegate to. + //! \param[in] is_64_bit Whether the target process is 64-bit. + //! \param[in] base The base address of the initial range. + //! \param[in] size The size of the initial range. + //! \return `true` on success. `false` on failure with a message logged. + bool Initialize(const ProcessMemory* memory, + bool is_64_bit, + LinuxVMAddress base, + LinuxVMSize size); + + //! \brief Initializes this object with the maximum range for the address + //! space. + //! + //! One of the Initialize methods must be successfully called on this object + //! before calling any other. + //! + //! \param[in] memory The memory reader to delegate to. + //! \param[in] is_64_bit Whether the target process is 64-bit. + bool Initialize(const ProcessMemory* memory, bool is_64_bit); + + //! \brief Initializes this object from an existing memory range. + //! + //! One of the Initialize methods must be successfully called on this object + //! before calling any other. + //! + //! \param[in] other The memory range object to initialize from. + //! \return `true` on success. `false` on failure with a message logged. + bool Initialize(const ProcessMemoryRange& other); + + //! \brief Returns whether the range is part of a 64-bit address space. + bool Is64Bit() const { return range_.Is64Bit(); } + + //! \brief Returns the base address of the range. + LinuxVMAddress Base() const { return range_.Base(); } + + //! \brief Returns the size of the range. + LinuxVMSize Size() const { return range_.Size(); } + + //! \brief Shrinks the range to the new base and size. + //! + //! The new range must be contained within the existing range for this object. + //! + //! \param[in] base The new base of the range. + //! \param[in] size The new size of the range. + //! \return `true` on success. `false` on failure with a message logged. + bool RestrictRange(LinuxVMAddress base, LinuxVMSize size); + + //! \brief Copies memory from the target process into a caller-provided buffer + //! in the current process. + //! + //! \param[in] address The address, in the target process' address space, of + //! the memory region to copy. + //! \param[in] size The size, in bytes, of the memory region to copy. + //! \a buffer must be at least this size. + //! \param[out] buffer The buffer into which the contents of the other + //! process' memory will be copied. + //! + //! \return `true` on success, with \a buffer filled appropriately. `false` on + //! failure, with a message logged. + bool Read(LinuxVMAddress address, size_t size, void* buffer) const; + + //! \brief Reads a `NUL`-terminated C string from the target process into a + //! string in the current process. + //! + //! \param[in] address The address, in the target process’s address space, of + //! the string to copy. + //! \param[in] size The maximum number of bytes to read. The string is + //! required to be `NUL`-terminated within this many bytes. + //! \param[out] string The string read from the other process. + //! + //! \return `true` on success, with \a string set appropriately. `false` on + //! failure, with a message logged. Failures can occur, for example, when + //! a `NUL` terminator is not found within \a size bytes, or when + //! encountering unmapped or unreadable pages. + bool ReadCStringSizeLimited(LinuxVMAddress address, + size_t size, + std::string* string) const; + + private: + const ProcessMemory* memory_; // weak + CheckedLinuxAddressRange range_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcessMemoryRange); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_LINUX_PROCESS_MEMORY_RANGE_H_ diff --git a/util/linux/process_memory_range_test.cc b/util/linux/process_memory_range_test.cc new file mode 100644 index 00000000..bd23ae55 --- /dev/null +++ b/util/linux/process_memory_range_test.cc @@ -0,0 +1,91 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util/linux/process_memory_range.h" + +#include + +#include + +#include "base/logging.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "util/misc/from_pointer_cast.h" + +namespace crashpad { +namespace test { +namespace { + +struct TestObject { + char string1[16]; + char string2[16]; +} kTestObject = {"string1", "string2"}; + +TEST(ProcessMemoryRange, Basic) { + pid_t pid = getpid(); +#if defined(ARCH_CPU_64_BITS) + constexpr bool is_64_bit = true; +#else + constexpr bool is_64_bit = false; +#endif // ARCH_CPU_64_BITS + + ProcessMemory memory; + ASSERT_TRUE(memory.Initialize(pid)); + + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); + EXPECT_EQ(range.Is64Bit(), is_64_bit); + + // Both strings are accessible within the object's range. + auto object_addr = FromPointerCast(&kTestObject); + EXPECT_TRUE(range.RestrictRange(object_addr, sizeof(kTestObject))); + + TestObject object; + ASSERT_TRUE(range.Read(object_addr, sizeof(object), &object)); + EXPECT_EQ(memcmp(&object, &kTestObject, sizeof(object)), 0); + + std::string string; + auto string1_addr = FromPointerCast(kTestObject.string1); + auto string2_addr = FromPointerCast(kTestObject.string2); + ASSERT_TRUE(range.ReadCStringSizeLimited( + string1_addr, arraysize(kTestObject.string1), &string)); + EXPECT_STREQ(string.c_str(), kTestObject.string1); + + ASSERT_TRUE(range.ReadCStringSizeLimited( + string2_addr, arraysize(kTestObject.string2), &string)); + EXPECT_STREQ(string.c_str(), kTestObject.string2); + + // Limit the range to remove access to string2. + ProcessMemoryRange range2; + ASSERT_TRUE(range2.Initialize(range)); + ASSERT_TRUE( + range2.RestrictRange(string1_addr, arraysize(kTestObject.string1))); + EXPECT_TRUE(range2.ReadCStringSizeLimited( + string1_addr, arraysize(kTestObject.string1), &string)); + EXPECT_FALSE(range2.ReadCStringSizeLimited( + string2_addr, arraysize(kTestObject.string2), &string)); + EXPECT_FALSE(range2.Read(object_addr, sizeof(object), &object)); + + // String reads fail if the NUL terminator is outside the range. + ASSERT_TRUE(range2.RestrictRange(string1_addr, strlen(kTestObject.string1))); + EXPECT_FALSE(range2.ReadCStringSizeLimited( + string1_addr, arraysize(kTestObject.string1), &string)); + + // New range outside the old range. + EXPECT_FALSE(range2.RestrictRange(string1_addr - 1, 1)); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/util/linux/process_memory_test.cc b/util/linux/process_memory_test.cc index 21377553..4ad2e756 100644 --- a/util/linux/process_memory_test.cc +++ b/util/linux/process_memory_test.cc @@ -135,8 +135,8 @@ bool ReadCStringSizeLimited(const ProcessMemory& memory, FromPointerCast(pointer), size, result); } -const char kConstCharEmpty[] = ""; -const char kConstCharShort[] = "A short const char[]"; +constexpr char kConstCharEmpty[] = ""; +constexpr char kConstCharShort[] = "A short const char[]"; class ReadCStringTest : public TargetProcessTest { public: diff --git a/util/linux/thread_info.cc b/util/linux/thread_info.cc index 300ae7d3..f63106a6 100644 --- a/util/linux/thread_info.cc +++ b/util/linux/thread_info.cc @@ -283,17 +283,16 @@ bool ThreadInfo::GetThreadArea(LinuxVMAddress* address) { return true; } + size_t index = (context_.t32.xgs & 0xffff) >> 3; user_desc desc; - iovec iov; - iov.iov_base = &desc; - iov.iov_len = sizeof(desc); - *address = 0; - if (ptrace( - PTRACE_GETREGSET, tid_, reinterpret_cast(NT_386_TLS), &iov) != - 0) { + if (ptrace(PTRACE_GET_THREAD_AREA, + tid_, + reinterpret_cast(index), + &desc) != 0) { PLOG(ERROR) << "ptrace"; return false; } + *address = desc.base_addr; return true; diff --git a/util/linux/thread_info.h b/util/linux/thread_info.h index e9eeff31..f8b97514 100644 --- a/util/linux/thread_info.h +++ b/util/linux/thread_info.h @@ -40,7 +40,7 @@ union ThreadContext { //! \brief The general purpose registers used by the 32-bit variant of the //! architecture. - struct t32 { + struct t32_t { #if defined(ARCH_CPU_X86_FAMILY) // Reflects user_regs_struct in sys/user.h. uint32_t ebx; @@ -77,7 +77,7 @@ union ThreadContext { //! \brief The general purpose registers used by the 64-bit variant of the //! architecture. - struct t64 { + struct t64_t { #if defined(ARCH_CPU_X86_FAMILY) // Reflects user_regs_struct in sys/user.h. uint64_t r15; @@ -127,9 +127,9 @@ union ThreadContext { #endif // ARCH_CPU_X86_FAMILY || ARCH_CPU_ARM64 #if defined(ARCH_CPU_32_BITS) - static_assert(sizeof(t32) == sizeof(NativeThreadContext), "Size mismatch"); + static_assert(sizeof(t32_t) == sizeof(NativeThreadContext), "Size mismatch"); #else // ARCH_CPU_64_BITS - static_assert(sizeof(t64) == sizeof(NativeThreadContext), "Size mismatch"); + static_assert(sizeof(t64_t) == sizeof(NativeThreadContext), "Size mismatch"); #endif // ARCH_CPU_32_BITS }; static_assert(std::is_standard_layout::value, @@ -142,7 +142,7 @@ union FloatContext { //! \brief The floating point registers used by the 32-bit variant of the //! architecture. - struct f32 { + struct f32_t { #if defined(ARCH_CPU_X86_FAMILY) // Reflects user_fpxregs_struct in sys/user.h struct fxsave { @@ -193,7 +193,7 @@ union FloatContext { //! \brief The floating point registers used by the 64-bit variant of the //! architecture. - struct f64 { + struct f64_t { #if defined(ARCH_CPU_X86_FAMILY) // Refelects user_fpregs_struct in sys/user.h struct fxsave { @@ -220,18 +220,22 @@ union FloatContext { } f64; #if defined(ARCH_CPU_X86) -#if defined(OS_ANDROID) && __ANDROID_API__ <= 19 +// __ANDROID_API_N__ is a proxy for determining whether unified headers are in +// use. It’s only defined by unified headers. Unified headers call this +// structure user_fpxregs_struct regardless of API level. +#if defined(OS_ANDROID) && __ANDROID_API__ <= 19 && !defined(__ANDROID_API_N__) using NativeFpxregs = user_fxsr_struct; #else using NativeFpxregs = user_fpxregs_struct; #endif // OS_ANDROID - static_assert(sizeof(f32::fxsave) == sizeof(NativeFpxregs), "Size mismatch"); + static_assert(sizeof(f32_t::fxsave) == sizeof(NativeFpxregs), + "Size mismatch"); #elif defined(ARCH_CPU_X86_64) - static_assert(sizeof(f64::fxsave) == sizeof(user_fpregs_struct), + static_assert(sizeof(f64_t::fxsave) == sizeof(user_fpregs_struct), "Size mismatch"); #elif defined(ARCH_CPU_ARMEL) - static_assert(sizeof(f32::fpregs) == sizeof(user_fpregs), "Size mismatch"); - static_assert(sizeof(f32::vfp) == sizeof(user_vfp), "Size mismatch"); + static_assert(sizeof(f32_t::fpregs) == sizeof(user_fpregs), "Size mismatch"); + static_assert(sizeof(f32_t::vfp) == sizeof(user_vfp), "Size mismatch"); #elif defined(ARCH_CPU_ARM64) static_assert(sizeof(f64) == sizeof(user_fpsimd_struct), "Size mismatch"); #else diff --git a/util/linux/thread_info_test.cc b/util/linux/thread_info_test.cc index fe05b4a8..a4bf6baf 100644 --- a/util/linux/thread_info_test.cc +++ b/util/linux/thread_info_test.cc @@ -39,9 +39,9 @@ class SameBitnessTest : public Multiprocess { ASSERT_TRUE(thread_info.Initialize(ChildPID())); #if defined(ARCH_CPU_64_BITS) - const bool am_64_bit = true; + constexpr bool am_64_bit = true; #else - const bool am_64_bit = false; + constexpr bool am_64_bit = false; #endif // ARCH_CPU_64_BITS EXPECT_EQ(thread_info.Is64Bit(), am_64_bit); diff --git a/util/mac/checked_mach_address_range_test.cc b/util/mac/checked_mach_address_range_test.cc index 860bdff8..089f6815 100644 --- a/util/mac/checked_mach_address_range_test.cc +++ b/util/mac/checked_mach_address_range_test.cc @@ -43,7 +43,7 @@ bool ExpectationForValidity64(Validity validity) { } TEST(CheckedMachAddressRange, IsValid) { - const struct TestData { + static constexpr struct { mach_vm_address_t base; mach_vm_size_t size; Validity validity; @@ -117,7 +117,7 @@ TEST(CheckedMachAddressRange, IsValid) { }; for (size_t index = 0; index < arraysize(kTestData); ++index) { - const TestData& testcase = kTestData[index]; + const auto& testcase = kTestData[index]; SCOPED_TRACE(base::StringPrintf("index %zu, base 0x%llx, size 0x%llx", index, testcase.base, @@ -132,7 +132,7 @@ TEST(CheckedMachAddressRange, IsValid) { } TEST(CheckedMachAddressRange, ContainsValue) { - const struct TestData { + static constexpr struct { mach_vm_address_t value; bool expectation; } kTestData[] = { @@ -167,7 +167,7 @@ TEST(CheckedMachAddressRange, ContainsValue) { ASSERT_TRUE(parent_range_32.IsValid()); for (size_t index = 0; index < arraysize(kTestData); ++index) { - const TestData& testcase = kTestData[index]; + const auto& testcase = kTestData[index]; SCOPED_TRACE( base::StringPrintf("index %zu, value 0x%llx", index, testcase.value)); @@ -185,7 +185,7 @@ TEST(CheckedMachAddressRange, ContainsValue) { } TEST(CheckedMachAddressRange, ContainsRange) { - const struct TestData { + static constexpr struct { mach_vm_address_t base; mach_vm_size_t size; bool expectation; @@ -224,7 +224,7 @@ TEST(CheckedMachAddressRange, ContainsRange) { ASSERT_TRUE(parent_range_32.IsValid()); for (size_t index = 0; index < arraysize(kTestData); ++index) { - const TestData& testcase = kTestData[index]; + const auto& testcase = kTestData[index]; SCOPED_TRACE(base::StringPrintf("index %zu, base 0x%llx, size 0x%llx", index, testcase.base, diff --git a/util/mac/launchd_test.mm b/util/mac/launchd_test.mm index 8d9db762..12ed8320 100644 --- a/util/mac/launchd_test.mm +++ b/util/mac/launchd_test.mm @@ -153,7 +153,7 @@ TEST(Launchd, CFPropertyToLaunchData_Data) { @autoreleasepool { base::mac::ScopedLaunchData launch_data; - const uint8_t data_c[] = { + static constexpr uint8_t data_c[] = { 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 9, 8, 7, 6, 5, 4, 3, 2}; NSData* data_ns = [NSData dataWithBytes:data_c length:sizeof(data_c)]; launch_data.reset(CFPropertyToLaunchData(data_ns)); diff --git a/util/mac/service_management_test.mm b/util/mac/service_management_test.mm index 769c32f1..55019df0 100644 --- a/util/mac/service_management_test.mm +++ b/util/mac/service_management_test.mm @@ -114,7 +114,8 @@ TEST(ServiceManagement, SubmitRemoveJob) { base::StringPrintf("sleep 10; echo %s", cookie.c_str()); NSString* shell_script_ns = base::SysUTF8ToNSString(shell_script); - const char kJobLabel[] = "org.chromium.crashpad.test.service_management"; + static constexpr char kJobLabel[] = + "org.chromium.crashpad.test.service_management"; NSDictionary* job_dictionary_ns = @{ @LAUNCH_JOBKEY_LABEL : @"org.chromium.crashpad.test.service_management", @LAUNCH_JOBKEY_RUNATLOAD : @YES, diff --git a/util/mac/xattr_test.cc b/util/mac/xattr_test.cc index 477bb138..56318d13 100644 --- a/util/mac/xattr_test.cc +++ b/util/mac/xattr_test.cc @@ -55,7 +55,7 @@ class Xattr : public testing::Test { base::FilePath path_; }; -const char kKey[] = "com.google.crashpad.test"; +constexpr char kKey[] = "org.chromium.crashpad.test"; TEST_F(Xattr, ReadNonExistentXattr) { std::string value; diff --git a/util/mach/child_port_handshake.cc b/util/mach/child_port_handshake.cc index e3624d70..750d3cbe 100644 --- a/util/mach/child_port_handshake.cc +++ b/util/mach/child_port_handshake.cc @@ -175,7 +175,7 @@ mach_port_t ChildPortHandshakeServer::RunServer( // be delivered out of order and the check-in message will still be // processed. struct kevent event; - const timespec nonblocking_timeout = {}; + constexpr timespec nonblocking_timeout = {}; const timespec* timeout = blocking ? nullptr : &nonblocking_timeout; rv = HANDLE_EINTR(kevent(kq.get(), nullptr, 0, &event, 1, timeout)); PCHECK(rv != -1) << "kevent"; @@ -341,7 +341,7 @@ ChildPortHandshake::ChildPortHandshake() // SIGPIPE is undesirable when writing to this pipe. Allow broken-pipe writes // to fail with EPIPE instead. - const int value = 1; + constexpr int value = 1; PCHECK(setsockopt(server_write_fd_.get(), SOL_SOCKET, SO_NOSIGPIPE, diff --git a/util/mach/child_port_server.cc b/util/mach/child_port_server.cc index 4fc723ac..9a34d431 100644 --- a/util/mach/child_port_server.cc +++ b/util/mach/child_port_server.cc @@ -106,7 +106,8 @@ bool ChildPortServer::MachMessageServerFunction( } std::set ChildPortServer::MachMessageServerRequestIDs() { - const mach_msg_id_t request_ids[] = {kMachMessageIDChildPortCheckIn}; + static constexpr mach_msg_id_t request_ids[] = + {kMachMessageIDChildPortCheckIn}; return std::set(&request_ids[0], &request_ids[arraysize(request_ids)]); } diff --git a/util/mach/child_port_server_test.cc b/util/mach/child_port_server_test.cc index d579ce91..984d64ac 100644 --- a/util/mach/child_port_server_test.cc +++ b/util/mach/child_port_server_test.cc @@ -32,12 +32,12 @@ using testing::Return; // Fake Mach ports. These aren’t used as ports in these tests, they’re just used // as cookies to make sure that the correct values get passed to the correct // places. -const mach_port_t kServerLocalPort = 0x05050505; -const mach_port_t kCheckInPort = 0x06060606; +constexpr mach_port_t kServerLocalPort = 0x05050505; +constexpr mach_port_t kCheckInPort = 0x06060606; // Other fake values. -const mach_msg_type_name_t kCheckInPortRightType = MACH_MSG_TYPE_PORT_SEND; -const child_port_token_t kCheckInToken = 0xfedcba9876543210; +constexpr mach_msg_type_name_t kCheckInPortRightType = MACH_MSG_TYPE_PORT_SEND; +constexpr child_port_token_t kCheckInToken = 0xfedcba9876543210; // The definition of the request structure from child_port.h isn’t available // here. It needs custom initialization code, so duplicate the expected diff --git a/util/mach/composite_mach_message_server_test.cc b/util/mach/composite_mach_message_server_test.cc index 3a37e49b..74e1707a 100644 --- a/util/mach/composite_mach_message_server_test.cc +++ b/util/mach/composite_mach_message_server_test.cc @@ -135,10 +135,10 @@ TEST(CompositeMachMessageServer, HandlerDoesNotHandle) { } TEST(CompositeMachMessageServer, OneHandler) { - const mach_msg_id_t kRequestID = 100; - const mach_msg_size_t kRequestSize = 256; - const mach_msg_size_t kReplySize = 128; - const kern_return_t kReturnCode = KERN_SUCCESS; + constexpr mach_msg_id_t kRequestID = 100; + constexpr mach_msg_size_t kRequestSize = 256; + constexpr mach_msg_size_t kReplySize = 128; + constexpr kern_return_t kReturnCode = KERN_SUCCESS; TestMachMessageHandler handler; handler.AddRequestID(kRequestID); @@ -182,16 +182,16 @@ TEST(CompositeMachMessageServer, OneHandler) { } TEST(CompositeMachMessageServer, ThreeHandlers) { - const mach_msg_id_t kRequestIDs0[] = {5}; - const kern_return_t kReturnCode0 = KERN_SUCCESS; + static constexpr mach_msg_id_t kRequestIDs0[] = {5}; + constexpr kern_return_t kReturnCode0 = KERN_SUCCESS; - const mach_msg_id_t kRequestIDs1[] = {4, 7}; - const kern_return_t kReturnCode1 = KERN_PROTECTION_FAILURE; + static constexpr mach_msg_id_t kRequestIDs1[] = {4, 7}; + constexpr kern_return_t kReturnCode1 = KERN_PROTECTION_FAILURE; - const mach_msg_id_t kRequestIDs2[] = {10, 0, 20}; - const mach_msg_size_t kRequestSize2 = 6144; - const mach_msg_size_t kReplySize2 = 16384; - const kern_return_t kReturnCode2 = KERN_NOT_RECEIVER; + static constexpr mach_msg_id_t kRequestIDs2[] = {10, 0, 20}; + constexpr mach_msg_size_t kRequestSize2 = 6144; + constexpr mach_msg_size_t kReplySize2 = 16384; + constexpr kern_return_t kReturnCode2 = KERN_NOT_RECEIVER; TestMachMessageHandler handlers[3]; std::set expect_request_ids; @@ -289,7 +289,7 @@ TEST(CompositeMachMessageServer, ThreeHandlers) { // CompositeMachMessageServer can’t deal with two handlers that want to handle // the same request ID. TEST(CompositeMachMessageServerDeathTest, DuplicateRequestID) { - const mach_msg_id_t kRequestID = 400; + constexpr mach_msg_id_t kRequestID = 400; TestMachMessageHandler handlers[2]; handlers[0].AddRequestID(kRequestID); diff --git a/util/mach/exc_client_variants_test.cc b/util/mach/exc_client_variants_test.cc index 3bf77bb5..b7288563 100644 --- a/util/mach/exc_client_variants_test.cc +++ b/util/mach/exc_client_variants_test.cc @@ -264,7 +264,7 @@ mach_exception_subcode_t TestExcClientVariants::exception_subcode_ = 0xffffffff00000000; TEST(ExcClientVariants, UniversalExceptionRaise) { - const exception_behavior_t kBehaviors[] = { + static constexpr exception_behavior_t kBehaviors[] = { EXCEPTION_DEFAULT, EXCEPTION_STATE, EXCEPTION_STATE_IDENTITY, diff --git a/util/mach/exc_server_variants.cc b/util/mach/exc_server_variants.cc index 209c45ef..ff918065 100644 --- a/util/mach/exc_server_variants.cc +++ b/util/mach/exc_server_variants.cc @@ -328,7 +328,7 @@ class ExcServer : public MachMessageServer::Interface { bool* destroy_complex_request) override; std::set MachMessageServerRequestIDs() override { - const mach_msg_id_t request_ids[] = { + constexpr mach_msg_id_t request_ids[] = { Traits::kMachMessageIDExceptionRaise, Traits::kMachMessageIDExceptionRaiseState, Traits::kMachMessageIDExceptionRaiseStateIdentity, diff --git a/util/mach/exc_server_variants_test.cc b/util/mach/exc_server_variants_test.cc index 482edac3..358cd1ec 100644 --- a/util/mach/exc_server_variants_test.cc +++ b/util/mach/exc_server_variants_test.cc @@ -43,28 +43,28 @@ using testing::Return; // Fake Mach ports. These aren’t used as ports in these tests, they’re just used // as cookies to make sure that the correct values get passed to the correct // places. -const mach_port_t kClientRemotePort = 0x01010101; -const mach_port_t kServerLocalPort = 0x02020202; -const thread_t kExceptionThreadPort = 0x03030303; -const task_t kExceptionTaskPort = 0x04040404; +constexpr mach_port_t kClientRemotePort = 0x01010101; +constexpr mach_port_t kServerLocalPort = 0x02020202; +constexpr thread_t kExceptionThreadPort = 0x03030303; +constexpr task_t kExceptionTaskPort = 0x04040404; // Other fake exception values. -const exception_type_t kExceptionType = EXC_BAD_ACCESS; +constexpr exception_type_t kExceptionType = EXC_BAD_ACCESS; // Test using an exception code with the high bit set to ensure that it gets // promoted to the wider mach_exception_data_type_t type as a signed quantity. -const exception_data_type_t kTestExceptonCodes[] = { +constexpr exception_data_type_t kTestExceptonCodes[] = { KERN_PROTECTION_FAILURE, implicit_cast(0xfedcba98), }; -const mach_exception_data_type_t kTestMachExceptionCodes[] = { +constexpr mach_exception_data_type_t kTestMachExceptionCodes[] = { KERN_PROTECTION_FAILURE, implicit_cast(0xfedcba9876543210), }; -const thread_state_flavor_t kThreadStateFlavor = MACHINE_THREAD_STATE; -const mach_msg_type_number_t kThreadStateFlavorCount = +constexpr thread_state_flavor_t kThreadStateFlavor = MACHINE_THREAD_STATE; +constexpr mach_msg_type_number_t kThreadStateFlavorCount = MACHINE_THREAD_STATE_COUNT; void InitializeMachMsgPortDescriptor(mach_msg_port_descriptor_t* descriptor, @@ -601,7 +601,7 @@ TEST(ExcServerVariants, MockExceptionRaise) { EXPECT_LE(sizeof(reply), universal_mach_exc_server.MachMessageServerReplySize()); - const exception_behavior_t kExceptionBehavior = EXCEPTION_DEFAULT; + constexpr exception_behavior_t kExceptionBehavior = EXCEPTION_DEFAULT; EXPECT_CALL(server, MockCatchMachException(kExceptionBehavior, @@ -646,7 +646,7 @@ TEST(ExcServerVariants, MockExceptionRaiseState) { EXPECT_LE(sizeof(reply), universal_mach_exc_server.MachMessageServerReplySize()); - const exception_behavior_t kExceptionBehavior = EXCEPTION_STATE; + constexpr exception_behavior_t kExceptionBehavior = EXCEPTION_STATE; EXPECT_CALL( server, @@ -695,7 +695,7 @@ TEST(ExcServerVariants, MockExceptionRaiseStateIdentity) { EXPECT_LE(sizeof(reply), universal_mach_exc_server.MachMessageServerReplySize()); - const exception_behavior_t kExceptionBehavior = EXCEPTION_STATE_IDENTITY; + constexpr exception_behavior_t kExceptionBehavior = EXCEPTION_STATE_IDENTITY; EXPECT_CALL( server, @@ -741,7 +741,7 @@ TEST(ExcServerVariants, MockMachExceptionRaise) { EXPECT_LE(sizeof(reply), universal_mach_exc_server.MachMessageServerReplySize()); - const exception_behavior_t kExceptionBehavior = + constexpr exception_behavior_t kExceptionBehavior = EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES; EXPECT_CALL( @@ -788,7 +788,7 @@ TEST(ExcServerVariants, MockMachExceptionRaiseState) { EXPECT_LE(sizeof(reply), universal_mach_exc_server.MachMessageServerReplySize()); - const exception_behavior_t kExceptionBehavior = + constexpr exception_behavior_t kExceptionBehavior = EXCEPTION_STATE | MACH_EXCEPTION_CODES; EXPECT_CALL( @@ -838,7 +838,7 @@ TEST(ExcServerVariants, MockMachExceptionRaiseStateIdentity) { EXPECT_LE(sizeof(reply), universal_mach_exc_server.MachMessageServerReplySize()); - const exception_behavior_t kExceptionBehavior = + constexpr exception_behavior_t kExceptionBehavior = EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES; EXPECT_CALL( @@ -877,7 +877,7 @@ TEST(ExcServerVariants, MockUnknownID) { // UniversalMachExcServer should not dispatch the message to // MachMessageServerFunction, but should generate a MIG_BAD_ID error reply. - const mach_msg_id_t unknown_ids[] = { + static constexpr mach_msg_id_t unknown_ids[] = { // Reasonable things to check. -101, -100, @@ -1130,11 +1130,10 @@ TEST(ExcServerVariants, ThreadStates) { // So far, all of the tests worked with MACHINE_THREAD_STATE. Now try all of // the other thread state flavors that are expected to work. - struct TestData { + static constexpr struct { thread_state_flavor_t flavor; mach_msg_type_number_t count; - }; - const TestData test_data[] = { + } test_data[] = { #if defined(ARCH_CPU_X86_FAMILY) // For the x86 family, exception handlers can only properly receive the // thread, float, and exception state flavors. There’s a bug in the kernel @@ -1179,7 +1178,7 @@ TEST(ExcServerVariants, ThreadStates) { }; for (size_t index = 0; index < arraysize(test_data); ++index) { - const TestData& test = test_data[index]; + const auto& test = test_data[index]; SCOPED_TRACE( base::StringPrintf("index %zu, flavor %d", index, test.flavor)); @@ -1195,13 +1194,12 @@ TEST(ExcServerVariants, ExcServerSuccessfulReturnValue) { const kern_return_t prefer_not_set_thread_state = MacOSXMinorVersion() < 11 ? MACH_RCV_PORT_DIED : KERN_SUCCESS; - struct TestData { + const struct { exception_type_t exception; exception_behavior_t behavior; bool set_thread_state; kern_return_t kr; - }; - const TestData kTestData[] = { + } kTestData[] = { {EXC_CRASH, EXCEPTION_DEFAULT, false, KERN_SUCCESS}, {EXC_CRASH, EXCEPTION_STATE, false, prefer_not_set_thread_state}, {EXC_CRASH, EXCEPTION_STATE_IDENTITY, false, prefer_not_set_thread_state}, @@ -1253,7 +1251,7 @@ TEST(ExcServerVariants, ExcServerSuccessfulReturnValue) { }; for (size_t index = 0; index < arraysize(kTestData); ++index) { - const TestData& test_data = kTestData[index]; + const auto& test_data = kTestData[index]; SCOPED_TRACE( base::StringPrintf("index %zu, behavior %d, set_thread_state %s", index, @@ -1268,10 +1266,10 @@ TEST(ExcServerVariants, ExcServerSuccessfulReturnValue) { } TEST(ExcServerVariants, ExcServerCopyState) { - const natural_t old_state[] = {1, 2, 3, 4, 5}; + static constexpr natural_t old_state[] = {1, 2, 3, 4, 5}; natural_t new_state[10] = {}; - const mach_msg_type_number_t old_state_count = arraysize(old_state); + constexpr mach_msg_type_number_t old_state_count = arraysize(old_state); mach_msg_type_number_t new_state_count = arraysize(new_state); // EXCEPTION_DEFAULT (with or without MACH_EXCEPTION_CODES) is not diff --git a/util/mach/exception_behaviors_test.cc b/util/mach/exception_behaviors_test.cc index 3ccb2027..d45110d9 100644 --- a/util/mach/exception_behaviors_test.cc +++ b/util/mach/exception_behaviors_test.cc @@ -26,14 +26,13 @@ namespace test { namespace { TEST(ExceptionBehaviors, ExceptionBehaviors) { - struct TestData { + static constexpr struct { exception_behavior_t behavior; bool state; bool identity; bool mach_exception_codes; exception_behavior_t basic_behavior; - }; - const TestData kTestData[] = { + } kTestData[] = { {EXCEPTION_DEFAULT, false, true, false, EXCEPTION_DEFAULT}, {EXCEPTION_STATE, true, false, false, EXCEPTION_STATE}, {EXCEPTION_STATE_IDENTITY, true, true, false, EXCEPTION_STATE_IDENTITY}, @@ -55,7 +54,7 @@ TEST(ExceptionBehaviors, ExceptionBehaviors) { }; for (size_t index = 0; index < arraysize(kTestData); ++index) { - const TestData& test_data = kTestData[index]; + const auto& test_data = kTestData[index]; SCOPED_TRACE(base::StringPrintf( "index %zu, behavior %d", index, test_data.behavior)); diff --git a/util/mach/exception_ports.cc b/util/mach/exception_ports.cc index ef535b4c..3e4e093b 100644 --- a/util/mach/exception_ports.cc +++ b/util/mach/exception_ports.cc @@ -99,7 +99,7 @@ bool ExceptionPorts::GetExceptionPorts(exception_mask_t mask, // later operating system versions have defined more exception types. The // generated task_get_exception_ports() in taskUser.c expects there to be room // for 32. - const int kMaxPorts = 32; + constexpr int kMaxPorts = 32; // task_get_exception_ports() doesn’t actually use the initial value of // handler_count, but 10.9.4 diff --git a/util/mach/exception_ports_test.cc b/util/mach/exception_ports_test.cc index 0bdd92ec..224217bf 100644 --- a/util/mach/exception_ports_test.cc +++ b/util/mach/exception_ports_test.cc @@ -58,7 +58,7 @@ namespace { void TestGetExceptionPorts(const ExceptionPorts& exception_ports, mach_port_t expect_port, exception_behavior_t expect_behavior) { - const exception_mask_t kExceptionMask = EXC_MASK_CRASH; + constexpr exception_mask_t kExceptionMask = EXC_MASK_CRASH; thread_state_flavor_t expect_flavor = (expect_behavior == EXCEPTION_DEFAULT) ? THREAD_STATE_NONE @@ -440,7 +440,7 @@ class TestExceptionPorts : public MachMultiprocess, if (who_crashes_ != kNobodyCrashes) { UniversalMachExcServer universal_mach_exc_server(this); - const mach_msg_timeout_t kTimeoutMs = 50; + constexpr mach_msg_timeout_t kTimeoutMs = 50; kern_return_t kr = MachMessageServer::Run(&universal_mach_exc_server, local_port, diff --git a/util/mach/exception_types_test.cc b/util/mach/exception_types_test.cc index ff09164e..c24428a2 100644 --- a/util/mach/exception_types_test.cc +++ b/util/mach/exception_types_test.cc @@ -31,13 +31,12 @@ namespace test { namespace { TEST(ExceptionTypes, ExcCrashRecoverOriginalException) { - struct TestData { + static constexpr struct { mach_exception_code_t code_0; exception_type_t exception; mach_exception_code_t original_code_0; int signal; - }; - const TestData kTestData[] = { + } kTestData[] = { {0xb100001, EXC_BAD_ACCESS, KERN_INVALID_ADDRESS, SIGSEGV}, {0xb100002, EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE, SIGSEGV}, {0xa100002, EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE, SIGBUS}, @@ -69,7 +68,7 @@ TEST(ExceptionTypes, ExcCrashRecoverOriginalException) { }; for (size_t index = 0; index < arraysize(kTestData); ++index) { - const TestData& test_data = kTestData[index]; + const auto& test_data = kTestData[index]; SCOPED_TRACE(base::StringPrintf( "index %zu, code_0 0x%llx", index, test_data.code_0)); @@ -86,7 +85,7 @@ TEST(ExceptionTypes, ExcCrashRecoverOriginalException) { // Now make sure that ExcCrashRecoverOriginalException() properly ignores // optional arguments. static_assert(arraysize(kTestData) >= 1, "must have something to test"); - const TestData& test_data = kTestData[0]; + const auto& test_data = kTestData[0]; EXPECT_EQ( ExcCrashRecoverOriginalException(test_data.code_0, nullptr, nullptr), test_data.exception); @@ -133,12 +132,11 @@ TEST(ExceptionTypes, ExcCrashCouldContainException) { (static_cast(flavor) & 0x7ull) << 58))) TEST(ExceptionTypes, ExceptionCodeForMetrics) { - struct TestData { + static constexpr struct { exception_type_t exception; mach_exception_code_t code_0; int32_t metrics_code; - }; - const TestData kTestData[] = { + } kTestData[] = { #define ENCODE_EXC(type, code_0) \ { (type), (code_0), ((type) << 16) | (code_0) } ENCODE_EXC(EXC_BAD_ACCESS, KERN_INVALID_ADDRESS), @@ -241,7 +239,7 @@ TEST(ExceptionTypes, ExceptionCodeForMetrics) { }; for (size_t index = 0; index < arraysize(kTestData); ++index) { - const TestData& test_data = kTestData[index]; + const auto& test_data = kTestData[index]; SCOPED_TRACE(base::StringPrintf("index %zu, exception 0x%x, code_0 0x%llx", index, test_data.exception, diff --git a/util/mach/mach_extensions.cc b/util/mach/mach_extensions.cc index eb4e330e..5450ab28 100644 --- a/util/mach/mach_extensions.cc +++ b/util/mach/mach_extensions.cc @@ -34,9 +34,9 @@ struct BootstrapCheckInTraits { mach_port_t* service_port) { return bootstrap_check_in(bootstrap_port, service_name, service_port); } - static const char kName[]; + static constexpr char kName[] = "bootstrap_check_in"; }; -const char BootstrapCheckInTraits::kName[] = "bootstrap_check_in"; +constexpr char BootstrapCheckInTraits::kName[]; struct BootstrapLookUpTraits { using Type = base::mac::ScopedMachSendRight; @@ -45,9 +45,9 @@ struct BootstrapLookUpTraits { mach_port_t* service_port) { return bootstrap_look_up(bootstrap_port, service_name, service_port); } - static const char kName[]; + static constexpr char kName[] = "bootstrap_look_up"; }; -const char BootstrapLookUpTraits::kName[] = "bootstrap_look_up"; +constexpr char BootstrapLookUpTraits::kName[]; template typename Traits::Type BootstrapCheckInOrLookUp( @@ -103,7 +103,7 @@ exception_mask_t ExcMaskAll() { // See 10.6.8 xnu-1504.15.3/osfmk/mach/exception_types.h. 10.7 uses the same // definition as 10.6. See 10.7.5 xnu-1699.32.7/osfmk/mach/exception_types.h - const exception_mask_t kExcMaskAll_10_6 = + constexpr exception_mask_t kExcMaskAll_10_6 = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | @@ -122,7 +122,7 @@ exception_mask_t ExcMaskAll() { // 10.8 added EXC_MASK_RESOURCE. See 10.8.5 // xnu-2050.48.11/osfmk/mach/exception_types.h. - const exception_mask_t kExcMaskAll_10_8 = + constexpr exception_mask_t kExcMaskAll_10_8 = kExcMaskAll_10_6 | EXC_MASK_RESOURCE; #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9 if (mac_os_x_minor_version < 9) { @@ -132,7 +132,8 @@ exception_mask_t ExcMaskAll() { // 10.9 added EXC_MASK_GUARD. See 10.9.4 // xnu-2422.110.17/osfmk/mach/exception_types.h. - const exception_mask_t kExcMaskAll_10_9 = kExcMaskAll_10_8 | EXC_MASK_GUARD; + constexpr exception_mask_t kExcMaskAll_10_9 = + kExcMaskAll_10_8 | EXC_MASK_GUARD; return kExcMaskAll_10_9; } diff --git a/util/mach/mach_extensions.h b/util/mach/mach_extensions.h index 5eeca9ae..25e401c2 100644 --- a/util/mach/mach_extensions.h +++ b/util/mach/mach_extensions.h @@ -30,7 +30,7 @@ namespace crashpad { //! are not performed, use kMachPortNull instead of an explicit `implicit_cast` //! of `MACH_PORT_NULL` to `mach_port_t`. This is useful for logging and testing //! assertions. -const mach_port_t kMachPortNull = MACH_PORT_NULL; +constexpr mach_port_t kMachPortNull = MACH_PORT_NULL; //! \brief `MACH_EXCEPTION_CODES` with the correct type for a Mach exception //! behavior, `exception_behavior_t`. @@ -38,7 +38,7 @@ const mach_port_t kMachPortNull = MACH_PORT_NULL; //! Signedness problems can occur when ORing `MACH_EXCEPTION_CODES` as a signed //! integer, because a signed integer overflow results. This constant can be //! used instead of `MACH_EXCEPTION_CODES` in such cases. -const exception_behavior_t kMachExceptionCodes = MACH_EXCEPTION_CODES; +constexpr exception_behavior_t kMachExceptionCodes = MACH_EXCEPTION_CODES; // Because exception_mask_t is an int and has one bit for each defined // exception_type_t, it’s reasonable to assume that there cannot be any @@ -49,7 +49,7 @@ const exception_behavior_t kMachExceptionCodes = MACH_EXCEPTION_CODES; // EXC_MASK_CRASH. //! \brief An exception type to use for simulated exceptions. -const exception_type_t kMachExceptionSimulated = 'CPsx'; +constexpr exception_type_t kMachExceptionSimulated = 'CPsx'; //! \brief A const version of `thread_state_t`. //! diff --git a/util/mach/mach_extensions_test.cc b/util/mach/mach_extensions_test.cc index 289a1999..1c5b368c 100644 --- a/util/mach/mach_extensions_test.cc +++ b/util/mach/mach_extensions_test.cc @@ -62,7 +62,7 @@ TEST(MachExtensions, NewMachPort_DeadName) { EXPECT_EQ(type, MACH_PORT_TYPE_DEAD_NAME); } -const exception_mask_t kExcMaskBasic = +constexpr exception_mask_t kExcMaskBasic = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | diff --git a/util/mach/mach_message.cc b/util/mach/mach_message.cc index 771f4603..5294c035 100644 --- a/util/mach/mach_message.cc +++ b/util/mach/mach_message.cc @@ -28,7 +28,7 @@ namespace crashpad { namespace { -const int kNanosecondsPerMillisecond = 1E6; +constexpr int kNanosecondsPerMillisecond = 1E6; // TimerRunning() determines whether |deadline| has passed. If |deadline| is // kMachMessageDeadlineWaitIndefinitely, |*timeout_options| is set to @@ -66,7 +66,8 @@ bool TimerRunning(uint64_t deadline, uint64_t remaining = deadline - now; // Round to the nearest millisecond, taking care not to overflow. - const int kHalfMillisecondInNanoseconds = kNanosecondsPerMillisecond / 2; + constexpr int kHalfMillisecondInNanoseconds = + kNanosecondsPerMillisecond / 2; if (remaining <= std::numeric_limits::max() - kHalfMillisecondInNanoseconds) { *remaining_ms = (remaining + kHalfMillisecondInNanoseconds) / diff --git a/util/mach/mach_message.h b/util/mach/mach_message.h index df4b6b4f..e0fc6f6f 100644 --- a/util/mach/mach_message.h +++ b/util/mach/mach_message.h @@ -26,7 +26,7 @@ namespace crashpad { //! //! This constant is provided because the macros normally used to request this //! behavior are cumbersome. -const mach_msg_option_t kMachMessageReceiveAuditTrailer = +constexpr mach_msg_option_t kMachMessageReceiveAuditTrailer = MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT); diff --git a/util/mach/mach_message_server_test.cc b/util/mach/mach_message_server_test.cc index e42ef6a5..16ba8f20 100644 --- a/util/mach/mach_message_server_test.cc +++ b/util/mach/mach_message_server_test.cc @@ -279,7 +279,7 @@ class TestMachMessageServer : public MachMessageServer::Interface, } std::set MachMessageServerRequestIDs() override { - const mach_msg_id_t request_ids[] = {kRequestMessageID}; + static constexpr mach_msg_id_t request_ids[] = {kRequestMessageID}; return std::set(&request_ids[0], &request_ids[arraysize(request_ids)]); } @@ -589,16 +589,16 @@ class TestMachMessageServer : public MachMessageServer::Interface, static uint32_t requests_; static uint32_t replies_; - static const mach_msg_id_t kRequestMessageID = 16237; - static const mach_msg_id_t kReplyMessageID = kRequestMessageID + 100; + static constexpr mach_msg_id_t kRequestMessageID = 16237; + static constexpr mach_msg_id_t kReplyMessageID = kRequestMessageID + 100; DISALLOW_COPY_AND_ASSIGN(TestMachMessageServer); }; uint32_t TestMachMessageServer::requests_; uint32_t TestMachMessageServer::replies_; -const mach_msg_id_t TestMachMessageServer::kRequestMessageID; -const mach_msg_id_t TestMachMessageServer::kReplyMessageID; +constexpr mach_msg_id_t TestMachMessageServer::kRequestMessageID; +constexpr mach_msg_id_t TestMachMessageServer::kReplyMessageID; TEST(MachMessageServer, Basic) { // The client sends one message to the server, which will wait indefinitely in @@ -688,7 +688,7 @@ TEST(MachMessageServer, PersistentNonblockingFourMessages) { // child is allowed to begin sending messages, so // child_wait_for_parent_pipe_early is used to make the child wait until the // parent is ready. - const size_t kTransactionCount = 4; + constexpr size_t kTransactionCount = 4; static_assert(kTransactionCount <= MACH_PORT_QLIMIT_DEFAULT, "must not exceed queue limit"); diff --git a/util/mach/notify_server.cc b/util/mach/notify_server.cc index 9ac377d1..2dbdf73e 100644 --- a/util/mach/notify_server.cc +++ b/util/mach/notify_server.cc @@ -259,7 +259,7 @@ bool NotifyServer::MachMessageServerFunction( } std::set NotifyServer::MachMessageServerRequestIDs() { - const mach_msg_id_t request_ids[] = { + static constexpr mach_msg_id_t request_ids[] = { MACH_NOTIFY_PORT_DELETED, MACH_NOTIFY_PORT_DESTROYED, MACH_NOTIFY_NO_SENDERS, diff --git a/util/mach/symbolic_constants_mach.cc b/util/mach/symbolic_constants_mach.cc index 26cc564f..b7296be2 100644 --- a/util/mach/symbolic_constants_mach.cc +++ b/util/mach/symbolic_constants_mach.cc @@ -26,7 +26,7 @@ namespace { -const char* kExceptionNames[] = { +constexpr const char* kExceptionNames[] = { nullptr, // sed -Ene 's/^#define[[:space:]]EXC_([[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/ "\1",/p' @@ -48,10 +48,10 @@ const char* kExceptionNames[] = { static_assert(arraysize(kExceptionNames) == EXC_TYPES_COUNT, "kExceptionNames length"); -const char kExcPrefix[] = "EXC_"; -const char kExcMaskPrefix[] = "EXC_MASK_"; +constexpr char kExcPrefix[] = "EXC_"; +constexpr char kExcMaskPrefix[] = "EXC_MASK_"; -const char* kBehaviorNames[] = { +constexpr const char* kBehaviorNames[] = { nullptr, // sed -Ene 's/^# define[[:space:]]EXCEPTION_([[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/ "\1",/p' @@ -61,11 +61,11 @@ const char* kBehaviorNames[] = { "STATE_IDENTITY", }; -const char kBehaviorPrefix[] = "EXCEPTION_"; -const char kMachExceptionCodesFull[] = "MACH_EXCEPTION_CODES"; -const char kMachExceptionCodesShort[] = "MACH"; +constexpr char kBehaviorPrefix[] = "EXCEPTION_"; +constexpr char kMachExceptionCodesFull[] = "MACH_EXCEPTION_CODES"; +constexpr char kMachExceptionCodesShort[] = "MACH"; -const char* kFlavorNames[] = { +constexpr const char* kFlavorNames[] = { "THREAD_STATE_FLAVOR_LIST", #if defined(__i386__) || defined(__x86_64__) @@ -128,7 +128,7 @@ const char* kFlavorNames[] = { // Certain generic flavors have high constants not contiguous with the flavors // above. List them separately alongside their constants. -const struct { +constexpr struct { thread_state_flavor_t flavor; const char* name; } kGenericFlavorNames[] = { @@ -139,7 +139,7 @@ const struct { // Returns the short name for a flavor name, given its full flavor name. std::string ThreadStateFlavorFullToShort(const base::StringPiece& flavor) { // For generic flavors like THREAD_STATE_NONE and THREAD_STATE_FLAVOR_LIST_*. - const char kThreadState[] = "THREAD_STATE_"; + static constexpr char kThreadState[] = "THREAD_STATE_"; size_t prefix_len = strlen(kThreadState); const char* flavor_data = flavor.data(); size_t flavor_len = flavor.size(); @@ -150,11 +150,11 @@ std::string ThreadStateFlavorFullToShort(const base::StringPiece& flavor) { // For architecture-specific flavors. #if defined(__i386__) || defined(__x86_64__) - const char kArchPrefix[] = "x86_"; + static constexpr char kArchPrefix[] = "x86_"; #elif defined(__ppc__) || defined(__ppc64__) - const char kArchPrefix[] = "PPC_"; + static constexpr char kArchPrefix[] = "PPC_"; #elif defined(__arm__) || defined(__arm64__) - const char kArchPrefix[] = "ARM_" + static constexpr char kArchPrefix[] = "ARM_" #endif prefix_len = strlen(kArchPrefix); if (flavor_len >= prefix_len && @@ -162,7 +162,7 @@ std::string ThreadStateFlavorFullToShort(const base::StringPiece& flavor) { // Shorten the suffix by removing _STATE. If the suffix contains a // significant designation like 32 or 64, keep it, so that a full name like // x86_THREAD_STATE64 becomes a short name like THREAD64. - const struct { + static constexpr struct { const char* orig; const char* repl; } kStateSuffixes[] = { @@ -343,7 +343,7 @@ bool StringToExceptionMask(const base::StringPiece& string, // EXC_MASK_ALL is a special case: it is not in kExceptionNames as it exists // only as a mask value. - const char kExcMaskAll[] = "ALL"; + static constexpr char kExcMaskAll[] = "ALL"; if ((can_match_full && short_string.compare(kExcMaskAll) == 0) || ((options & kAllowShortName) && string.compare(kExcMaskAll) == 0)) { *exception_mask = ExcMaskAll(); diff --git a/util/mach/symbolic_constants_mach_test.cc b/util/mach/symbolic_constants_mach_test.cc index f3086d25..32711412 100644 --- a/util/mach/symbolic_constants_mach_test.cc +++ b/util/mach/symbolic_constants_mach_test.cc @@ -32,7 +32,7 @@ namespace test { namespace { // Options to use for normal tests, those that don’t require kAllowOr. -const StringToSymbolicConstantOptions kNormalOptions[] = { +constexpr StringToSymbolicConstantOptions kNormalOptions[] = { 0, kAllowFullName, kAllowShortName, @@ -118,7 +118,7 @@ void TestStringToSomething(const base::StringPiece& string, } } -const struct { +constexpr struct { exception_type_t exception; const char* full_name; const char* short_name; @@ -147,9 +147,9 @@ struct ConvertExceptionTraits { ValueType* value) { return StringToException(string, options, value); } - static const char kValueName[]; + static constexpr char kValueName[] = "exception"; }; -const char ConvertExceptionTraits::kValueName[] = "exception"; +constexpr char ConvertExceptionTraits::kValueName[]; void TestExceptionToString(exception_type_t value, const char* expect_full, @@ -217,7 +217,7 @@ TEST(SymbolicConstantsMach, StringToException) { } } - const char* const kNegativeTestData[] = { + static constexpr const char* kNegativeTestData[] = { "EXC_CRASH ", " EXC_BAD_INSTRUCTION", "CRASH ", @@ -235,7 +235,7 @@ TEST(SymbolicConstantsMach, StringToException) { TestStringToException(kNegativeTestData[index], options, false, 0); } - const struct { + static constexpr struct { const char* string; size_t length; } kNULTestData[] = { @@ -276,7 +276,7 @@ TEST(SymbolicConstantsMach, StringToException) { } } -const struct { +constexpr struct { exception_mask_t exception_mask; const char* full_name; const char* short_name; @@ -322,9 +322,9 @@ struct ConvertExceptionMaskTraits { ValueType* value) { return StringToExceptionMask(string, options, value); } - static const char kValueName[]; + static constexpr char kValueName[] = "exception_mask"; }; -const char ConvertExceptionMaskTraits::kValueName[] = "exception_mask"; +constexpr char ConvertExceptionMaskTraits::kValueName[]; void TestExceptionMaskToString(exception_mask_t value, const char* expect_full, @@ -370,7 +370,7 @@ void TestStringToExceptionMask(const base::StringPiece& string, TEST(SymbolicConstantsMach, StringToExceptionMask) { // Don’t use kNormalOptions, because kAllowOr needs to be tested. - const StringToSymbolicConstantOptions kOptions[] = { + static constexpr StringToSymbolicConstantOptions kOptions[] = { 0, kAllowFullName, kAllowShortName, @@ -430,7 +430,7 @@ TEST(SymbolicConstantsMach, StringToExceptionMask) { } } - const char* const kNegativeTestData[] = { + static constexpr const char* kNegativeTestData[] = { "EXC_MASK_CRASH ", " EXC_MASK_BAD_INSTRUCTION", "EXC_MASK_EXC_MASK_BAD_ACCESS", @@ -450,7 +450,7 @@ TEST(SymbolicConstantsMach, StringToExceptionMask) { TestStringToExceptionMask(kNegativeTestData[index], options, false, 0); } - const struct { + static constexpr struct { const char* string; size_t length; } kNULTestData[] = { @@ -479,7 +479,7 @@ TEST(SymbolicConstantsMach, StringToExceptionMask) { } } - const struct { + static const struct { const char* string; StringToSymbolicConstantOptions options; exception_mask_t mask; @@ -531,7 +531,7 @@ TEST(SymbolicConstantsMach, StringToExceptionMask) { } } -const struct { +constexpr struct { exception_behavior_t behavior; const char* full_name; const char* short_name; @@ -565,9 +565,9 @@ struct ConvertExceptionBehaviorTraits { ValueType* value) { return StringToExceptionBehavior(string, options, value); } - static const char kValueName[]; + static constexpr char kValueName[] = "behavior"; }; -const char ConvertExceptionBehaviorTraits::kValueName[] = "behavior"; +constexpr char ConvertExceptionBehaviorTraits::kValueName[]; void TestExceptionBehaviorToString(exception_behavior_t value, const char* expect_full, @@ -643,7 +643,7 @@ TEST(SymbolicConstantsMach, StringToExceptionBehavior) { } } - const char* const kNegativeTestData[] = { + static constexpr const char* kNegativeTestData[] = { "EXCEPTION_DEFAULT ", " EXCEPTION_STATE", "EXCEPTION_EXCEPTION_STATE_IDENTITY", @@ -666,7 +666,7 @@ TEST(SymbolicConstantsMach, StringToExceptionBehavior) { kNegativeTestData[index], options, false, 0); } - const struct { + static constexpr struct { const char* string; size_t length; } kNULTestData[] = { @@ -694,7 +694,7 @@ TEST(SymbolicConstantsMach, StringToExceptionBehavior) { } } - const struct { + static constexpr struct { const char* string; StringToSymbolicConstantOptions options; exception_behavior_t behavior; @@ -763,7 +763,7 @@ TEST(SymbolicConstantsMach, StringToExceptionBehavior) { } } -const struct { +constexpr struct { thread_state_flavor_t flavor; const char* full_name; const char* short_name; @@ -828,9 +828,9 @@ struct ConvertThreadStateFlavorTraits { ValueType* value) { return StringToThreadStateFlavor(string, options, value); } - static const char kValueName[]; + static constexpr char kValueName[] = "flavor"; }; -const char ConvertThreadStateFlavorTraits::kValueName[] = "flavor"; +constexpr char ConvertThreadStateFlavorTraits::kValueName[]; void TestThreadStateFlavorToString(exception_type_t value, const char* expect_full, @@ -917,7 +917,7 @@ TEST(SymbolicConstantsMach, StringToThreadStateFlavor) { } } - const char* const kNegativeTestData[] = { + static constexpr const char* kNegativeTestData[] = { "THREAD_STATE_NONE ", " THREAD_STATE_NONE", "NONE ", @@ -965,7 +965,7 @@ TEST(SymbolicConstantsMach, StringToThreadStateFlavor) { kNegativeTestData[index], options, false, 0); } - const struct { + static constexpr struct { const char* string; size_t length; } kNULTestData[] = { diff --git a/util/mach/task_memory_test.cc b/util/mach/task_memory_test.cc index 514f48f1..d9f0cb77 100644 --- a/util/mach/task_memory_test.cc +++ b/util/mach/task_memory_test.cc @@ -33,7 +33,7 @@ namespace { TEST(TaskMemory, ReadSelf) { vm_address_t address = 0; - const vm_size_t kSize = 4 * PAGE_SIZE; + constexpr vm_size_t kSize = 4 * PAGE_SIZE; kern_return_t kr = vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_allocate"); @@ -102,7 +102,7 @@ TEST(TaskMemory, ReadSelf) { TEST(TaskMemory, ReadSelfUnmapped) { vm_address_t address = 0; - const vm_size_t kSize = 2 * PAGE_SIZE; + constexpr vm_size_t kSize = 2 * PAGE_SIZE; kern_return_t kr = vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_allocate"); @@ -194,13 +194,34 @@ TEST(TaskMemory, ReadCStringSelf) { EXPECT_FALSE(result.empty()); EXPECT_EQ(result, kStaticConstCharShort); + constexpr char kConstexprCharEmpty[] = ""; + ASSERT_TRUE(ReadCStringSelf(&memory, kConstexprCharEmpty, &result)); + EXPECT_TRUE(result.empty()); + EXPECT_EQ(result, kConstexprCharEmpty); + + constexpr char kConstexprCharShort[] = "A short constexpr char[]"; + ASSERT_TRUE(ReadCStringSelf(&memory, kConstexprCharShort, &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(result, kConstexprCharShort); + + static constexpr char kStaticConstexprCharEmpty[] = ""; + ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstexprCharEmpty, &result)); + EXPECT_TRUE(result.empty()); + EXPECT_EQ(result, kStaticConstexprCharEmpty); + + static constexpr char kStaticConstexprCharShort[] = + "A short static constexpr char[]"; + ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstexprCharShort, &result)); + EXPECT_FALSE(result.empty()); + EXPECT_EQ(result, kStaticConstexprCharShort); + std::string string_short("A short std::string in a function"); ASSERT_TRUE(ReadCStringSelf(&memory, &string_short[0], &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result, string_short); std::string string_long; - const size_t kStringLongSize = 4 * PAGE_SIZE; + constexpr size_t kStringLongSize = 4 * PAGE_SIZE; for (size_t index = 0; index < kStringLongSize; ++index) { // Don’t include any NUL bytes, because ReadCString stops when it encounters // a NUL. @@ -215,7 +236,7 @@ TEST(TaskMemory, ReadCStringSelf) { TEST(TaskMemory, ReadCStringSelfUnmapped) { vm_address_t address = 0; - const vm_size_t kSize = 2 * PAGE_SIZE; + constexpr vm_size_t kSize = 2 * PAGE_SIZE; kern_return_t kr = vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_allocate"); @@ -280,7 +301,7 @@ TEST(TaskMemory, ReadCStringSizeLimited_ConstCharEmpty) { TaskMemory memory(mach_task_self()); std::string result; - const char kConstCharEmpty[] = ""; + static constexpr char kConstCharEmpty[] = ""; ASSERT_TRUE(ReadCStringSizeLimitedSelf( &memory, kConstCharEmpty, arraysize(kConstCharEmpty), &result)); EXPECT_TRUE(result.empty()); @@ -302,7 +323,7 @@ TEST(TaskMemory, ReadCStringSizeLimited_ConstCharShort) { TaskMemory memory(mach_task_self()); std::string result; - const char kConstCharShort[] = "A short const char[]"; + static constexpr char kConstCharShort[] = "A short const char[]"; ASSERT_TRUE(ReadCStringSizeLimitedSelf( &memory, kConstCharShort, arraysize(kConstCharShort), &result)); EXPECT_FALSE(result.empty()); @@ -322,7 +343,7 @@ TEST(TaskMemory, ReadCStringSizeLimited_StaticConstCharEmpty) { TaskMemory memory(mach_task_self()); std::string result; - static const char kStaticConstCharEmpty[] = ""; + static constexpr char kStaticConstCharEmpty[] = ""; ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, kStaticConstCharEmpty, arraysize(kStaticConstCharEmpty), @@ -349,7 +370,8 @@ TEST(TaskMemory, ReadCStringSizeLimited_StaticConstCharShort) { TaskMemory memory(mach_task_self()); std::string result; - static const char kStaticConstCharShort[] = "A short static const char[]"; + static constexpr char kStaticConstCharShort[] = + "A short static constexpr char[]"; ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, kStaticConstCharShort, arraysize(kStaticConstCharShort), @@ -396,7 +418,7 @@ TEST(TaskMemory, ReadCStringSizeLimited_StringLong) { std::string result; std::string string_long; - const size_t kStringLongSize = 4 * PAGE_SIZE; + constexpr size_t kStringLongSize = 4 * PAGE_SIZE; for (size_t index = 0; index < kStringLongSize; ++index) { // Don’t include any NUL bytes, because ReadCString stops when it encounters // a NUL. @@ -461,7 +483,7 @@ TEST(TaskMemory, MappedMemoryDeallocates) { TaskMemory memory(mach_task_self()); std::unique_ptr mapped; - static const char kTestBuffer[] = "hello!"; + static constexpr char kTestBuffer[] = "hello!"; mach_vm_address_t test_address = FromPointerCast(&kTestBuffer); ASSERT_TRUE((mapped = memory.ReadMapped(test_address, sizeof(kTestBuffer)))); @@ -476,7 +498,7 @@ TEST(TaskMemory, MappedMemoryDeallocates) { // This is the same but with a big buffer that’s definitely larger than a // single page. This makes sure that the whole mapped region winds up being // deallocated. - const size_t kBigSize = 4 * PAGE_SIZE; + constexpr size_t kBigSize = 4 * PAGE_SIZE; std::unique_ptr big_buffer(new char[kBigSize]); test_address = FromPointerCast(&big_buffer[0]); ASSERT_TRUE((mapped = memory.ReadMapped(test_address, kBigSize))); @@ -498,7 +520,7 @@ TEST(TaskMemory, MappedMemoryReadCString) { TaskMemory memory(mach_task_self()); std::unique_ptr mapped; - static const char kTestBuffer[] = "0\0" "2\0" "45\0" "789"; + static constexpr char kTestBuffer[] = "0\0" "2\0" "45\0" "789"; const mach_vm_address_t kTestAddress = FromPointerCast(&kTestBuffer); ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 10))); diff --git a/util/misc/clock_posix.cc b/util/misc/clock_posix.cc index 24171091..51c1406d 100644 --- a/util/misc/clock_posix.cc +++ b/util/misc/clock_posix.cc @@ -22,7 +22,7 @@ namespace { -const uint64_t kNanosecondsPerSecond = 1E9; +constexpr uint64_t kNanosecondsPerSecond = 1E9; } // namespace diff --git a/util/misc/clock_test.cc b/util/misc/clock_test.cc index d7f9cd71..95330eb2 100644 --- a/util/misc/clock_test.cc +++ b/util/misc/clock_test.cc @@ -71,7 +71,7 @@ void TestSleepNanoseconds(uint64_t nanoseconds) { } TEST(Clock, SleepNanoseconds) { - const uint64_t kTestData[] = { + static constexpr uint64_t kTestData[] = { 0, 1, static_cast(1E3), // 1 microsecond diff --git a/util/misc/clock_win.cc b/util/misc/clock_win.cc index 2de7ae75..18d07545 100644 --- a/util/misc/clock_win.cc +++ b/util/misc/clock_win.cc @@ -40,7 +40,7 @@ uint64_t ClockMonotonicNanoseconds() { int64_t frequency = QpcFrequency(); int64_t whole_seconds = time.QuadPart / frequency; int64_t leftover_ticks = time.QuadPart % frequency; - const int64_t kNanosecondsPerSecond = static_cast(1E9); + constexpr int64_t kNanosecondsPerSecond = static_cast(1E9); return (whole_seconds * kNanosecondsPerSecond) + ((leftover_ticks * kNanosecondsPerSecond) / frequency); } diff --git a/util/misc/implicit_cast.h b/util/misc/implicit_cast.h index 6f8a1a30..018800ab 100644 --- a/util/misc/implicit_cast.h +++ b/util/misc/implicit_cast.h @@ -34,8 +34,8 @@ namespace crashpad { // implicit_cast would have been part of the C++ standard library, // but the proposal was submitted too late. It will probably make // its way into the language in the future. -template -inline To implicit_cast(From const &f) { +template +constexpr To implicit_cast(From const& f) { return f; } diff --git a/util/misc/lexing.cc b/util/misc/lexing.cc new file mode 100644 index 00000000..6c318654 --- /dev/null +++ b/util/misc/lexing.cc @@ -0,0 +1,75 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util/misc/lexing.h" + +#include +#include +#include +#include + +#include + +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" + +namespace crashpad { + +namespace { + +#define MAKE_ADAPTER(type, function) \ + bool ConvertStringToNumber(const base::StringPiece& input, type* value) { \ + return function(input, value); \ + } +MAKE_ADAPTER(int, base::StringToInt); +MAKE_ADAPTER(unsigned int, base::StringToUint); +MAKE_ADAPTER(int64_t, base::StringToInt64); +MAKE_ADAPTER(uint64_t, base::StringToUint64); +#undef MAKE_ADAPTER + +} // namespace + +bool AdvancePastPrefix(const char** input, const char* pattern) { + size_t length = strlen(pattern); + if (strncmp(*input, pattern, length) == 0) { + *input += length; + return true; + } + return false; +} + +template +bool AdvancePastNumber(const char** input, T* value) { + size_t length = 0; + if (std::numeric_limits::is_signed && **input == '-') { + ++length; + } + while (isdigit((*input)[length])) { + ++length; + } + bool success = + ConvertStringToNumber(base::StringPiece(*input, length), value); + if (success) { + *input += length; + return true; + } + return false; +} + +template bool AdvancePastNumber(const char** input, int* value); +template bool AdvancePastNumber(const char** input, unsigned int* value); +template bool AdvancePastNumber(const char** input, int64_t* value); +template bool AdvancePastNumber(const char** input, uint64_t* value); + +} // namespace crashpad diff --git a/util/misc/lexing.h b/util/misc/lexing.h new file mode 100644 index 00000000..ba37e0ca --- /dev/null +++ b/util/misc/lexing.h @@ -0,0 +1,44 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_UTIL_MISC_LEXING_H_ +#define CRASHPAD_UTIL_MISC_LEXING_H_ + +namespace crashpad { + +//! \brief Match a pattern at the start of a char string. +//! +//! \param[in,out] input A pointer to the char string to match against. \a input +//! is advanced past the matched pattern if it is found. +//! \param[in] pattern The pattern to match at the start of \a input. +//! \return `true` if the pattern is matched exactly and \a input is advanced, +//! otherwise `false`. +bool AdvancePastPrefix(const char** input, const char* pattern); + +//! \brief Convert a prefix of a char string to a numeric value. +//! +//! Valid values are positive or negative decimal numbers, matching the regular +//! expression "-?\d+", and within the limits of T. +//! +//! \param[in,out] input A pointer to the char string to match against. \a input +//! is advanced past the number if one is found. +//! \param[out] value The converted number, if one is found. +//! \return `true` if a number is found at the start of \a input and \a input is +//! advanced, otherwise `false`. +template +bool AdvancePastNumber(const char** input, T* value); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MISC_LEXING_H diff --git a/util/misc/scoped_forbid_return_test.cc b/util/misc/scoped_forbid_return_test.cc index 78787d49..4f73ea58 100644 --- a/util/misc/scoped_forbid_return_test.cc +++ b/util/misc/scoped_forbid_return_test.cc @@ -43,7 +43,7 @@ void ScopedForbidReturnHelper(ForbidReturnType type) { } } -const char kForbiddenMessage[] = "attempt to exit scope forbidden"; +constexpr char kForbiddenMessage[] = "attempt to exit scope forbidden"; TEST(ScopedForbidReturnDeathTest, Default) { // kForbiddenMessage may appear to be unused if ASSERT_DEATH_CHECK() throws it diff --git a/util/misc/uuid.cc b/util/misc/uuid.cc index ac2468c6..12c7935a 100644 --- a/util/misc/uuid.cc +++ b/util/misc/uuid.cc @@ -66,7 +66,7 @@ bool UUID::InitializeFromString(const base::StringPiece& string) { return false; UUID temp; - const char kScanFormat[] = + static constexpr char kScanFormat[] = "%08" SCNx32 "-%04" SCNx16 "-%04" SCNx16 "-%02" SCNx8 "%02" SCNx8 "-%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8 "%02" SCNx8; diff --git a/util/misc/uuid_test.cc b/util/misc/uuid_test.cc index e550751b..72b8216b 100644 --- a/util/misc/uuid_test.cc +++ b/util/misc/uuid_test.cc @@ -45,22 +45,22 @@ TEST(UUID, UUID) { EXPECT_EQ(uuid_zero.data_5[5], 0u); EXPECT_EQ(uuid_zero.ToString(), "00000000-0000-0000-0000-000000000000"); - const uint8_t kBytes[16] = {0x00, - 0x01, - 0x02, - 0x03, - 0x04, - 0x05, - 0x06, - 0x07, - 0x08, - 0x09, - 0x0a, - 0x0b, - 0x0c, - 0x0d, - 0x0e, - 0x0f}; + static constexpr uint8_t kBytes[16] = {0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0a, + 0x0b, + 0x0c, + 0x0d, + 0x0e, + 0x0f}; UUID uuid; uuid.InitializeFromBytes(kBytes); EXPECT_EQ(uuid.data_1, 0x00010203u); @@ -110,22 +110,22 @@ TEST(UUID, UUID) { // have been valid. EXPECT_EQ(uuid_2, uuid); - const uint8_t kMoreBytes[16] = {0xff, - 0xee, - 0xdd, - 0xcc, - 0xbb, - 0xaa, - 0x99, - 0x88, - 0x77, - 0x66, - 0x55, - 0x44, - 0x33, - 0x22, - 0x11, - 0x00}; + static constexpr uint8_t kMoreBytes[16] = {0xff, + 0xee, + 0xdd, + 0xcc, + 0xbb, + 0xaa, + 0x99, + 0x88, + 0x77, + 0x66, + 0x55, + 0x44, + 0x33, + 0x22, + 0x11, + 0x00}; uuid.InitializeFromBytes(kMoreBytes); EXPECT_EQ(uuid.data_1, 0xffeeddccu); EXPECT_EQ(uuid.data_2, 0xbbaau); @@ -164,7 +164,7 @@ TEST(UUID, UUID) { } TEST(UUID, FromString) { - const struct TestCase { + static constexpr struct TestCase { const char* uuid_string; bool success; } kCases[] = { diff --git a/util/net/http_body_gzip.cc b/util/net/http_body_gzip.cc index 70f4db35..02abe713 100644 --- a/util/net/http_body_gzip.cc +++ b/util/net/http_body_gzip.cc @@ -54,8 +54,8 @@ FileOperationResult GzipHTTPBodyStream::GetBytesBuffer(uint8_t* buffer, // are the values that deflateInit() would use, but they’re not exported // from zlib. deflateInit2() is used instead of deflateInit() to get the // gzip wrapper. - const int kZlibMaxWindowBits = 15; - const int kZlibDefaultMemoryLevel = 8; + constexpr int kZlibMaxWindowBits = 15; + constexpr int kZlibDefaultMemoryLevel = 8; int zr = deflateInit2(z_stream_.get(), Z_DEFAULT_COMPRESSION, diff --git a/util/net/http_body_gzip_test.cc b/util/net/http_body_gzip_test.cc index 770f64af..85887502 100644 --- a/util/net/http_body_gzip_test.cc +++ b/util/net/http_body_gzip_test.cc @@ -83,7 +83,7 @@ void TestGzipDeflateInflate(const std::string& string) { // The minimum size of a gzip wrapper per RFC 1952: a 10-byte header and an // 8-byte trailer. - const size_t kGzipHeaderSize = 18; + constexpr size_t kGzipHeaderSize = 18; // Per http://www.zlib.net/zlib_tech.html, in the worst case, zlib will store // uncompressed data as-is, at an overhead of 5 bytes per 16384-byte block. diff --git a/util/net/http_headers.h b/util/net/http_headers.h index 851ff31c..50335063 100644 --- a/util/net/http_headers.h +++ b/util/net/http_headers.h @@ -24,13 +24,13 @@ namespace crashpad { using HTTPHeaders = std::map; //! \brief The header name `"Content-Type"`. -extern const char kContentType[]; +constexpr char kContentType[] = "Content-Type"; //! \brief The header name `"Content-Length"`. -extern const char kContentLength[]; +constexpr char kContentLength[] = "Content-Length"; //! \brief The header name `"Content-Encoding"`. -extern const char kContentEncoding[]; +constexpr char kContentEncoding[] = "Content-Encoding"; } // namespace crashpad diff --git a/util/net/http_multipart_builder.cc b/util/net/http_multipart_builder.cc index 91105be9..cd88e430 100644 --- a/util/net/http_multipart_builder.cc +++ b/util/net/http_multipart_builder.cc @@ -29,9 +29,9 @@ namespace crashpad { namespace { -const char kCRLF[] = "\r\n"; +constexpr char kCRLF[] = "\r\n"; -const char kBoundaryCRLF[] = "\r\n\r\n"; +constexpr char kBoundaryCRLF[] = "\r\n\r\n"; // Generates a random string suitable for use as a multipart boundary. std::string GenerateBoundaryString() { @@ -47,7 +47,7 @@ std::string GenerateBoundaryString() { // randomness (62^32 > 2^190). std::string boundary_string = "---MultipartBoundary-"; for (int index = 0; index < 32; ++index) { - const char kCharacters[] = + static constexpr char kCharacters[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; int random_value = base::RandInt(0, static_cast(strlen(kCharacters)) - 1); diff --git a/util/net/http_multipart_builder_test.cc b/util/net/http_multipart_builder_test.cc index 79366bc2..71b2fa84 100644 --- a/util/net/http_multipart_builder_test.cc +++ b/util/net/http_multipart_builder_test.cc @@ -55,16 +55,16 @@ std::vector SplitCRLF(const std::string& string) { TEST(HTTPMultipartBuilder, ThreeStringFields) { HTTPMultipartBuilder builder; - const char kKey1[] = "key1"; - const char kValue1[] = "test"; + static constexpr char kKey1[] = "key1"; + static constexpr char kValue1[] = "test"; builder.SetFormData(kKey1, kValue1); - const char kKey2[] = "key2"; - const char kValue2[] = "This is another test."; + static constexpr char kKey2[] = "key2"; + static constexpr char kValue2[] = "This is another test."; builder.SetFormData(kKey2, kValue2); - const char kKey3[] = "key-three"; - const char kValue3[] = "More tests"; + static constexpr char kKey3[] = "key-three"; + static constexpr char kValue3[] = "More tests"; builder.SetFormData(kKey3, kValue3); std::unique_ptr body(builder.GetBodyStream()); @@ -115,7 +115,7 @@ TEST(HTTPMultipartBuilder, ThreeFileAttachments) { ascii_http_body_path, "text/plain"); - const char kFileContents[] = "This is a test.\n"; + static constexpr char kFileContents[] = "This is a test.\n"; std::unique_ptr body(builder.GetBodyStream()); ASSERT_TRUE(body.get()); @@ -158,7 +158,7 @@ TEST(HTTPMultipartBuilder, ThreeFileAttachments) { TEST(HTTPMultipartBuilder, OverwriteFormDataWithEscapedKey) { HTTPMultipartBuilder builder; - const char kKey[] = "a 100% \"silly\"\r\ntest"; + static constexpr char kKey[] = "a 100% \"silly\"\r\ntest"; builder.SetFormData(kKey, "some dummy value"); builder.SetFormData(kKey, "overwrite"); std::unique_ptr body(builder.GetBodyStream()); @@ -183,7 +183,7 @@ TEST(HTTPMultipartBuilder, OverwriteFormDataWithEscapedKey) { TEST(HTTPMultipartBuilder, OverwriteFileAttachment) { HTTPMultipartBuilder builder; - const char kValue[] = "1 2 3 test"; + static constexpr char kValue[] = "1 2 3 test"; builder.SetFormData("a key", kValue); base::FilePath testdata_path = TestPaths::TestDataRoot().Append(FILE_PATH_LITERAL("util/net/testdata")); @@ -240,7 +240,7 @@ TEST(HTTPMultipartBuilder, OverwriteFileAttachment) { TEST(HTTPMultipartBuilder, SharedFormDataAndAttachmentKeyNamespace) { HTTPMultipartBuilder builder; - const char kValue1[] = "11111"; + static constexpr char kValue1[] = "11111"; builder.SetFormData("one", kValue1); base::FilePath ascii_http_body_path = TestPaths::TestDataRoot().Append( FILE_PATH_LITERAL("util/net/testdata/ascii_http_body.txt")); @@ -248,7 +248,7 @@ TEST(HTTPMultipartBuilder, SharedFormDataAndAttachmentKeyNamespace) { "minidump.dmp", ascii_http_body_path, ""); - const char kValue2[] = "this is not a file"; + static constexpr char kValue2[] = "this is not a file"; builder.SetFormData("minidump", kValue2); std::unique_ptr body(builder.GetBodyStream()); diff --git a/util/net/http_transport_libcurl.cc b/util/net/http_transport_libcurl.cc index 1a20913a..c16a593d 100644 --- a/util/net/http_transport_libcurl.cc +++ b/util/net/http_transport_libcurl.cc @@ -57,31 +57,31 @@ std::string UserAgent() { // linux-4.9.17/arch/x86/kernel/cpu/bugs.c check_bugs() sets the first digit // to 4, 5, or 6, but no higher. #if defined(__i686__) - const char arch[] = "i686"; + static constexpr char arch[] = "i686"; #elif defined(__i586__) - const char arch[] = "i586"; + static constexpr char arch[] = "i586"; #elif defined(__i486__) - const char arch[] = "i486"; + static constexpr char arch[] = "i486"; #else - const char arch[] = "i386"; + static constexpr char arch[] = "i386"; #endif #elif defined(ARCH_CPU_X86_64) - const char arch[] = "x86_64"; + static constexpr char arch[] = "x86_64"; #elif defined(ARCH_CPU_ARMEL) // linux-4.9.17/arch/arm/kernel/setup.c setup_processor() bases the string // on the ARM processor name and a character identifying little- or // big-endian. The processor name comes from a definition in // arch/arm/mm/proc-*.S. #if defined(__ARM_ARCH_4T__) - const char arch[] = "armv4t" + static constexpr char arch[] = "armv4t" #elif defined(__ARM_ARCH_5TEJ__) - const char arch[] = "armv5tej" + static constexpr char arch[] = "armv5tej" #elif defined(__ARM_ARCH_5TE__) - const char arch[] = "armv5te" + static constexpr char arch[] = "armv5te" #elif defined(__ARM_ARCH_5T__) - const char arch[] = "armv5t" + static constexpr char arch[] = "armv5t" #elif defined(__ARM_ARCH_7M__) - const char arch[] = "armv7m" + static constexpr char arch[] = "armv7m" #else // Most ARM architectures fall into here, including all profile variants of // armv6, armv7, armv8, with one exception, armv7m, handled above. @@ -89,7 +89,7 @@ std::string UserAgent() { // or 8. #define xstr(s) str(s) #define str(s) #s - const char arch[] = "armv" xstr(__ARM_ARCH) + static constexpr char arch[] = "armv" xstr(__ARM_ARCH) #undef str #undef xstr #endif @@ -102,14 +102,14 @@ std::string UserAgent() { // ARM64 uses aarch64 or aarch64_be as directed by ELF_PLATFORM. See // linux-4.9.17/arch/arm64/kernel/setup.c setup_arch(). #if defined(ARCH_CPU_LITTLE_ENDIAN) - const char arch[] = "aarch64"; + static constexpr char arch[] = "aarch64"; #elif defined(ARCH_CPU_BIG_ENDIAN) - const char arch[] = "aarch64_be"; + static constexpr char arch[] = "aarch64_be"; #endif #elif defined(ARCH_CPU_MIPSEL) - const char arch[] = "mips"; + static constexpr char arch[] = "mips"; #elif defined(ARCH_CPU_MIPS64EL) - const char arch[] = "mips64"; + static constexpr char arch[] = "mips64"; #else #error Port #endif @@ -257,7 +257,7 @@ bool HTTPTransportLibcurl::ExecuteSynchronously(std::string* response_body) { TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_URL, url().c_str()); - const int kMillisecondsPerSecond = 1E3; + constexpr int kMillisecondsPerSecond = 1E3; TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_TIMEOUT_MS, static_cast(timeout() * kMillisecondsPerSecond)); diff --git a/util/net/http_transport_mac.mm b/util/net/http_transport_mac.mm index 5b4f18c7..8d5f78cc 100644 --- a/util/net/http_transport_mac.mm +++ b/util/net/http_transport_mac.mm @@ -118,23 +118,23 @@ class HTTPBodyStreamCFReadStream { // Creates a new NSInputStream, which the caller owns. NSInputStream* CreateInputStream() { CFStreamClientContext context = { - .version = 0, - .info = this, - .retain = nullptr, - .release = nullptr, - .copyDescription = nullptr + .version = 0, + .info = this, + .retain = nullptr, + .release = nullptr, + .copyDescription = nullptr }; - const CFReadStreamCallBacksV0 callbacks = { - .version = 0, - .open = &Open, - .openCompleted = &OpenCompleted, - .read = &Read, - .getBuffer = &GetBuffer, - .canRead = &CanRead, - .close = &Close, - .copyProperty = &CopyProperty, - .schedule = &Schedule, - .unschedule = &Unschedule + constexpr CFReadStreamCallBacksV0 callbacks = { + .version = 0, + .open = &Open, + .openCompleted = &OpenCompleted, + .read = &Read, + .getBuffer = &GetBuffer, + .canRead = &CanRead, + .close = &Close, + .copyProperty = &CopyProperty, + .schedule = &Schedule, + .unschedule = &Unschedule }; CFReadStreamRef read_stream = CFReadStreamCreate(nullptr, reinterpret_cast(&callbacks), &context); diff --git a/util/net/http_transport_test.cc b/util/net/http_transport_test.cc index 70c3eee4..3b7e7ebd 100644 --- a/util/net/http_transport_test.cc +++ b/util/net/http_transport_test.cc @@ -136,7 +136,7 @@ class HTTPTransportTestFixture : public MultiprocessExec { RequestValidator request_validator_; }; -const char kMultipartFormData[] = "multipart/form-data"; +constexpr char kMultipartFormData[] = "multipart/form-data"; void GetHeaderField(const std::string& request, const std::string& header, @@ -179,7 +179,7 @@ void GetMultipartBoundary(const std::string& request, } } -const char kBoundaryEq[] = "boundary="; +constexpr char kBoundaryEq[] = "boundary="; void ValidFormData(HTTPTransportTestFixture* fixture, const std::string& request) { @@ -242,7 +242,7 @@ TEST(HTTPTransport, ValidFormData_Gzip) { test.Run(); } -const char kTextPlain[] = "text/plain"; +constexpr char kTextPlain[] = "text/plain"; void ErrorResponse(HTTPTransportTestFixture* fixture, const std::string& request) { @@ -260,7 +260,7 @@ TEST(HTTPTransport, ErrorResponse) { test.Run(); } -const char kTextBody[] = "hello world"; +constexpr char kTextBody[] = "hello world"; void UnchunkedPlainText(HTTPTransportTestFixture* fixture, const std::string& request) { diff --git a/util/net/http_transport_win.cc b/util/net/http_transport_win.cc index 56fdaad1..27cd4048 100644 --- a/util/net/http_transport_win.cc +++ b/util/net/http_transport_win.cc @@ -39,7 +39,7 @@ namespace crashpad { namespace { -const wchar_t kWinHttpDll[] = L"winhttp.dll"; +constexpr wchar_t kWinHttpDll[] = L"winhttp.dll"; std::string UserAgent() { std::string user_agent = @@ -242,7 +242,8 @@ bool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) { DWORD content_length_dword; if (chunked) { - const wchar_t kTransferEncodingHeader[] = L"Transfer-Encoding: chunked\r\n"; + static constexpr wchar_t kTransferEncodingHeader[] = + L"Transfer-Encoding: chunked\r\n"; if (!WinHttpAddRequestHeaders( request.get(), kTransferEncodingHeader, diff --git a/util/numeric/checked_address_range_test.cc b/util/numeric/checked_address_range_test.cc index fa7ae87d..e6bd9ecd 100644 --- a/util/numeric/checked_address_range_test.cc +++ b/util/numeric/checked_address_range_test.cc @@ -46,7 +46,7 @@ bool ExpectationForValidity64(Validity validity) { } TEST(CheckedAddressRange, IsValid) { - const struct TestData { + static constexpr struct { uint64_t base; uint64_t size; Validity validity; @@ -120,7 +120,7 @@ TEST(CheckedAddressRange, IsValid) { }; for (size_t index = 0; index < arraysize(kTestData); ++index) { - const TestData& testcase = kTestData[index]; + const auto& testcase = kTestData[index]; SCOPED_TRACE(base::StringPrintf("index %" PRIuS ", base 0x%" PRIx64 ", size 0x%" PRIx64, index, @@ -136,7 +136,7 @@ TEST(CheckedAddressRange, IsValid) { } TEST(CheckedAddressRange, ContainsValue) { - const struct TestData { + static constexpr struct { uint64_t value; bool expectation; } kTestData[] = { @@ -171,7 +171,7 @@ TEST(CheckedAddressRange, ContainsValue) { ASSERT_TRUE(parent_range_32.IsValid()); for (size_t index = 0; index < arraysize(kTestData); ++index) { - const TestData& testcase = kTestData[index]; + const auto& testcase = kTestData[index]; SCOPED_TRACE(base::StringPrintf( "index %" PRIuS ", value 0x%" PRIx64, index, testcase.value)); @@ -189,7 +189,7 @@ TEST(CheckedAddressRange, ContainsValue) { } TEST(CheckedAddressRange, ContainsRange) { - const struct TestData { + static constexpr struct { uint64_t base; uint64_t size; bool expectation; @@ -228,7 +228,7 @@ TEST(CheckedAddressRange, ContainsRange) { ASSERT_TRUE(parent_range_32.IsValid()); for (size_t index = 0; index < arraysize(kTestData); ++index) { - const TestData& testcase = kTestData[index]; + const auto& testcase = kTestData[index]; SCOPED_TRACE(base::StringPrintf("index %" PRIuS ", base 0x%" PRIx64 ", size 0x%" PRIx64, index, diff --git a/util/numeric/checked_range_test.cc b/util/numeric/checked_range_test.cc index 404b9898..04f6bb1f 100644 --- a/util/numeric/checked_range_test.cc +++ b/util/numeric/checked_range_test.cc @@ -29,7 +29,7 @@ namespace test { namespace { TEST(CheckedRange, IsValid) { - const struct UnsignedTestData { + static constexpr struct { uint32_t base; uint32_t size; bool valid; @@ -79,7 +79,7 @@ TEST(CheckedRange, IsValid) { }; for (size_t index = 0; index < arraysize(kUnsignedTestData); ++index) { - const UnsignedTestData& testcase = kUnsignedTestData[index]; + const auto& testcase = kUnsignedTestData[index]; SCOPED_TRACE(base::StringPrintf("unsigned index %" PRIuS ", base 0x%x, size 0x%x", index, @@ -91,7 +91,7 @@ TEST(CheckedRange, IsValid) { } const int32_t kMinInt32 = std::numeric_limits::min(); - const struct SignedTestData { + static constexpr struct { int32_t base; uint32_t size; bool valid; @@ -141,7 +141,7 @@ TEST(CheckedRange, IsValid) { }; for (size_t index = 0; index < arraysize(kSignedTestData); ++index) { - const SignedTestData& testcase = kSignedTestData[index]; + const auto& testcase = kSignedTestData[index]; SCOPED_TRACE(base::StringPrintf("signed index %" PRIuS ", base 0x%x, size 0x%x", index, @@ -154,7 +154,7 @@ TEST(CheckedRange, IsValid) { } TEST(CheckedRange, ContainsValue) { - const struct TestData { + static constexpr struct { uint32_t value; bool contains; } kTestData[] = { @@ -187,7 +187,7 @@ TEST(CheckedRange, ContainsValue) { ASSERT_TRUE(parent_range.IsValid()); for (size_t index = 0; index < arraysize(kTestData); ++index) { - const TestData& testcase = kTestData[index]; + const auto& testcase = kTestData[index]; SCOPED_TRACE(base::StringPrintf( "index %" PRIuS ", value 0x%x", index, testcase.value)); @@ -196,7 +196,7 @@ TEST(CheckedRange, ContainsValue) { } TEST(CheckedRange, ContainsRange) { - const struct TestData { + static constexpr struct { uint32_t base; uint32_t size; bool contains; @@ -235,7 +235,7 @@ TEST(CheckedRange, ContainsRange) { ASSERT_TRUE(parent_range.IsValid()); for (size_t index = 0; index < arraysize(kTestData); ++index) { - const TestData& testcase = kTestData[index]; + const auto& testcase = kTestData[index]; SCOPED_TRACE(base::StringPrintf("index %" PRIuS ", base 0x%x, size 0x%x", index, testcase.base, @@ -248,7 +248,7 @@ TEST(CheckedRange, ContainsRange) { } TEST(CheckedRange, OverlapsRange) { - const struct TestData { + static constexpr struct { uint32_t base; uint32_t size; bool overlaps; @@ -288,7 +288,7 @@ TEST(CheckedRange, OverlapsRange) { ASSERT_TRUE(first_range.IsValid()); for (size_t index = 0; index < arraysize(kTestData); ++index) { - const TestData& testcase = kTestData[index]; + const auto& testcase = kTestData[index]; SCOPED_TRACE(base::StringPrintf("index %" PRIuS ", base 0x%x, size 0x%x", index, testcase.base, diff --git a/util/numeric/in_range_cast_test.cc b/util/numeric/in_range_cast_test.cc index 6e74129f..73f5fd57 100644 --- a/util/numeric/in_range_cast_test.cc +++ b/util/numeric/in_range_cast_test.cc @@ -25,8 +25,8 @@ namespace crashpad { namespace test { namespace { -const int32_t kInt32Min = std::numeric_limits::min(); -const int64_t kInt64Min = std::numeric_limits::min(); +constexpr int32_t kInt32Min = std::numeric_limits::min(); +constexpr int64_t kInt64Min = std::numeric_limits::min(); TEST(InRangeCast, Uint32) { EXPECT_EQ(InRangeCast(0, 1), 0u); diff --git a/util/numeric/int128_test.cc b/util/numeric/int128_test.cc index e0b45999..83b4f6e8 100644 --- a/util/numeric/int128_test.cc +++ b/util/numeric/int128_test.cc @@ -23,10 +23,10 @@ namespace { TEST(Int128, UInt128) { #if defined(ARCH_CPU_LITTLE_ENDIAN) - const uint8_t kBytes[] = + static constexpr uint8_t kBytes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; #else - const uint8_t kBytes[] = + static constexpr uint8_t kBytes[] = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; #endif diff --git a/util/posix/close_multiple.cc b/util/posix/close_multiple.cc index 908febf3..6e31eb9c 100644 --- a/util/posix/close_multiple.cc +++ b/util/posix/close_multiple.cc @@ -75,9 +75,9 @@ void CloseNowOrOnExec(int fd, bool ebadf_ok) { // no attempt needs to be made to close file descriptors that are not open. bool CloseMultipleNowOrOnExecUsingFDDir(int fd, int preserve_fd) { #if defined(OS_MACOSX) - const char kFDDir[] = "/dev/fd"; + static constexpr char kFDDir[] = "/dev/fd"; #elif defined(OS_LINUX) || defined(OS_ANDROID) - const char kFDDir[] = "/proc/self/fd"; + static constexpr char kFDDir[] = "/proc/self/fd"; #endif DIR* dir = opendir(kFDDir); @@ -99,10 +99,10 @@ bool CloseMultipleNowOrOnExecUsingFDDir(int fd, int preserve_fd) { // readdir_r() is deprecated as of glibc 2.24. See // https://sourceware.org/bugzilla/show_bug.cgi?id=19056 and // https://git.kernel.org/cgit/docs/man-pages/man-pages.git/commit?id=0c52f6d623636a61eacd0f7b7a3bb942793a2a05. - const char kReaddirName[] = "readdir"; + static constexpr char kReaddirName[] = "readdir"; while ((errno = 0, result = readdir(dir)) != nullptr) #else - const char kReaddirName[] = "readdir_r"; + static constexpr char kReaddirName[] = "readdir_r"; dirent entry; while ((errno = readdir_r(dir, &entry, &result)) == 0 && result != nullptr) #endif diff --git a/util/posix/process_info_linux.cc b/util/posix/process_info_linux.cc index ac3a2aea..05896d27 100644 --- a/util/posix/process_info_linux.cc +++ b/util/posix/process_info_linux.cc @@ -14,84 +14,18 @@ #include "util/posix/process_info.h" -#include -#include -#include -#include -#include +#include #include "base/files/file_path.h" -#include "base/files/scoped_file.h" #include "base/logging.h" -#include "base/posix/eintr_wrapper.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_piece.h" #include "util/file/delimited_file_reader.h" -#include "util/file/file_io.h" #include "util/file/file_reader.h" +#include "util/linux/proc_stat_reader.h" #include "util/linux/thread_info.h" +#include "util/misc/lexing.h" namespace crashpad { -namespace { - -// If the string |pattern| is matched exactly at the start of |input|, advance -// |input| past |pattern| and return true. -bool AdvancePastPrefix(const char** input, const char* pattern) { - size_t length = strlen(pattern); - if (strncmp(*input, pattern, length) == 0) { - *input += length; - return true; - } - return false; -} - -#define MAKE_ADAPTER(type, function) \ - bool ConvertStringToNumber(const base::StringPiece& input, type* value) { \ - return function(input, value); \ - } -MAKE_ADAPTER(int, base::StringToInt) -MAKE_ADAPTER(unsigned int, base::StringToUint) -MAKE_ADAPTER(uint64_t, base::StringToUint64) -#undef MAKE_ADAPTER - -// Attempt to convert a prefix of |input| to numeric type T. On success, set -// |value| to the number, advance |input| past the number, and return true. -template -bool AdvancePastNumber(const char** input, T* value) { - size_t length = 0; - if (std::numeric_limits::is_signed && **input == '-') { - ++length; - } - while (isdigit((*input)[length])) { - ++length; - } - bool success = ConvertStringToNumber(base::StringPiece(*input, length), - value); - if (success) { - *input += length; - return true; - } - return false; -} - -void SubtractTimespec(const timespec& t1, const timespec& t2, - timespec* result) { - result->tv_sec = t1.tv_sec - t2.tv_sec; - result->tv_nsec = t1.tv_nsec - t2.tv_nsec; - if (result->tv_nsec < 0) { - result->tv_sec -= 1; - result->tv_nsec += static_cast(1E9); - } -} - -void TimespecToTimeval(const timespec& ts, timeval* tv) { - tv->tv_sec = ts.tv_sec; - tv->tv_usec = ts.tv_nsec / 1000; -} - -} // namespace - ProcessInfo::ProcessInfo() : supplementary_groups_(), start_time_(), @@ -293,9 +227,9 @@ bool ProcessInfo::Is64Bit(bool* is_64_bit) const { is_64_bit_initialized_.set_invalid(); #if defined(ARCH_CPU_64_BITS) - const bool am_64_bit = true; + constexpr bool am_64_bit = true; #else - const bool am_64_bit = false; + constexpr bool am_64_bit = false; #endif if (pid_ == getpid()) { @@ -324,74 +258,13 @@ bool ProcessInfo::StartTime(timeval* start_time) const { if (start_time_initialized_.is_uninitialized()) { start_time_initialized_.set_invalid(); - - char path[32]; - snprintf(path, sizeof(path), "/proc/%d/stat", pid_); - std::string stat_contents; - if (!LoggingReadEntireFile(base::FilePath(path), &stat_contents)) { + ProcStatReader reader; + if (!reader.Initialize(pid_)) { return false; } - - // The process start time is the 22nd column. - // The second column is the executable name in parentheses. - // The executable name may have parentheses itself, so find the end of the - // second column by working backwards to find the last closing parens and - // then count forward to the 22nd column. - size_t stat_pos = stat_contents.rfind(')'); - if (stat_pos == std::string::npos) { - LOG(ERROR) << "format error"; + if (!reader.StartTime(&start_time_)) { return false; } - - for (int index = 1; index < 21; ++index) { - stat_pos = stat_contents.find(' ', stat_pos); - if (stat_pos == std::string::npos) { - break; - } - ++stat_pos; - } - if (stat_pos >= stat_contents.size()) { - LOG(ERROR) << "format error"; - return false; - } - - const char* ticks_ptr = &stat_contents[stat_pos]; - - // start time is in jiffies instead of clock ticks pre 2.6. - uint64_t ticks_after_boot; - if (!AdvancePastNumber(&ticks_ptr, &ticks_after_boot)) { - LOG(ERROR) << "format error"; - return false; - } - long clock_ticks_per_s = sysconf(_SC_CLK_TCK); - if (clock_ticks_per_s <= 0) { - PLOG(ERROR) << "sysconf"; - return false; - } - timeval time_after_boot; - time_after_boot.tv_sec = ticks_after_boot / clock_ticks_per_s; - time_after_boot.tv_usec = - (ticks_after_boot % clock_ticks_per_s) * - (static_cast(1E6) / clock_ticks_per_s); - - timespec uptime; - if (clock_gettime(CLOCK_BOOTTIME, &uptime) != 0) { - PLOG(ERROR) << "clock_gettime"; - return false; - } - - timespec current_time; - if (clock_gettime(CLOCK_REALTIME, ¤t_time) != 0) { - PLOG(ERROR) << "clock_gettime"; - return false; - } - - timespec boot_time_ts; - SubtractTimespec(current_time, uptime, &boot_time_ts); - timeval boot_time_tv; - TimespecToTimeval(boot_time_ts, &boot_time_tv); - timeradd(&boot_time_tv, &time_after_boot, &start_time_); - start_time_initialized_.set_valid(); } diff --git a/util/posix/signals.cc b/util/posix/signals.cc index a19fbafb..d79d53ab 100644 --- a/util/posix/signals.cc +++ b/util/posix/signals.cc @@ -238,7 +238,7 @@ void Signals::RestoreHandlerAndReraiseSignalOnReturn( // Failures in this function should _exit(kFailureExitCode). This is a quick // and quiet failure. This function runs in signal handler context, and it’s // difficult to safely be loud from a signal handler. - const int kFailureExitCode = 191; + constexpr int kFailureExitCode = 191; struct sigaction default_action; sigemptyset(&default_action.sa_mask); diff --git a/util/posix/symbolic_constants_posix.cc b/util/posix/symbolic_constants_posix.cc index 4ea2300b..51cc583b 100644 --- a/util/posix/symbolic_constants_posix.cc +++ b/util/posix/symbolic_constants_posix.cc @@ -26,7 +26,7 @@ namespace { -const char* kSignalNames[] = { +constexpr const char* kSignalNames[] = { nullptr, #if defined(OS_MACOSX) @@ -108,7 +108,7 @@ static_assert(arraysize(kSignalNames) == 32, "kSignalNames length"); static_assert(arraysize(kSignalNames) == NSIG, "kSignalNames length"); #endif -const char kSigPrefix[] = "SIG"; +constexpr char kSigPrefix[] = "SIG"; } // namespace diff --git a/util/posix/symbolic_constants_posix_test.cc b/util/posix/symbolic_constants_posix_test.cc index 9cb29b58..8478b7bc 100644 --- a/util/posix/symbolic_constants_posix_test.cc +++ b/util/posix/symbolic_constants_posix_test.cc @@ -29,7 +29,7 @@ namespace crashpad { namespace test { namespace { -const struct { +constexpr struct { int signal; const char* full_name; const char* short_name; @@ -122,9 +122,9 @@ TEST(SymbolicConstantsPOSIX, SignalToString) { #if defined(OS_LINUX) || defined(OS_ANDROID) // NSIG is 64 to account for real-time signals. - const int kSignalCount = 32; + constexpr int kSignalCount = 32; #else - const int kSignalCount = NSIG; + constexpr int kSignalCount = NSIG; #endif for (int signal = 0; signal < kSignalCount + 8; ++signal) { @@ -157,7 +157,7 @@ void TestStringToSignal(const base::StringPiece& string, } TEST(SymbolicConstantsPOSIX, StringToSignal) { - const StringToSymbolicConstantOptions kOptions[] = { + static constexpr StringToSymbolicConstantOptions kOptions[] = { 0, kAllowFullName, kAllowShortName, @@ -198,7 +198,7 @@ TEST(SymbolicConstantsPOSIX, StringToSignal) { } } - const char* const kNegativeTestData[] = { + static constexpr const char* kNegativeTestData[] = { "SIGHUP ", " SIGINT", "QUIT ", @@ -216,7 +216,7 @@ TEST(SymbolicConstantsPOSIX, StringToSignal) { TestStringToSignal(kNegativeTestData[index], options, false, 0); } - const struct { + static constexpr struct { const char* string; size_t length; } kNULTestData[] = { diff --git a/util/stdlib/aligned_allocator_test.cc b/util/stdlib/aligned_allocator_test.cc index 4a018f58..d52dcfa2 100644 --- a/util/stdlib/aligned_allocator_test.cc +++ b/util/stdlib/aligned_allocator_test.cc @@ -52,10 +52,10 @@ TEST(AlignedAllocator, AlignedVector) { IsAligned(&natural_aligned_vector[2], alignof(NaturalAlignedStruct))); // Test a structure that declares its own alignment. - struct alignas(32) AlignedStruct { + struct alignas(16) AlignedStruct { int i; }; - ASSERT_EQ(alignof(AlignedStruct), 32u); + ASSERT_EQ(alignof(AlignedStruct), 16u); AlignedVector aligned_vector; aligned_vector.push_back(AlignedStruct()); @@ -69,15 +69,15 @@ TEST(AlignedAllocator, AlignedVector) { // Try a custom alignment. Since the structure itself doesn’t specify an // alignment constraint, only the base address will be aligned to the // requested boundary. - AlignedVector custom_aligned_vector; + AlignedVector custom_aligned_vector; custom_aligned_vector.push_back(NaturalAlignedStruct()); - EXPECT_TRUE(IsAligned(&custom_aligned_vector[0], 64)); + EXPECT_TRUE(IsAligned(&custom_aligned_vector[0], 32)); // Try a structure with a pretty big alignment request. - struct alignas(1024) BigAlignedStruct { + struct alignas(64) BigAlignedStruct { int i; }; - ASSERT_EQ(alignof(BigAlignedStruct), 1024u); + ASSERT_EQ(alignof(BigAlignedStruct), 64u); AlignedVector big_aligned_vector; big_aligned_vector.push_back(BigAlignedStruct()); diff --git a/util/stdlib/string_number_conversion_test.cc b/util/stdlib/string_number_conversion_test.cc index f6634d0f..dd17c00d 100644 --- a/util/stdlib/string_number_conversion_test.cc +++ b/util/stdlib/string_number_conversion_test.cc @@ -26,7 +26,7 @@ namespace test { namespace { TEST(StringNumberConversion, StringToInt) { - const struct { + static constexpr struct { const char* string; bool valid; int value; @@ -113,7 +113,7 @@ TEST(StringNumberConversion, StringToInt) { // Ensure that embedded NUL characters are treated as bad input. The string // is split to avoid MSVC warning: // "decimal digit terminates octal escape sequence". - const char input[] = "6\000" "6"; + static constexpr char input[] = "6\000" "6"; base::StringPiece input_string(input, arraysize(input) - 1); int output; EXPECT_FALSE(StringToNumber(input_string, &output)); @@ -124,7 +124,7 @@ TEST(StringNumberConversion, StringToInt) { } TEST(StringNumberConversion, StringToUnsignedInt) { - const struct { + static constexpr struct { const char* string; bool valid; unsigned int value; @@ -211,7 +211,7 @@ TEST(StringNumberConversion, StringToUnsignedInt) { // Ensure that embedded NUL characters are treated as bad input. The string // is split to avoid MSVC warning: // "decimal digit terminates octal escape sequence". - const char input[] = "6\000" "6"; + static constexpr char input[] = "6\000" "6"; base::StringPiece input_string(input, arraysize(input) - 1); unsigned int output; EXPECT_FALSE(StringToNumber(input_string, &output)); @@ -222,7 +222,7 @@ TEST(StringNumberConversion, StringToUnsignedInt) { } TEST(StringNumberConversion, StringToInt64) { - const struct { + static constexpr struct { const char* string; bool valid; int64_t value; @@ -271,7 +271,7 @@ TEST(StringNumberConversion, StringToInt64) { } TEST(StringNumberConversion, StringToUnsignedInt64) { - const struct { + static constexpr struct { const char* string; bool valid; uint64_t value; diff --git a/util/stdlib/strlcpy_test.cc b/util/stdlib/strlcpy_test.cc index 57315c3b..5185840f 100644 --- a/util/stdlib/strlcpy_test.cc +++ b/util/stdlib/strlcpy_test.cc @@ -41,7 +41,8 @@ TEST(strlcpy, c16lcpy) { // Test with M, é, Ā, ő, and Ḙ. This is a mix of characters that have zero and // nonzero low and high bytes. - const base::char16 test_characters[] = {0x4d, 0xe9, 0x100, 0x151, 0x1e18}; + static constexpr base::char16 test_characters[] = + {0x4d, 0xe9, 0x100, 0x151, 0x1e18}; for (size_t index = 0; index < arraysize(test_characters); ++index) { base::char16 test_character = test_characters[index]; diff --git a/util/stdlib/strnlen_test.cc b/util/stdlib/strnlen_test.cc index e030fdc8..1913e208 100644 --- a/util/stdlib/strnlen_test.cc +++ b/util/stdlib/strnlen_test.cc @@ -23,7 +23,7 @@ namespace test { namespace { TEST(strnlen, strnlen) { - const char kTestBuffer[] = "abc\0d"; + static constexpr char kTestBuffer[] = "abc\0d"; ASSERT_EQ(strlen(kTestBuffer), 3u); EXPECT_EQ(crashpad::strnlen(kTestBuffer, 0), 0u); EXPECT_EQ(crashpad::strnlen(kTestBuffer, 1), 1u); @@ -33,7 +33,7 @@ TEST(strnlen, strnlen) { EXPECT_EQ(crashpad::strnlen(kTestBuffer, 5), 3u); EXPECT_EQ(crashpad::strnlen(kTestBuffer, 6), 3u); - const char kEmptyBuffer[] = "\0"; + static constexpr char kEmptyBuffer[] = "\0"; ASSERT_EQ(strlen(kEmptyBuffer), 0u); EXPECT_EQ(crashpad::strnlen(kEmptyBuffer, 0), 0u); EXPECT_EQ(crashpad::strnlen(kEmptyBuffer, 1), 0u); diff --git a/util/synchronization/semaphore_posix.cc b/util/synchronization/semaphore_posix.cc index c781e4c6..f6e8bea1 100644 --- a/util/synchronization/semaphore_posix.cc +++ b/util/synchronization/semaphore_posix.cc @@ -15,8 +15,8 @@ #include "util/synchronization/semaphore.h" #include - -#include +#include +#include #include "base/logging.h" #include "base/posix/eintr_wrapper.h" @@ -25,6 +25,19 @@ namespace crashpad { #if !defined(OS_MACOSX) +namespace { + +void AddTimespec(const timespec& ts1, const timespec& ts2, timespec* result) { + result->tv_sec = ts1.tv_sec + ts2.tv_sec; + result->tv_nsec = ts1.tv_nsec + ts2.tv_nsec; + if (result->tv_nsec > static_cast(1E9)) { + ++result->tv_sec; + result->tv_nsec -= static_cast(1E9); + } +} + +} // namespace + Semaphore::Semaphore(int value) { PCHECK(sem_init(&semaphore_, 0, value) == 0) << "sem_init"; } @@ -40,14 +53,20 @@ void Semaphore::Wait() { bool Semaphore::TimedWait(double seconds) { DCHECK_GE(seconds, 0.0); - if (std::isinf(seconds)) { + if (isinf(seconds)) { Wait(); return true; } + timespec current_time; + if (clock_gettime(CLOCK_REALTIME, ¤t_time) != 0) { + PLOG(ERROR) << "clock_gettime"; + return false; + } timespec timeout; timeout.tv_sec = seconds; timeout.tv_nsec = (seconds - trunc(seconds)) * 1E9; + AddTimespec(current_time, timeout, &timeout); int rv = HANDLE_EINTR(sem_timedwait(&semaphore_, &timeout)); PCHECK(rv == 0 || errno == ETIMEDOUT) << "sem_timedwait"; diff --git a/util/synchronization/semaphore_test.cc b/util/synchronization/semaphore_test.cc index ccaf742d..e1f3648c 100644 --- a/util/synchronization/semaphore_test.cc +++ b/util/synchronization/semaphore_test.cc @@ -16,6 +16,7 @@ #include +#include "base/macros.h" #include "gtest/gtest.h" #if defined(OS_POSIX) @@ -120,10 +121,9 @@ TEST(Semaphore, TenThreaded) { // resources (10), and the threads each try to obtain the resource a different // number of times. Semaphore semaphore(5); - const size_t kThreads = 10; - ThreadMainInfo info[kThreads]; + ThreadMainInfo info[10]; size_t iterations = 0; - for (size_t index = 0; index < kThreads; ++index) { + for (size_t index = 0; index < arraysize(info); ++index) { info[index].semaphore = &semaphore; info[index].iterations = index; iterations += info[index].iterations; @@ -135,7 +135,7 @@ TEST(Semaphore, TenThreaded) { semaphore.Signal(); } - for (size_t index = 0; index < kThreads; ++index) { + for (size_t index = 0; index < arraysize(info); ++index) { JoinThread(&info[index]); } } diff --git a/util/thread/thread_log_messages_test.cc b/util/thread/thread_log_messages_test.cc index 65c0b854..00b612ce 100644 --- a/util/thread/thread_log_messages_test.cc +++ b/util/thread/thread_log_messages_test.cc @@ -44,13 +44,13 @@ std::string MessageString(const std::string& log_message) { return std::string(); } - const char kStartChar = '['; + constexpr char kStartChar = '['; if (log_message[0] != kStartChar) { EXPECT_EQ(log_message[0], kStartChar); return std::string(); } - const char kFindString[] = "] "; + static constexpr char kFindString[] = "] "; size_t pos = log_message.find(kFindString); if (pos == std::string::npos) { EXPECT_NE(pos, std::string::npos); @@ -63,7 +63,7 @@ std::string MessageString(const std::string& log_message) { return std::string(); } - const char kEndChar = '\n'; + constexpr char kEndChar = '\n'; if (message_string[message_string.size() - 1] != kEndChar) { EXPECT_NE(message_string[message_string.size() - 1], kEndChar); return std::string(); @@ -78,7 +78,7 @@ TEST(ThreadLogMessages, Basic) { ASSERT_TRUE(LOG_IS_ON(INFO)); { - const char* const kMessages[] = { + static constexpr const char* kMessages[] = { "An info message", "A warning message", "An error message", @@ -101,7 +101,7 @@ TEST(ThreadLogMessages, Basic) { } { - const char kMessage[] = "Sample error message"; + static constexpr char kMessage[] = "Sample error message"; ThreadLogMessages thread_log_messages; diff --git a/util/thread/worker_thread_test.cc b/util/thread/worker_thread_test.cc index 2070c6ad..9b11fef1 100644 --- a/util/thread/worker_thread_test.cc +++ b/util/thread/worker_thread_test.cc @@ -22,7 +22,7 @@ namespace crashpad { namespace test { namespace { -const uint64_t kNanosecondsPerSecond = static_cast(1E9); +constexpr uint64_t kNanosecondsPerSecond = static_cast(1E9); class WorkDelegate : public WorkerThread::Delegate { public: diff --git a/util/util.gyp b/util/util.gyp index 6a052273..80d5e117 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -50,12 +50,16 @@ 'linux/checked_address_range.h', 'linux/memory_map.cc', 'linux/memory_map.h', + 'linux/proc_stat_reader.cc', + 'linux/proc_stat_reader.h', 'linux/process_memory.cc', 'linux/process_memory.h', - 'linux/thread_info.cc', - 'linux/thread_info.h', + 'linux/process_memory_range.cc', + 'linux/process_memory_range.h', 'linux/scoped_ptrace_attach.cc', 'linux/scoped_ptrace_attach.h', + 'linux/thread_info.cc', + 'linux/thread_info.h', 'mac/checked_mach_address_range.h', 'mac/launchd.h', 'mac/launchd.mm', @@ -110,6 +114,8 @@ 'misc/initialization_state.h', 'misc/initialization_state_dcheck.cc', 'misc/initialization_state_dcheck.h', + 'misc/lexing.cc', + 'misc/lexing.h', 'misc/metrics.cc', 'misc/metrics.h', 'misc/paths.h', @@ -134,7 +140,6 @@ 'net/http_body.h', 'net/http_body_gzip.cc', 'net/http_body_gzip.h', - 'net/http_headers.cc', 'net/http_headers.h', 'net/http_multipart_builder.cc', 'net/http_multipart_builder.h', diff --git a/util/util_test.gyp b/util/util_test.gyp index 1b4cff95..23be3600 100644 --- a/util/util_test.gyp +++ b/util/util_test.gyp @@ -41,9 +41,11 @@ 'file/string_file_test.cc', 'linux/auxiliary_vector_test.cc', 'linux/memory_map_test.cc', + 'linux/proc_stat_reader_test.cc', + 'linux/process_memory_range_test.cc', 'linux/process_memory_test.cc', - 'linux/thread_info_test.cc', 'linux/scoped_ptrace_attach_test.cc', + 'linux/thread_info_test.cc', 'mac/launchd_test.mm', 'mac/mac_util_test.mm', 'mac/service_management_test.mm', diff --git a/util/win/capture_context_test.cc b/util/win/capture_context_test.cc index 59bef2ff..4bb6f4f3 100644 --- a/util/win/capture_context_test.cc +++ b/util/win/capture_context_test.cc @@ -34,13 +34,13 @@ namespace { // gtest assertions. void SanityCheckContext(const CONTEXT& context) { #if defined(ARCH_CPU_X86) - const uint32_t must_have = CONTEXT_i386 | - CONTEXT_CONTROL | - CONTEXT_INTEGER | - CONTEXT_SEGMENTS | - CONTEXT_FLOATING_POINT; + constexpr uint32_t must_have = CONTEXT_i386 | + CONTEXT_CONTROL | + CONTEXT_INTEGER | + CONTEXT_SEGMENTS | + CONTEXT_FLOATING_POINT; ASSERT_EQ(context.ContextFlags & must_have, must_have); - const uint32_t may_have = CONTEXT_EXTENDED_REGISTERS; + constexpr uint32_t may_have = CONTEXT_EXTENDED_REGISTERS; ASSERT_EQ(context.ContextFlags & ~(must_have | may_have), 0); #elif defined(ARCH_CPU_X86_64) ASSERT_EQ(context.ContextFlags, diff --git a/util/win/command_line_test.cc b/util/win/command_line_test.cc index 95849b63..1eb15b88 100644 --- a/util/win/command_line_test.cc +++ b/util/win/command_line_test.cc @@ -60,10 +60,10 @@ TEST(CommandLine, AppendCommandLineArgument) { { SCOPED_TRACE("simple"); - const wchar_t* const kArguments[] = { - L"child.exe", - L"argument 1", - L"argument 2", + static constexpr wchar_t* const kArguments[] = { + L"child.exe", + L"argument 1", + L"argument 2", }; AppendCommandLineArgumentTest(arraysize(kArguments), kArguments); } @@ -71,11 +71,11 @@ TEST(CommandLine, AppendCommandLineArgument) { { SCOPED_TRACE("path with spaces"); - const wchar_t* const kArguments[] = { - L"child.exe", - L"argument1", - L"argument 2", - L"\\some\\path with\\spaces", + static constexpr wchar_t* const kArguments[] = { + L"child.exe", + L"argument1", + L"argument 2", + L"\\some\\path with\\spaces", }; AppendCommandLineArgumentTest(arraysize(kArguments), kArguments); } @@ -83,11 +83,11 @@ TEST(CommandLine, AppendCommandLineArgument) { { SCOPED_TRACE("argument with embedded quotation marks"); - const wchar_t* const kArguments[] = { - L"child.exe", - L"argument1", - L"she said, \"you had me at hello\"", - L"\\some\\path with\\spaces", + static constexpr wchar_t* const kArguments[] = { + L"child.exe", + L"argument1", + L"she said, \"you had me at hello\"", + L"\\some\\path with\\spaces", }; AppendCommandLineArgumentTest(arraysize(kArguments), kArguments); } @@ -95,12 +95,12 @@ TEST(CommandLine, AppendCommandLineArgument) { { SCOPED_TRACE("argument with unbalanced quotation marks"); - const wchar_t* const kArguments[] = { - L"child.exe", - L"argument1", - L"argument\"2", - L"argument3", - L"argument4", + static constexpr wchar_t* const kArguments[] = { + L"child.exe", + L"argument1", + L"argument\"2", + L"argument3", + L"argument4", }; AppendCommandLineArgumentTest(arraysize(kArguments), kArguments); } @@ -108,10 +108,10 @@ TEST(CommandLine, AppendCommandLineArgument) { { SCOPED_TRACE("argument ending with backslash"); - const wchar_t* const kArguments[] = { - L"child.exe", - L"\\some\\directory with\\spaces\\", - L"argument2", + static constexpr wchar_t* const kArguments[] = { + L"child.exe", + L"\\some\\directory with\\spaces\\", + L"argument2", }; AppendCommandLineArgumentTest(arraysize(kArguments), kArguments); } @@ -119,10 +119,10 @@ TEST(CommandLine, AppendCommandLineArgument) { { SCOPED_TRACE("empty argument"); - const wchar_t* const kArguments[] = { - L"child.exe", - L"", - L"argument2", + static constexpr wchar_t* const kArguments[] = { + L"child.exe", + L"", + L"argument2", }; AppendCommandLineArgumentTest(arraysize(kArguments), kArguments); } @@ -130,34 +130,34 @@ TEST(CommandLine, AppendCommandLineArgument) { { SCOPED_TRACE("funny nonprintable characters"); - const wchar_t* const kArguments[] = { - L"child.exe", - L"argument 1", - L"argument\t2", - L"argument\n3", - L"argument\v4", - L"argument\"5", - L" ", - L"\t", - L"\n", - L"\v", - L"\"", - L" x", - L"\tx", - L"\nx", - L"\vx", - L"\"x", - L"x ", - L"x\t", - L"x\n", - L"x\v", - L"x\"", - L" ", - L"\t\t", - L"\n\n", - L"\v\v", - L"\"\"", - L" \t\n\v\"", + static constexpr wchar_t* const kArguments[] = { + L"child.exe", + L"argument 1", + L"argument\t2", + L"argument\n3", + L"argument\v4", + L"argument\"5", + L" ", + L"\t", + L"\n", + L"\v", + L"\"", + L" x", + L"\tx", + L"\nx", + L"\vx", + L"\"x", + L"x ", + L"x\t", + L"x\n", + L"x\v", + L"x\"", + L" ", + L"\t\t", + L"\n\n", + L"\v\v", + L"\"\"", + L" \t\n\v\"", }; AppendCommandLineArgumentTest(arraysize(kArguments), kArguments); } diff --git a/util/win/process_info.cc b/util/win/process_info.cc index d3871c2d..9db70282 100644 --- a/util/win/process_info.cc +++ b/util/win/process_info.cc @@ -329,7 +329,7 @@ bool ReadProcessData(HANDLE process, bool ReadMemoryInfo(HANDLE process, bool is_64_bit, ProcessInfo* process_info) { DCHECK(process_info->memory_info_.empty()); - const WinVMAddress min_address = 0; + constexpr WinVMAddress min_address = 0; // We can't use GetSystemInfo() to get the address space range for another // process. VirtualQueryEx() will fail with ERROR_INVALID_PARAMETER if the // address is above the highest memory address accessible to the process, so diff --git a/util/win/process_info_test.cc b/util/win/process_info_test.cc index 50f932c8..07c40bb0 100644 --- a/util/win/process_info_test.cc +++ b/util/win/process_info_test.cc @@ -42,7 +42,7 @@ namespace crashpad { namespace test { namespace { -const wchar_t kNtdllName[] = L"\\ntdll.dll"; +constexpr wchar_t kNtdllName[] = L"\\ntdll.dll"; bool IsProcessWow64(HANDLE process_handle) { static const auto is_wow64_process = @@ -101,7 +101,7 @@ TEST(ProcessInfo, Self) { std::vector modules; EXPECT_TRUE(process_info.Modules(&modules)); ASSERT_GE(modules.size(), 2u); - const wchar_t kSelfName[] = L"\\crashpad_util_test.exe"; + static constexpr wchar_t kSelfName[] = L"\\crashpad_util_test.exe"; ASSERT_GE(modules[0].name.size(), wcslen(kSelfName)); EXPECT_EQ(modules[0].name.substr(modules[0].name.size() - wcslen(kSelfName)), kSelfName); @@ -178,7 +178,7 @@ void TestOtherProcess(const base::string16& directory_modification) { kNtdllName); // lz32.dll is an uncommonly-used-but-always-available module that the test // binary manually loads. - const wchar_t kLz32dllName[] = L"\\lz32.dll"; + static constexpr wchar_t kLz32dllName[] = L"\\lz32.dll"; ASSERT_GE(modules.back().name.size(), wcslen(kLz32dllName)); EXPECT_EQ(modules.back().name.substr(modules.back().name.size() - wcslen(kLz32dllName)), @@ -601,7 +601,7 @@ TEST(ProcessInfo, Handles) { // OBJ_INHERIT from ntdef.h, but including that conflicts with other // headers. - const int kObjInherit = 0x2; + constexpr int kObjInherit = 0x2; EXPECT_EQ(handle.attributes, kObjInherit); } if (handle.handle == HandleToInt(scoped_key.get())) { @@ -633,7 +633,7 @@ TEST(ProcessInfo, Handles) { } TEST(ProcessInfo, OutOfRangeCheck) { - const size_t kAllocationSize = 12345; + constexpr size_t kAllocationSize = 12345; std::unique_ptr safe_memory(new char[kAllocationSize]); ProcessInfo info; diff --git a/util/win/registration_protocol_win.cc b/util/win/registration_protocol_win.cc index 87c45748..dc31e7d4 100644 --- a/util/win/registration_protocol_win.cc +++ b/util/win/registration_protocol_win.cc @@ -136,7 +136,7 @@ const void* GetSecurityDescriptorForNamedPipeInstance(size_t* size) { // would in turn cause deadlock. #pragma pack(push, 1) - static const struct SecurityDescriptorBlob { + static constexpr struct SecurityDescriptorBlob { // See https://msdn.microsoft.com/en-us/library/cc230366.aspx. SECURITY_DESCRIPTOR_RELATIVE sd_rel; struct { diff --git a/util/win/registration_protocol_win_test.cc b/util/win/registration_protocol_win_test.cc index 10a5bb63..6601b4e7 100644 --- a/util/win/registration_protocol_win_test.cc +++ b/util/win/registration_protocol_win_test.cc @@ -33,7 +33,7 @@ TEST(SecurityDescriptor, MatchesAdvapi32) { // Mandatory Label, no ACE flags, no ObjectType, integrity level // untrusted. - const wchar_t kSddl[] = L"S:(ML;;;;;S-1-16-0)"; + static constexpr wchar_t kSddl[] = L"S:(ML;;;;;S-1-16-0)"; PSECURITY_DESCRIPTOR sec_desc; ULONG sec_desc_len; ASSERT_TRUE(ConvertStringSecurityDescriptorToSecurityDescriptor( diff --git a/util/win/safe_terminate_process_test.cc b/util/win/safe_terminate_process_test.cc index 2fbc8483..d176002e 100644 --- a/util/win/safe_terminate_process_test.cc +++ b/util/win/safe_terminate_process_test.cc @@ -134,7 +134,7 @@ TEST(SafeTerminateProcess, PatchBadly) { // https://crashpad.chromium.org/bug/179. In reality, this only affects // 32-bit x86, as there’s no calling convention confusion on x86_64. It // doesn’t hurt to run this test in the 64-bit environment, though. - const uint8_t patch[] = { + static constexpr uint8_t patch[] = { #if defined(ARCH_CPU_X86) 0x31, 0xc0, // xor eax, eax #elif defined(ARCH_CPU_X86_64) diff --git a/util/win/time.cc b/util/win/time.cc index 4c6fba19..f3c2360a 100644 --- a/util/win/time.cc +++ b/util/win/time.cc @@ -22,7 +22,7 @@ namespace crashpad { namespace { -const uint64_t kMicrosecondsPerSecond = static_cast(1E6); +constexpr uint64_t kMicrosecondsPerSecond = static_cast(1E6); uint64_t FiletimeToMicroseconds(const FILETIME& filetime) { uint64_t t = (static_cast(filetime.dwHighDateTime) << 32) | @@ -46,7 +46,7 @@ timeval FiletimeToTimevalEpoch(const FILETIME& filetime) { // 1601 to 1970 is 369 years + 89 leap days = 134774 days * 86400 seconds per // day. It's not entirely clear, but it appears that these are solar seconds, // not SI seconds, so there are no leap seconds to be considered. - const uint64_t kNumSecondsFrom1601To1970 = (369 * 365 + 89) * 86400ULL; + constexpr uint64_t kNumSecondsFrom1601To1970 = (369 * 365 + 89) * 86400ULL; DCHECK_GE(microseconds, kNumSecondsFrom1601To1970 * kMicrosecondsPerSecond); microseconds -= kNumSecondsFrom1601To1970 * kMicrosecondsPerSecond; return MicrosecondsToTimeval(microseconds);