diff --git a/client/crashpad_info.cc b/client/crashpad_info.cc index d2896f4a..b545a3cd 100644 --- a/client/crashpad_info.cc +++ b/client/crashpad_info.cc @@ -51,17 +51,24 @@ static_assert(std::is_standard_layout::value, // This may result in a static module initializer in debug-mode builds, but // because it’s POD, no code should need to run to initialize this under // release-mode optimization. + +// Platforms that use ELF objects need to locate this structure via the dynamic +// symbol table, so avoid name mangling. +#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) +extern "C" { +#endif + #if defined(OS_POSIX) __attribute__(( +#if defined(OS_MACOSX) // Put the structure in a well-known section name where it can be easily // found without having to consult the symbol table. -#if defined(OS_MACOSX) section(SEG_DATA ",crashpad_info"), -#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) - section("crashpad_info"), -#else -#error Port + + // There's no need to expose this as a public symbol from the symbol table. + // All accesses from the outside can locate the well-known section name. + visibility("hidden"), #endif #if defined(ADDRESS_SANITIZER) @@ -74,11 +81,7 @@ __attribute__(( #endif // defined(ADDRESS_SANITIZER) // The “used” attribute prevents the structure from being dead-stripped. - used, - - // There’s no need to expose this as a public symbol from the symbol table. - // All accesses from the outside can locate the well-known section name. - visibility("hidden"))) + used)) #elif defined(OS_WIN) @@ -93,6 +96,10 @@ __declspec(allocate("CPADinfo")) CrashpadInfo g_crashpad_info; +#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) +} // extern "C" +#endif + // static CrashpadInfo* CrashpadInfo::GetCrashpadInfo() { return &g_crashpad_info; diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index b98f9d25..67291c7d 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -118,6 +118,8 @@ static_library("snapshot") { "linux/exception_snapshot_linux.h", "linux/memory_snapshot_linux.cc", "linux/memory_snapshot_linux.h", + "linux/module_snapshot_linux.cc", + "linux/module_snapshot_linux.h", "linux/process_reader.cc", "linux/process_reader.h", "linux/process_snapshot_linux.cc", diff --git a/snapshot/elf/elf_image_reader.h b/snapshot/elf/elf_image_reader.h index b6b59979..67cbc1fa 100644 --- a/snapshot/elf/elf_image_reader.h +++ b/snapshot/elf/elf_image_reader.h @@ -202,6 +202,11 @@ class ElfImageReader { NoteReader::NoteType type, ssize_t max_note_size); + //! \brief Return a ProcessMemoryRange restricted to the range of this image. + //! + //! The caller does not take ownership of the returned object. + const ProcessMemoryRange* Memory() const; + private: template class ProgramHeaderTableSpecific; diff --git a/snapshot/linux/module_snapshot_linux.cc b/snapshot/linux/module_snapshot_linux.cc new file mode 100644 index 00000000..0ddbebfa --- /dev/null +++ b/snapshot/linux/module_snapshot_linux.cc @@ -0,0 +1,188 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/linux/module_snapshot_linux.h" + +#include + +#include "base/files/file_path.h" +#include "snapshot/crashpad_types/image_annotation_reader.h" + +namespace crashpad { +namespace internal { + +ModuleSnapshotLinux::ModuleSnapshotLinux() + : ModuleSnapshot(), + name_(), + elf_reader_(nullptr), + crashpad_info_(), + type_(kModuleTypeUnknown), + initialized_() {} + +ModuleSnapshotLinux::~ModuleSnapshotLinux() = default; + +bool ModuleSnapshotLinux::Initialize( + const ProcessReader::Module& process_reader_module) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (!process_reader_module.elf_reader) { + LOG(ERROR) << "no elf reader"; + return false; + } + + name_ = process_reader_module.name; + elf_reader_ = process_reader_module.elf_reader; + type_ = process_reader_module.type; + + VMAddress info_address; + VMSize info_size; + if (elf_reader_->GetDynamicSymbol( + "g_crashpad_info", &info_address, &info_size)) { + ProcessMemoryRange range; + if (range.Initialize(*elf_reader_->Memory()) && + range.RestrictRange(info_address, info_size)) { + auto info = std::make_unique(); + if (info->Initialize(&range, info_address)) { + crashpad_info_ = std::move(info); + } + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ModuleSnapshotLinux::GetCrashpadOptions( + CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!crashpad_info_) { + return false; + } + + options->crashpad_handler_behavior = + crashpad_info_->CrashpadHandlerBehavior(); + options->system_crash_reporter_forwarding = + crashpad_info_->SystemCrashReporterForwarding(); + options->gather_indirectly_referenced_memory = + crashpad_info_->GatherIndirectlyReferencedMemory(); + options->indirectly_referenced_memory_cap = + crashpad_info_->IndirectlyReferencedMemoryCap(); + return true; +} + +std::string ModuleSnapshotLinux::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return name_; +} + +uint64_t ModuleSnapshotLinux::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return elf_reader_->Address(); +} + +uint64_t ModuleSnapshotLinux::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return elf_reader_->Size(); +} + +time_t ModuleSnapshotLinux::Timestamp() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return 0; +} + +void ModuleSnapshotLinux::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; +} + +void ModuleSnapshotLinux::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; +} + +ModuleSnapshot::ModuleType ModuleSnapshotLinux::GetModuleType() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return type_; +} + +void ModuleSnapshotLinux::UUIDAndAge(crashpad::UUID* uuid, + uint32_t* age) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *age = 0; + + std::unique_ptr notes = + elf_reader_->NotesWithNameAndType(ELF_NOTE_GNU, NT_GNU_BUILD_ID, 64); + std::string desc; + notes->NextNote(nullptr, nullptr, &desc); + desc.insert(desc.end(), 16 - std::min(desc.size(), size_t{16}), '\0'); + uuid->InitializeFromBytes(reinterpret_cast(&desc[0])); +} + +std::string ModuleSnapshotLinux::DebugFileName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return base::FilePath(Name()).BaseName().value(); +} + +std::vector ModuleSnapshotLinux::AnnotationsVector() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::map ModuleSnapshotLinux::AnnotationsSimpleMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::map annotations; + if (crashpad_info_ && crashpad_info_->SimpleAnnotations()) { + ImageAnnotationReader reader(elf_reader_->Memory()); + reader.SimpleMap(crashpad_info_->SimpleAnnotations(), &annotations); + } + return annotations; +} + +std::vector ModuleSnapshotLinux::AnnotationObjects() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector annotations; + if (crashpad_info_ && crashpad_info_->AnnotationsList()) { + ImageAnnotationReader reader(elf_reader_->Memory()); + reader.AnnotationsList(crashpad_info_->AnnotationsList(), &annotations); + } + return annotations; +} + +std::set> ModuleSnapshotLinux::ExtraMemoryRanges() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::set>(); +} + +std::vector +ModuleSnapshotLinux::CustomMinidumpStreams() const { + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/linux/module_snapshot_linux.h b/snapshot/linux/module_snapshot_linux.h new file mode 100644 index 00000000..b277ff7a --- /dev/null +++ b/snapshot/linux/module_snapshot_linux.h @@ -0,0 +1,96 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_MODULE_SNAPSHOT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_MODULE_SNAPSHOT_LINUX_H_ + +#include +#include + +#include +#include +#include + +#include "base/macros.h" +#include "client/crashpad_info.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/crashpad_types/crashpad_info_reader.h" +#include "snapshot/elf/elf_image_reader.h" +#include "snapshot/linux/process_reader.h" +#include "snapshot/module_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +namespace internal { + +//! \brief A ModuleSnapshot of a code module (binary image) loaded into a +//! running (or crashed) process on a Linux system. +class ModuleSnapshotLinux final : public ModuleSnapshot { + public: + ModuleSnapshotLinux(); + ~ModuleSnapshotLinux() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader_module The module within the ProcessReader for + //! which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(const ProcessReader::Module& process_reader_module); + + //! \brief Returns options from the module’s CrashpadInfo structure. + //! + //! \param[out] options Options set in the module’s CrashpadInfo structure. + //! \return `true` if there were options returned. Otherwise `false`. + bool GetCrashpadOptions(CrashpadInfoClientOptions* options); + + // ModuleSnapshot: + + std::string Name() const override; + uint64_t Address() const override; + uint64_t Size() const override; + time_t Timestamp() const override; + void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + ModuleType GetModuleType() const override; + void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector AnnotationsVector() const override; + std::map AnnotationsSimpleMap() const override; + std::vector AnnotationObjects() const override; + std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; + + private: + std::string name_; + ElfImageReader* elf_reader_; + std::unique_ptr crashpad_info_; + ModuleType type_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotLinux); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_MODULE_SNAPSHOT_LINUX_H_ diff --git a/snapshot/linux/process_reader.cc b/snapshot/linux/process_reader.cc index c9a5a382..fb5b8eef 100644 --- a/snapshot/linux/process_reader.cc +++ b/snapshot/linux/process_reader.cc @@ -14,6 +14,7 @@ #include "snapshot/linux/process_reader.h" +#include #include #include #include @@ -26,7 +27,9 @@ #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "build/build_config.h" +#include "snapshot/linux/debug_rendezvous.h" #include "util/file/directory_reader.h" +#include "util/linux/auxiliary_vector.h" #include "util/linux/proc_stat_reader.h" #include "util/misc/as_underlying_type.h" @@ -166,14 +169,22 @@ void ProcessReader::Thread::InitializeStack(ProcessReader* reader) { } } +ProcessReader::Module::Module() + : name(), elf_reader(nullptr), type(ModuleSnapshot::kModuleTypeUnknown) {} + +ProcessReader::Module::~Module() = default; + ProcessReader::ProcessReader() : connection_(), process_info_(), memory_map_(), threads_(), + modules_(), + elf_readers_(), process_memory_(), is_64_bit_(false), initialized_threads_(false), + initialized_modules_(false), initialized_() {} ProcessReader::~ProcessReader() {} @@ -250,6 +261,14 @@ const std::vector& ProcessReader::Threads() { return threads_; } +const std::vector& ProcessReader::Modules() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!initialized_modules_) { + InitializeModules(); + } + return modules_; +} + void ProcessReader::InitializeThreads() { DCHECK(threads_.empty()); @@ -307,4 +326,78 @@ void ProcessReader::InitializeThreads() { DCHECK(main_thread_found); } +void ProcessReader::InitializeModules() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + AuxiliaryVector aux; + if (!aux.Initialize(ProcessID(), is_64_bit_)) { + return; + } + + LinuxVMAddress phdrs; + if (!aux.GetValue(AT_PHDR, &phdrs)) { + return; + } + + const MemoryMap::Mapping* exe_mapping; + if (!(exe_mapping = GetMemoryMap()->FindMapping(phdrs)) && + !(exe_mapping = GetMemoryMap()->FindFileMmapStart(*exe_mapping))) { + return; + } + + ProcessMemoryRange range; + if (!range.Initialize(Memory(), is_64_bit_)) { + return; + } + + auto exe_reader = std::make_unique(); + if (!exe_reader->Initialize(range, exe_mapping->range.Base())) { + return; + } + + LinuxVMAddress debug_address; + if (!exe_reader->GetDebugAddress(&debug_address)) { + return; + } + + DebugRendezvous debug; + if (!debug.Initialize(range, debug_address)) { + return; + } + + Module exe = {}; + exe.name = !debug.Executable()->name.empty() ? debug.Executable()->name + : exe_mapping->name; + exe.elf_reader = exe_reader.get(); + exe.type = ModuleSnapshot::ModuleType::kModuleTypeExecutable; + + modules_.push_back(exe); + elf_readers_.push_back(std::move(exe_reader)); + + LinuxVMAddress loader_base = 0; + aux.GetValue(AT_BASE, &loader_base); + + for (const DebugRendezvous::LinkEntry& entry : debug.Modules()) { + const MemoryMap::Mapping* mapping; + if (!(mapping = memory_map_.FindMapping(entry.dynamic_array)) || + !(mapping = memory_map_.FindFileMmapStart(*mapping))) { + continue; + } + + auto elf_reader = std::make_unique(); + if (!elf_reader->Initialize(range, mapping->range.Base())) { + continue; + } + + Module module = {}; + module.name = !entry.name.empty() ? entry.name : mapping->name; + module.elf_reader = elf_reader.get(); + module.type = loader_base && elf_reader->Address() == loader_base + ? ModuleSnapshot::kModuleTypeDynamicLoader + : ModuleSnapshot::kModuleTypeSharedLibrary; + modules_.push_back(module); + elf_readers_.push_back(std::move(elf_reader)); + } +} + } // namespace crashpad diff --git a/snapshot/linux/process_reader.h b/snapshot/linux/process_reader.h index 26d777e4..59a3c7ef 100644 --- a/snapshot/linux/process_reader.h +++ b/snapshot/linux/process_reader.h @@ -18,9 +18,13 @@ #include #include +#include +#include #include #include "base/macros.h" +#include "snapshot/elf/elf_image_reader.h" +#include "snapshot/module_snapshot.h" #include "util/linux/address_types.h" #include "util/linux/memory_map.h" #include "util/linux/ptrace_connection.h" @@ -56,6 +60,27 @@ class ProcessReader { void InitializeStack(ProcessReader* reader); }; + //! \brief Contains information about a module loaded into a process. + struct Module { + Module(); + ~Module(); + + //! \brief The pathname used to load the module from disk. + std::string name; + + //! \brief An image reader for the module. + //! + //! The lifetime of this ElfImageReader is scoped to the lifetime of the + //! ProcessReader that created it. + //! + //! This field may be `nullptr` if a reader could not be created for the + //! module. + ElfImageReader* elf_reader; + + //! \brief The module's type. + ModuleSnapshot::ModuleType type; + }; + ProcessReader(); ~ProcessReader(); @@ -107,16 +132,24 @@ class ProcessReader { //! index `0`. const std::vector& Threads(); + //! \return The modules loaded in the process. The first element (at index + //! `0`) corresponds to the main executable. + const std::vector& Modules(); + private: void InitializeThreads(); + void InitializeModules(); PtraceConnection* connection_; // weak ProcessInfo process_info_; MemoryMap memory_map_; std::vector threads_; + std::vector modules_; + std::vector> elf_readers_; ProcessMemoryLinux process_memory_; bool is_64_bit_; bool initialized_threads_; + bool initialized_modules_; InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ProcessReader); diff --git a/snapshot/linux/process_reader_test.cc b/snapshot/linux/process_reader_test.cc index 4bdee743..f877bf86 100644 --- a/snapshot/linux/process_reader_test.cc +++ b/snapshot/linux/process_reader_test.cc @@ -15,6 +15,7 @@ #include "snapshot/linux/process_reader.h" #include +#include #include #include #include @@ -440,6 +441,89 @@ TEST(ProcessReader, ChildWithSplitStack) { test.Run(); } +int ExpectFindModule(dl_phdr_info* info, size_t size, void* data) { + SCOPED_TRACE( + base::StringPrintf("module %s at 0x%" PRIx64 " phdrs 0x%" PRIx64, + info->dlpi_name, + LinuxVMAddress{info->dlpi_addr}, + FromPointerCast(info->dlpi_phdr))); + auto modules = + reinterpret_cast*>(data); + + auto phdr_addr = FromPointerCast(info->dlpi_phdr); + +#if defined(OS_ANDROID) + // Bionic includes a null entry. + if (!phdr_addr) { + EXPECT_EQ(info->dlpi_name, nullptr); + EXPECT_EQ(info->dlpi_addr, 0u); + EXPECT_EQ(info->dlpi_phnum, 0u); + return 0; + } +#endif + + // TODO(jperaza): This can use a range map when one is available. + bool found = false; + for (const auto& module : *modules) { + if (module.elf_reader && phdr_addr >= module.elf_reader->Address() && + phdr_addr < module.elf_reader->Address() + module.elf_reader->Size()) { + found = true; + break; + } + } + EXPECT_TRUE(found); + return 0; +} + +void ExpectModulesFromSelf(const std::vector& modules) { + for (const auto& module : modules) { + EXPECT_FALSE(module.name.empty()); + EXPECT_NE(module.type, ModuleSnapshot::kModuleTypeUnknown); + } + + EXPECT_EQ(dl_iterate_phdr( + ExpectFindModule, + reinterpret_cast( + const_cast*>(&modules))), + 0); +} + +TEST(ProcessReader, SelfModules) { + FakePtraceConnection connection; + connection.Initialize(getpid()); + + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + ExpectModulesFromSelf(process_reader.Modules()); +} + +class ChildModuleTest : public Multiprocess { + public: + ChildModuleTest() : Multiprocess() {} + ~ChildModuleTest() = default; + + private: + void MultiprocessParent() override { + DirectPtraceConnection connection; + ASSERT_TRUE(connection.Initialize(ChildPID())); + + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + ExpectModulesFromSelf(process_reader.Modules()); + } + + void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); } + + DISALLOW_COPY_AND_ASSIGN(ChildModuleTest); +}; + +TEST(ProcessReader, ChildModules) { + ChildModuleTest test; + test.Run(); +} + } // namespace } // namespace test } // namespace crashpad diff --git a/snapshot/linux/process_snapshot_linux.cc b/snapshot/linux/process_snapshot_linux.cc index 346da085..acfdcba0 100644 --- a/snapshot/linux/process_snapshot_linux.cc +++ b/snapshot/linux/process_snapshot_linux.cc @@ -49,6 +49,7 @@ bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection) { system_.Initialize(&process_reader_, &snapshot_time_); InitializeThreads(); + InitializeModules(); INITIALIZATION_STATE_SET_VALID(initialized_); return true; @@ -136,9 +137,11 @@ std::vector ProcessSnapshotLinux::Threads() const { std::vector ProcessSnapshotLinux::Modules() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - // TODO(jperaza): do this. - LOG(ERROR) << "Not implemented"; - return std::vector(); + std::vector modules; + for (const auto& module : modules_) { + modules.push_back(module.get()); + } + return modules; } std::vector ProcessSnapshotLinux::UnloadedModules() @@ -182,4 +185,13 @@ void ProcessSnapshotLinux::InitializeThreads() { } } +void ProcessSnapshotLinux::InitializeModules() { + for (const ProcessReader::Module& reader_module : process_reader_.Modules()) { + auto module = std::make_unique(); + if (module->Initialize(reader_module)) { + modules_.push_back(std::move(module)); + } + } +} + } // namespace crashpad diff --git a/snapshot/linux/process_snapshot_linux.h b/snapshot/linux/process_snapshot_linux.h index 446ddb54..68d625b0 100644 --- a/snapshot/linux/process_snapshot_linux.h +++ b/snapshot/linux/process_snapshot_linux.h @@ -25,6 +25,7 @@ #include "base/macros.h" #include "snapshot/linux/exception_snapshot_linux.h" +#include "snapshot/linux/module_snapshot_linux.h" #include "snapshot/linux/process_reader.h" #include "snapshot/linux/system_snapshot_linux.h" #include "snapshot/linux/thread_snapshot_linux.h" @@ -108,12 +109,14 @@ class ProcessSnapshotLinux final : public ProcessSnapshot { private: void InitializeThreads(); + void InitializeModules(); std::map annotations_simple_map_; timeval snapshot_time_; UUID report_id_; UUID client_id_; std::vector> threads_; + std::vector> modules_; std::unique_ptr exception_; internal::SystemSnapshotLinux system_; ProcessReader process_reader_; diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp index 35fb614e..caa5f0e0 100644 --- a/snapshot/snapshot.gyp +++ b/snapshot/snapshot.gyp @@ -60,6 +60,8 @@ 'linux/exception_snapshot_linux.h', 'linux/memory_snapshot_linux.cc', 'linux/memory_snapshot_linux.h', + 'linux/module_snapshot_linux.cc', + 'linux/module_snapshot_linux.h', 'linux/process_reader.cc', 'linux/process_reader.h', 'linux/process_snapshot_linux.cc',