123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- // Copyright 2020 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/breadcrumbs/core/breadcrumb_persistent_storage_manager.h"
- #include <string.h>
- #include <string>
- #include "base/bind.h"
- #include "base/containers/adapters.h"
- #include "base/debug/alias.h"
- #include "base/files/file_util.h"
- #include "base/files/memory_mapped_file.h"
- #include "base/strings/string_split.h"
- #include "base/strings/string_util.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/task/sequenced_task_runner.h"
- #include "base/task/thread_pool.h"
- #include "build/build_config.h"
- #include "components/breadcrumbs/core/breadcrumb_manager.h"
- #include "components/breadcrumbs/core/breadcrumb_manager_keyed_service.h"
- #include "components/breadcrumbs/core/breadcrumb_persistent_storage_util.h"
- namespace breadcrumbs {
- namespace {
- const char kEventSeparator[] = "\n";
- // Minimum time between breadcrumb writes to disk.
- constexpr auto kMinDelayBetweenWrites = base::Milliseconds(250);
- // Writes the given |events| to |file_path| at |position|. If |append| is false,
- // the existing file will be overwritten.
- void DoWriteEventsToFile(const base::FilePath& file_path,
- const size_t position,
- const std::string& events,
- const bool append) {
- const base::MemoryMappedFile::Region region = {0, kPersistedFilesizeInBytes};
- base::MemoryMappedFile file;
- int flags = base::File::FLAG_READ | base::File::FLAG_WRITE;
- if (append) {
- flags |= base::File::FLAG_OPEN_ALWAYS;
- } else {
- flags |= base::File::FLAG_CREATE_ALWAYS;
- }
- const bool file_valid =
- file.Initialize(base::File(file_path, flags), region,
- base::MemoryMappedFile::READ_WRITE_EXTEND);
- if (file_valid) {
- const size_t remaining_length = kPersistedFilesizeInBytes - position;
- #if BUILDFLAG(IS_CHROMEOS)
- // TODO(crbug.com/1327267): Remove this once crashes in this function on
- // CrOS are understood. The first and last values are delimiters to aid in
- // finding this array on the stack, as CrOS crashes are hard to debug.
- size_t debug_data[] = {
- 0x1234beef, reinterpret_cast<size_t>(file.data()),
- file.length(), position,
- events.length(), 0x5678beef};
- base::debug::Alias(&debug_data);
- #endif // BUILDFLAG(IS_CHROMEOS)
- char* data = reinterpret_cast<char*>(file.data());
- base::strlcpy(&data[position], events.c_str(), remaining_length);
- }
- }
- // Returns breadcrumb events stored at |file_path|.
- std::vector<std::string> DoGetStoredEvents(const base::FilePath& file_path) {
- base::File events_file(file_path,
- base::File::FLAG_OPEN | base::File::FLAG_READ);
- if (!events_file.IsValid()) {
- // File may not yet exist.
- return std::vector<std::string>();
- }
- size_t file_size = events_file.GetLength();
- if (file_size <= 0)
- return std::vector<std::string>();
- // Do not read more than |kPersistedFilesizeInBytes|, in case the file was
- // corrupted. If |kPersistedFilesizeInBytes| has been reduced since the last
- // breadcrumbs file was saved, this could result in a one time loss of the
- // oldest breadcrumbs which is ok because the decision has already been made
- // to reduce the size of the stored breadcrumbs.
- if (file_size > kPersistedFilesizeInBytes)
- file_size = kPersistedFilesizeInBytes;
- std::vector<uint8_t> data;
- data.resize(file_size);
- if (!events_file.ReadAndCheck(/*offset=*/0, data))
- return std::vector<std::string>();
- const std::string persisted_events(data.begin(), data.end());
- const std::string all_events =
- persisted_events.substr(/*pos=*/0, strlen(persisted_events.c_str()));
- return base::SplitString(all_events, kEventSeparator, base::TRIM_WHITESPACE,
- base::SPLIT_WANT_NONEMPTY);
- }
- // Returns the total length of stored breadcrumb events at |file_path|. The
- // file is opened and the length of the string contents calculated because
- // the file size is always constant. (Due to base::MemoryMappedFile filling the
- // unused space with \0s.
- size_t DoGetStoredEventsLength(const base::FilePath& file_path) {
- base::File events_file(file_path,
- base::File::FLAG_OPEN | base::File::FLAG_READ);
- if (!events_file.IsValid())
- return 0;
- size_t file_size = events_file.GetLength();
- if (file_size <= 0)
- return 0;
- // Do not read more than |kPersistedFilesizeInBytes|, in case the file was
- // corrupted. If |kPersistedFilesizeInBytes| has been reduced since the last
- // breadcrumbs file was saved, this could result in a one time loss of the
- // oldest breadcrumbs which is ok because the decision has already been made
- // to reduce the size of the stored breadcrumbs.
- if (file_size > kPersistedFilesizeInBytes)
- file_size = kPersistedFilesizeInBytes;
- std::vector<uint8_t> data;
- data.resize(file_size);
- if (!events_file.ReadAndCheck(/*offset=*/0, data))
- return 0;
- const std::string persisted_events(data.begin(), data.end());
- return strlen(persisted_events.c_str());
- }
- } // namespace
- BreadcrumbPersistentStorageManager::BreadcrumbPersistentStorageManager(
- const base::FilePath& directory,
- base::RepeatingCallback<bool()> is_metrics_enabled_callback)
- : // Ensure first event will not be delayed by initializing with a time in
- // the past.
- last_written_time_(base::TimeTicks::Now() - kMinDelayBetweenWrites),
- breadcrumbs_file_path_(GetBreadcrumbPersistentStorageFilePath(directory)),
- breadcrumbs_temp_file_path_(
- GetBreadcrumbPersistentStorageTempFilePath(directory)),
- is_metrics_enabled_callback_(std::move(is_metrics_enabled_callback)),
- task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
- {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
- base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
- weak_ptr_factory_(this) {
- task_runner_->PostTaskAndReplyWithResult(
- FROM_HERE,
- base::BindOnce(&DoGetStoredEventsLength, breadcrumbs_file_path_),
- base::BindOnce(
- &BreadcrumbPersistentStorageManager::InitializeFilePosition,
- weak_ptr_factory_.GetWeakPtr()));
- }
- BreadcrumbPersistentStorageManager::~BreadcrumbPersistentStorageManager() =
- default;
- void BreadcrumbPersistentStorageManager::GetStoredEvents(
- base::OnceCallback<void(std::vector<std::string>)> callback) {
- task_runner_->PostTaskAndReplyWithResult(
- FROM_HERE, base::BindOnce(&DoGetStoredEvents, breadcrumbs_file_path_),
- std::move(callback));
- }
- void BreadcrumbPersistentStorageManager::MonitorBreadcrumbManager(
- BreadcrumbManager* manager) {
- manager->AddObserver(this);
- }
- void BreadcrumbPersistentStorageManager::MonitorBreadcrumbManagerService(
- BreadcrumbManagerKeyedService* service) {
- service->AddObserver(this);
- }
- void BreadcrumbPersistentStorageManager::StopMonitoringBreadcrumbManager(
- BreadcrumbManager* manager) {
- manager->RemoveObserver(this);
- }
- void BreadcrumbPersistentStorageManager::StopMonitoringBreadcrumbManagerService(
- BreadcrumbManagerKeyedService* service) {
- service->RemoveObserver(this);
- }
- void BreadcrumbPersistentStorageManager::CombineEventsAndRewriteAllBreadcrumbs(
- const std::vector<std::string> pending_breadcrumbs,
- std::vector<std::string> existing_events) {
- if (!CheckForFileConsent())
- return;
- existing_events.insert(existing_events.end(), pending_breadcrumbs.begin(),
- pending_breadcrumbs.end());
- std::vector<std::string> breadcrumbs;
- for (const std::string& event : base::Reversed(existing_events)) {
- // Reduce saved events to only the amount that can be included in a crash
- // report. This allows future events to be appended up to
- // |kPersistedFilesizeInBytes|, reducing the number of resizes needed.
- const int event_with_seperator_size =
- event.size() + strlen(kEventSeparator);
- if (event_with_seperator_size + file_position_.value() >= kMaxDataLength)
- break;
- breadcrumbs.push_back(kEventSeparator);
- breadcrumbs.push_back(event);
- file_position_ = file_position_.value() + event_with_seperator_size;
- }
- std::reverse(breadcrumbs.begin(), breadcrumbs.end());
- const std::string breadcrumbs_string = base::JoinString(breadcrumbs, "");
- task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&DoWriteEventsToFile, breadcrumbs_temp_file_path_,
- /*position=*/0, breadcrumbs_string, /*append=*/false));
- task_runner_->PostTask(
- FROM_HERE, base::BindOnce(IgnoreResult(&base::ReplaceFile),
- breadcrumbs_temp_file_path_,
- breadcrumbs_file_path_, /*error=*/nullptr));
- }
- void BreadcrumbPersistentStorageManager::RewriteAllExistingBreadcrumbs() {
- // Collect breadcrumbs which haven't been written yet to include in this full
- // re-write.
- std::vector<std::string> pending_breadcrumbs =
- base::SplitString(pending_breadcrumbs_, kEventSeparator,
- base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- pending_breadcrumbs_.clear();
- write_timer_.Stop();
- last_written_time_ = base::TimeTicks::Now();
- file_position_ = 0;
- // Load persisted events directly from file because the correct order can not
- // be reconstructed from the multiple BreadcrumbManagers with the partial
- // timestamps embedded in each event.
- GetStoredEvents(base::BindOnce(&BreadcrumbPersistentStorageManager::
- CombineEventsAndRewriteAllBreadcrumbs,
- weak_ptr_factory_.GetWeakPtr(),
- pending_breadcrumbs));
- }
- void BreadcrumbPersistentStorageManager::WritePendingBreadcrumbs() {
- if (!CheckForFileConsent() || pending_breadcrumbs_.empty())
- return;
- // Make a copy of |pending_breadcrumbs_| to pass to the DoWriteEventsToFile()
- // callback, since |pending_breadcrumbs_| is about to be cleared.
- const std::string pending_breadcrumbs = pending_breadcrumbs_;
- task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&DoWriteEventsToFile, breadcrumbs_file_path_,
- file_position_.value(), pending_breadcrumbs,
- /*append=*/true));
- file_position_ = file_position_.value() + pending_breadcrumbs_.size();
- last_written_time_ = base::TimeTicks::Now();
- pending_breadcrumbs_.clear();
- }
- void BreadcrumbPersistentStorageManager::EventAdded(BreadcrumbManager* manager,
- const std::string& event) {
- pending_breadcrumbs_ += event + kEventSeparator;
- WriteEvents();
- }
- bool BreadcrumbPersistentStorageManager::CheckForFileConsent() {
- static bool should_create_files = true;
- const bool is_metrics_and_crash_reporting_enabled =
- is_metrics_enabled_callback_.Run();
- // If metrics consent has been revoked since this was last checked, delete any
- // existing breadcrumbs files.
- if (should_create_files && !is_metrics_and_crash_reporting_enabled) {
- DeleteBreadcrumbFiles(breadcrumbs_file_path_.DirName());
- file_position_ = 0;
- pending_breadcrumbs_.clear();
- }
- should_create_files = is_metrics_and_crash_reporting_enabled;
- return should_create_files;
- }
- void BreadcrumbPersistentStorageManager::InitializeFilePosition(
- size_t file_size) {
- file_position_ = file_size;
- // Write any startup events that have accumulated while waiting for this
- // function to run.
- WriteEvents();
- }
- void BreadcrumbPersistentStorageManager::WriteEvents() {
- // No events can be written to the file until the size of existing breadcrumbs
- // is known.
- if (!file_position_)
- return;
- write_timer_.Stop();
- const base::TimeDelta time_delta_since_last_write =
- base::TimeTicks::Now() - last_written_time_;
- if (time_delta_since_last_write < kMinDelayBetweenWrites) {
- // If an event was just written, delay writing the event to disk in order to
- // limit overhead.
- write_timer_.Start(
- FROM_HERE, kMinDelayBetweenWrites - time_delta_since_last_write,
- base::BindOnce(&BreadcrumbPersistentStorageManager::WriteEvents,
- weak_ptr_factory_.GetWeakPtr()));
- return;
- }
- // If the event does not fit within |kPersistedFilesizeInBytes|, rewrite the
- // file to trim old events.
- if ((file_position_.value() + pending_breadcrumbs_.size())
- // Use >= here instead of > to allow space for \0 to terminate file.
- >= kPersistedFilesizeInBytes) {
- RewriteAllExistingBreadcrumbs();
- return;
- }
- // Otherwise, simply append the pending breadcrumbs.
- WritePendingBreadcrumbs();
- }
- void BreadcrumbPersistentStorageManager::OldEventsRemoved(
- BreadcrumbManager* manager) {
- RewriteAllExistingBreadcrumbs();
- }
- } // namespace breadcrumbs
|