// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/browser_watcher/stability_paths.h" #if defined(OS_WIN) #include #endif // defined(OS_WIN) #include #include #include #include "base/debug/activity_tracker.h" #include "base/feature_list.h" #include "base/files/file.h" #include "base/files/file_enumerator.h" #include "base/files/memory_mapped_file.h" #include "base/metrics/persistent_memory_allocator.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" #include "components/browser_watcher/features.h" #include "components/browser_watcher/stability_metrics.h" #if defined(OS_WIN) #include "third_party/crashpad/crashpad/util/misc/time.h" namespace browser_watcher { using base::FilePath; using base::FilePersistentMemoryAllocator; using base::MemoryMappedFile; using base::PersistentMemoryAllocator; namespace { bool GetCreationTime(const base::Process& process, FILETIME* creation_time) { FILETIME ignore; return ::GetProcessTimes(process.Handle(), creation_time, &ignore, &ignore, &ignore) != 0; } bool SetPmaFileDeleted(const base::FilePath& file_path) { // Map the file read-write so it can guarantee consistency between // the analyzer and any trackers that may still be active. std::unique_ptr mmfile(new MemoryMappedFile()); mmfile->Initialize(file_path, MemoryMappedFile::READ_WRITE); if (!mmfile->IsValid()) return false; if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true)) return false; FilePersistentMemoryAllocator allocator(std::move(mmfile), 0, 0, base::StringPiece(), true); allocator.SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED); return true; } } // namespace FilePath GetStabilityDir(const FilePath& user_data_dir) { return user_data_dir.AppendASCII("Stability"); } FilePath GetStabilityFileForProcess(base::ProcessId pid, timeval creation_time, const FilePath& user_data_dir) { FilePath stability_dir = GetStabilityDir(user_data_dir); constexpr uint64_t kMicrosecondsPerSecond = static_cast(1E6); int64_t creation_time_us = creation_time.tv_sec * kMicrosecondsPerSecond + creation_time.tv_usec; std::string file_name = base::StringPrintf("%" CrPRIdPid "-%lld", pid, creation_time_us); return stability_dir.AppendASCII(file_name).AddExtension( base::PersistentMemoryAllocator::kFileExtension); } bool GetStabilityFileForProcess(const base::Process& process, const FilePath& user_data_dir, FilePath* file_path) { DCHECK(file_path); FILETIME creation_time; if (!GetCreationTime(process, &creation_time)) return false; // We rely on Crashpad's conversion to ensure the resulting filename is the // same as on crash, when the creation time is obtained via Crashpad. *file_path = GetStabilityFileForProcess( process.Pid(), crashpad::FiletimeToTimevalEpoch(creation_time), user_data_dir); return true; } FilePath::StringType GetStabilityFilePattern() { return FilePath::StringType(FILE_PATH_LITERAL("*-*")) + base::PersistentMemoryAllocator::kFileExtension; } std::vector GetStabilityFiles( const FilePath& stability_dir, const FilePath::StringType& stability_file_pattern, const std::set& excluded_stability_files) { DCHECK_NE(true, stability_dir.empty()); DCHECK_NE(true, stability_file_pattern.empty()); std::vector paths; base::FileEnumerator enumerator(stability_dir, false /* recursive */, base::FileEnumerator::FILES, stability_file_pattern); FilePath path; for (path = enumerator.Next(); !path.empty(); path = enumerator.Next()) { if (excluded_stability_files.find(path) == excluded_stability_files.end()) paths.push_back(path); } return paths; } void MarkOwnStabilityFileDeleted(const base::FilePath& user_data_dir) { base::debug::GlobalActivityTracker* global_tracker = base::debug::GlobalActivityTracker::Get(); if (!global_tracker) return; // No stability instrumentation. global_tracker->MarkDeleted(); LogStabilityRecordEvent(StabilityRecordEvent::kMarkDeleted); // Open (with delete) and then immediately close the file by going out of // scope. This should cause the stability debugging file to be deleted prior // to the next execution. base::FilePath stability_file; if (!GetStabilityFileForProcess(base::Process::Current(), user_data_dir, &stability_file)) { return; } LogStabilityRecordEvent(StabilityRecordEvent::kMarkDeletedGotFile); base::File deleter(stability_file, base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_DELETE_ON_CLOSE); if (!deleter.IsValid()) LogStabilityRecordEvent(StabilityRecordEvent::kOpenForDeleteFailed); } void MarkStabilityFileDeletedOnCrash(const base::FilePath& file_path) { if (!SetPmaFileDeleted(file_path)) LogCollectOnCrashEvent(CollectOnCrashEvent::kPmaSetDeletedFailed); base::File deleter(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_DELETE_ON_CLOSE); if (!deleter.IsValid()) LogCollectOnCrashEvent(CollectOnCrashEvent::kOpenForDeleteFailed); } } // namespace browser_watcher #endif // defined(OS_WIN)