mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-20 10:43:46 +00:00
Merge master c332e7ffdab6 into doc
This commit is contained in:
commit
926971a33b
2
DEPS
2
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',
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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<WinVMAddress>(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,
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
namespace {
|
||||
|
||||
static const uint32_t kCrashpadInfoVersion = 1;
|
||||
constexpr uint32_t kCrashpadInfoVersion = 1;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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,
|
||||
|
38
compat/android/elf.h
Normal file
38
compat/android/elf.h
Normal file
@ -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 <elf.h>
|
||||
|
||||
#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_
|
30
compat/android/sched.h
Normal file
30
compat/android/sched.h
Normal file
@ -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 <sched.h>
|
||||
|
||||
// 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_
|
@ -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 <sys/syscall.h>
|
||||
|
||||
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_
|
@ -94,6 +94,16 @@
|
||||
],
|
||||
},
|
||||
}],
|
||||
['OS=="linux"', {
|
||||
'include_dirs': [
|
||||
'linux',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'linux',
|
||||
],
|
||||
},
|
||||
}],
|
||||
],
|
||||
},
|
||||
],
|
||||
|
27
compat/linux/sys/ptrace.h
Normal file
27
compat/linux/sys/ptrace.h
Normal file
@ -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 <sys/ptrace.h>
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#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_
|
@ -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
|
||||
```
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class TestUserStreamDataSource : public crashpad::UserStreamDataSource {
|
||||
std::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource>
|
||||
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)));
|
||||
|
@ -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();
|
||||
|
@ -14,7 +14,9 @@
|
||||
|
||||
#include "handler/mac/file_limit_annotation.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
//!
|
||||
|
@ -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<MinidumpStreamType>(0x4d);
|
||||
const uint8_t kStreamValue = 0x5a;
|
||||
constexpr size_t kStreamSize = 5;
|
||||
constexpr MinidumpStreamType kStreamType =
|
||||
static_cast<MinidumpStreamType>(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<MinidumpStreamType>(0x4d);
|
||||
static constexpr uint8_t kStreamData[] = "Hello World!";
|
||||
constexpr size_t kStreamSize = arraysize(kStreamData);
|
||||
constexpr MinidumpStreamType kStreamType =
|
||||
static_cast<MinidumpStreamType>(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<MinidumpStreamType>(0x4d);
|
||||
constexpr MinidumpStreamType kStreamType =
|
||||
static_cast<MinidumpStreamType>(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<MinidumpStreamType>(0x6d);
|
||||
const uint8_t kStream0Value = 0x5a;
|
||||
constexpr size_t kStream0Size = 5;
|
||||
constexpr MinidumpStreamType kStream0Type =
|
||||
static_cast<MinidumpStreamType>(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<MinidumpStreamType>(0x4d);
|
||||
const uint8_t kStream1Value = 0xa5;
|
||||
constexpr size_t kStream1Size = 3;
|
||||
constexpr MinidumpStreamType kStream1Type =
|
||||
static_cast<MinidumpStreamType>(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<MinidumpStreamType>(0x7e);
|
||||
const uint8_t kStream2Value = 0x36;
|
||||
constexpr size_t kStream2Size = 1;
|
||||
constexpr MinidumpStreamType kStream2Type =
|
||||
static_cast<MinidumpStreamType>(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<MinidumpStreamType>(0x4d);
|
||||
constexpr size_t kStreamSize = 0;
|
||||
constexpr MinidumpStreamType kStreamType =
|
||||
static_cast<MinidumpStreamType>(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<time_t>(kSnapshotTime), 0 };
|
||||
constexpr uint32_t kSnapshotTime = 0x4976043c;
|
||||
constexpr timeval kSnapshotTimeval = {static_cast<time_t>(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<time_t>(kSnapshotTime), 0 };
|
||||
constexpr timeval kSnapshotTimeval = {static_cast<time_t>(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<time_t>(kSnapshotTime), 0 };
|
||||
constexpr uint32_t kSnapshotTime = 0x15393bd3;
|
||||
constexpr timeval kSnapshotTimeval = {static_cast<time_t>(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<MinidumpStreamType>(0x4d);
|
||||
const uint8_t kStream0Value = 0x5a;
|
||||
constexpr size_t kStream0Size = 3;
|
||||
constexpr MinidumpStreamType kStreamType =
|
||||
static_cast<MinidumpStreamType>(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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -35,7 +35,7 @@ namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
const MinidumpStreamType kBogusStreamType =
|
||||
constexpr MinidumpStreamType kBogusStreamType =
|
||||
static_cast<MinidumpStreamType>(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));
|
||||
|
@ -42,7 +42,7 @@ namespace {
|
||||
uint32_t TimevalToRoundedSeconds(const timeval& tv) {
|
||||
uint32_t seconds =
|
||||
InRangeCast<uint32_t>(tv.tv_sec, std::numeric_limits<uint32_t>::max());
|
||||
const int kMicrosecondsPerSecond = static_cast<int>(1E6);
|
||||
constexpr int kMicrosecondsPerSecond = static_cast<int>(1E6);
|
||||
if (tv.tv_usec >= kMicrosecondsPerSecond / 2 &&
|
||||
seconds != std::numeric_limits<uint32_t>::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<const uint32_t>(1E6);
|
||||
constexpr uint32_t kHzPerMHz = static_cast<const uint32_t>(1E6);
|
||||
SetProcessorPowerInfo(
|
||||
InRangeCast<uint32_t>(current_hz / kHzPerMHz,
|
||||
std::numeric_limits<uint32_t>::max()),
|
||||
|
@ -43,11 +43,11 @@ namespace {
|
||||
|
||||
template <typename T>
|
||||
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<uint64_t>(1E6);
|
||||
constexpr uint64_t kHzPerMHz = static_cast<uint64_t>(1E6);
|
||||
system_snapshot->SetCPUFrequency(
|
||||
expect_misc_info.ProcessorCurrentMhz * kHzPerMHz,
|
||||
expect_misc_info.ProcessorMaxMhz * kHzPerMHz);
|
||||
|
@ -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<std::string> 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<const ModuleSnapshot*> module_snapshots;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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]);
|
||||
|
@ -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[] = {
|
||||
|
@ -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<uint8_t>(std::min(kCPUFamily, static_cast<uint16_t>(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);
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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<uint8_t> data(kStreamSize, 'c');
|
||||
auto data_source = base::WrapUnique(new test::BufferExtensionStreamDataSource(
|
||||
kTestStreamId, &data[0], data.size()));
|
||||
|
@ -16,15 +16,13 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#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<size_t>::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_) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <dbghelp.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#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<size_t>::max();
|
||||
|
||||
MinidumpWritable();
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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 =
|
||||
|
108
snapshot/linux/cpu_context_linux.cc
Normal file
108
snapshot/linux/cpu_context_linux.cc
Normal file
@ -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 <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#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
|
53
snapshot/linux/cpu_context_linux.h
Normal file
53
snapshot/linux/cpu_context_linux.h
Normal file
@ -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_
|
143
snapshot/linux/debug_rendezvous.cc
Normal file
143
snapshot/linux/debug_rendezvous.cc
Normal file
@ -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 <stdint.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
#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 <typename Traits>
|
||||
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 <typename Traits>
|
||||
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 <typename Traits>
|
||||
bool ReadLinkEntry(const ProcessMemoryRange& memory,
|
||||
LinuxVMAddress* address,
|
||||
DebugRendezvous::LinkEntry* entry_out) {
|
||||
LinkEntrySpecific<Traits> 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<Traits64>(memory, address)
|
||||
: InitializeSpecific<Traits32>(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::LinkEntry>& DebugRendezvous::Modules()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return modules_;
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
bool DebugRendezvous::InitializeSpecific(const ProcessMemoryRange& memory,
|
||||
LinuxVMAddress address) {
|
||||
DebugRendezvousSpecific<Traits> 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<Traits>(memory, &link_entry_address, &executable_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::set<LinuxVMAddress> 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<Traits>(memory, &link_entry_address, &entry)) {
|
||||
return false;
|
||||
}
|
||||
modules_.push_back(entry);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
88
snapshot/linux/debug_rendezvous.h
Normal file
88
snapshot/linux/debug_rendezvous.h
Normal file
@ -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 <string>
|
||||
#include <vector>
|
||||
|
||||
#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 `<link.h>` 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<LinkEntry>& Modules() const;
|
||||
|
||||
private:
|
||||
template <typename Traits>
|
||||
bool InitializeSpecific(const ProcessMemoryRange& memory,
|
||||
LinuxVMAddress address);
|
||||
|
||||
std::vector<LinkEntry> modules_;
|
||||
LinkEntry executable_;
|
||||
InitializationStateDcheck initialized_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DebugRendezvous);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_SNAPSHOT_LINUX_DEBUG_RENDEZVOUS_H_
|
220
snapshot/linux/debug_rendezvous_test.cc
Normal file
220
snapshot/linux/debug_rendezvous_test.cc
Normal file
@ -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 <linux/auxvec.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#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 <sys/system_properties.h>
|
||||
#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<int>::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
|
75
snapshot/linux/elf_dynamic_array_reader.cc
Normal file
75
snapshot/linux/elf_dynamic_array_reader.cc
Normal file
@ -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 <elf.h>
|
||||
|
||||
#include "util/stdlib/map_insert.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename DynType>
|
||||
bool Read(const ProcessMemoryRange& memory,
|
||||
LinuxVMAddress address,
|
||||
LinuxVMSize size,
|
||||
std::map<uint64_t, uint64_t>* values) {
|
||||
std::map<uint64_t, uint64_t> 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<Elf64_Dyn>(memory, address, size, &values_)
|
||||
: Read<Elf32_Dyn>(memory, address, size, &values_);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
73
snapshot/linux/elf_dynamic_array_reader.h
Normal file
73
snapshot/linux/elf_dynamic_array_reader.h
Normal file
@ -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 <stdint.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#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 `<elf.h>`.
|
||||
//! \param[out] value The value, casted to an appropriate type, if found.
|
||||
//! \return `true` if the value is found.
|
||||
template <typename V>
|
||||
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<uint64_t, uint64_t> values_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ElfDynamicArrayReader);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_SNAPSHOT_LINUX_ELF_DYNAMIC_ARRAY_READER_H_
|
471
snapshot/linux/elf_image_reader.cc
Normal file
471
snapshot/linux/elf_image_reader.cc
Normal file
@ -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 <stddef.h>
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#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 <typename PhdrType>
|
||||
class ElfImageReader::ProgramHeaderTableSpecific
|
||||
: public ElfImageReader::ProgramHeaderTable {
|
||||
public:
|
||||
ProgramHeaderTableSpecific<PhdrType>() {}
|
||||
~ProgramHeaderTableSpecific<PhdrType>() {}
|
||||
|
||||
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<PhdrType, Elf64_Phdr>::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<PhdrType> table_;
|
||||
InitializationStateDcheck initialized_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ProgramHeaderTableSpecific<PhdrType>);
|
||||
};
|
||||
|
||||
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<PhdrType>(); \
|
||||
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
|
133
snapshot/linux/elf_image_reader.h
Normal file
133
snapshot/linux/elf_image_reader.h
Normal file
@ -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 <elf.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#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 `<elf.h>`.
|
||||
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
|
||||
//! `<link.h>`.
|
||||
//!
|
||||
//! \param[out] debug the debug address, if found.
|
||||
//! \return `true` if the debug address was found.
|
||||
bool GetDebugAddress(LinuxVMAddress* debug);
|
||||
|
||||
private:
|
||||
class ProgramHeaderTable;
|
||||
template <typename PhdrType>
|
||||
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<ProgramHeaderTable> program_headers_;
|
||||
std::unique_ptr<ElfDynamicArrayReader> dynamic_array_;
|
||||
std::unique_ptr<ElfSymbolTableReader> 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_
|
157
snapshot/linux/elf_image_reader_test.cc
Normal file
157
snapshot/linux/elf_image_reader_test.cc
Normal file
@ -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 <dlfcn.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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<LinuxVMAddress>(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<void*>(getpid), &info)) << "dladdr:"
|
||||
<< dlerror();
|
||||
LinuxVMAddress elf_address = FromPointerCast<LinuxVMAddress>(info.dli_fbase);
|
||||
|
||||
ExpectElfImageWithSymbol(pid,
|
||||
elf_address,
|
||||
am_64_bit,
|
||||
"getpid",
|
||||
FromPointerCast<LinuxVMAddress>(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
|
87
snapshot/linux/elf_symbol_table_reader.cc
Normal file
87
snapshot/linux/elf_symbol_table_reader.cc
Normal file
@ -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 <elf.h>
|
||||
|
||||
#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<Elf64_Sym>(name, info)
|
||||
: ScanSymbolTable<Elf32_Sym>(name, info);
|
||||
}
|
||||
|
||||
template <typename SymEnt>
|
||||
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
|
87
snapshot/linux/elf_symbol_table_reader.h
Normal file
87
snapshot/linux/elf_symbol_table_reader.h
Normal file
@ -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 <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#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 <typename SymEnt>
|
||||
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_
|
68
snapshot/linux/memory_snapshot_linux.cc
Normal file
68
snapshot/linux/memory_snapshot_linux.cc
Normal file
@ -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 <memory>
|
||||
|
||||
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<uint8_t[]> 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
|
69
snapshot/linux/memory_snapshot_linux.h
Normal file
69
snapshot/linux/memory_snapshot_linux.h
Normal file
@ -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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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_
|
322
snapshot/linux/process_reader.cc
Normal file
322
snapshot/linux/process_reader.cc
Normal file
@ -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 <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#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::Thread>& 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
|
127
snapshot/linux/process_reader.h
Normal file
127
snapshot/linux/process_reader.h
Normal file
@ -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 <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#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<Thread>& Threads();
|
||||
|
||||
private:
|
||||
void InitializeThreads();
|
||||
|
||||
ProcessInfo process_info_;
|
||||
class MemoryMap memory_map_;
|
||||
std::vector<Thread> threads_;
|
||||
std::unique_ptr<ProcessMemory> 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_
|
450
snapshot/linux/process_reader_test.cc
Normal file
450
snapshot/linux/process_reader_test.cc
Normal file
@ -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 <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#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<void* (*)()>(0xffff0fe0);
|
||||
tls = FromPointerCast<LinuxVMAddress>(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<LinuxVMAddress>(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<LinuxVMAddress>(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<char*>(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<char[], base::FreeDeleter> stack;
|
||||
Semaphore ready_semaphore;
|
||||
Semaphore exit_semaphore;
|
||||
pid_t tid;
|
||||
};
|
||||
|
||||
static void* ThreadMain(void* argument) {
|
||||
Thread* thread = static_cast<Thread*>(argument);
|
||||
|
||||
CHECK_EQ(setpriority(PRIO_PROCESS, 0, thread->expectation.nice_value), 0)
|
||||
<< ErrnoMessage("setpriority");
|
||||
|
||||
thread->expectation.tls = GetTLS();
|
||||
thread->expectation.stack_address =
|
||||
reinterpret_cast<LinuxVMAddress>(&thread);
|
||||
thread->tid = gettid();
|
||||
|
||||
thread->ready_semaphore.Signal();
|
||||
thread->exit_semaphore.Wait();
|
||||
|
||||
CHECK_EQ(pthread_self(), thread->pthread);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PointerVector<Thread> threads_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TestThreadPool);
|
||||
};
|
||||
|
||||
using ThreadMap = std::map<pid_t, TestThreadPool::ThreadExpectation>;
|
||||
|
||||
void ExpectThreads(const ThreadMap& thread_map,
|
||||
const std::vector<ProcessReader::Thread>& 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<ProcessReader::Thread>& 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<LinuxVMAddress>(&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<ProcessReader::Thread>& 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<LinuxVMAddress>(&stack_size));
|
||||
}
|
||||
|
||||
void GrowStack(LinuxVMSize stack_size, LinuxVMAddress bottom_of_stack) {
|
||||
char stack_contents[4096];
|
||||
auto stack_address = reinterpret_cast<LinuxVMAddress>(&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<void*>(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<void*>(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
|
18
snapshot/linux/test_exported_symbols.sym
Normal file
18
snapshot/linux/test_exported_symbols.sym
Normal file
@ -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;
|
||||
};
|
204
snapshot/linux/thread_snapshot_linux.cc
Normal file
204
snapshot/linux/thread_snapshot_linux.cc
Normal file
@ -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 <sched.h>
|
||||
|
||||
#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<const MemorySnapshot*> ThreadSnapshotLinux::ExtraMemory() const {
|
||||
return std::vector<const MemorySnapshot*>();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
82
snapshot/linux/thread_snapshot_linux.h
Normal file
82
snapshot/linux/thread_snapshot_linux.h
Normal file
@ -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 <stdint.h>
|
||||
|
||||
#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<const MemorySnapshot*> 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_
|
@ -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(
|
||||
|
@ -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) {
|
||||
|
@ -33,7 +33,7 @@
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kInvalidSegmentIndex = std::numeric_limits<uint32_t>::max();
|
||||
constexpr uint32_t kInvalidSegmentIndex = std::numeric_limits<uint32_t>::max();
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -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<mach_vm_size_t>::max();
|
||||
constexpr mach_vm_size_t kSlideUnknown =
|
||||
std::numeric_limits<mach_vm_size_t>::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
|
||||
|
@ -53,7 +53,7 @@ TEST(MachOImageSegmentReader, SegmentNameString) {
|
||||
|
||||
// Segment names defined in <mach-o/loader.h>. 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 <mach-o/loader.h>. 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)
|
||||
|
@ -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;
|
||||
|
@ -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<mach_vm_address_t>(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();
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ bool ReadIntoVersioned(ProcessReader* process_reader,
|
||||
template <typename Traits>
|
||||
size_t dyld_all_image_infos<Traits>::ExpectedSizeForVersion(
|
||||
decltype(dyld_all_image_infos<Traits>::version) version) {
|
||||
const size_t kSizeForVersion[] = {
|
||||
static constexpr size_t kSizeForVersion[] = {
|
||||
offsetof(dyld_all_image_infos<Traits>, infoArrayCount), // 0
|
||||
offsetof(dyld_all_image_infos<Traits>, libSystemInitialized), // 1
|
||||
offsetof(dyld_all_image_infos<Traits>, jitInfo), // 2
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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': {
|
||||
|
@ -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/'],
|
||||
],
|
||||
}],
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -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(),
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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<uintptr_t>(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:
|
||||
|
@ -528,7 +528,7 @@ WinVMSize ProcessSnapshotWin::DetermineSizeOfEnvironmentBlock(
|
||||
&env_block[0]);
|
||||
env_block.resize(
|
||||
static_cast<unsigned int>(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<Traits>), into);
|
||||
|
||||
const decltype(critical_section.DebugInfo) kInvalid =
|
||||
constexpr decltype(critical_section.DebugInfo) kInvalid =
|
||||
static_cast<decltype(critical_section.DebugInfo)>(-1);
|
||||
if (critical_section.DebugInfo == kInvalid)
|
||||
return;
|
||||
|
@ -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<uint64_t>(1E6);
|
||||
constexpr uint64_t kMhzToHz = static_cast<uint64_t>(1E6);
|
||||
*current_hz = std::max_element(info.begin(),
|
||||
info.end(),
|
||||
[](const PROCESSOR_POWER_INFORMATION& a,
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ base::FilePath GenerateCandidateName() {
|
||||
return system_temp_dir.Append(new_dir_name);
|
||||
}
|
||||
|
||||
const int kRetries = 50;
|
||||
constexpr int kRetries = 50;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -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;
|
||||
|
12
third_party/zlib/zlib.gyp
vendored
12
third_party/zlib/zlib.gyp
vendored
@ -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',
|
||||
|
@ -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},
|
||||
|
@ -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},
|
||||
|
@ -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},
|
||||
|
@ -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},
|
||||
|
@ -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},
|
||||
|
@ -81,7 +81,7 @@ int OnDemandServiceToolMain(int argc, char* argv[]) {
|
||||
std::vector<std::string> 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},
|
||||
|
@ -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},
|
||||
|
@ -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(
|
||||
|
@ -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.
|
||||
//!
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user