breadcrumb_persistent_storage_manager.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/breadcrumbs/core/breadcrumb_persistent_storage_manager.h"
  5. #include <string.h>
  6. #include <string>
  7. #include "base/bind.h"
  8. #include "base/containers/adapters.h"
  9. #include "base/debug/alias.h"
  10. #include "base/files/file_util.h"
  11. #include "base/files/memory_mapped_file.h"
  12. #include "base/strings/string_split.h"
  13. #include "base/strings/string_util.h"
  14. #include "base/strings/utf_string_conversions.h"
  15. #include "base/task/sequenced_task_runner.h"
  16. #include "base/task/thread_pool.h"
  17. #include "build/build_config.h"
  18. #include "components/breadcrumbs/core/breadcrumb_manager.h"
  19. #include "components/breadcrumbs/core/breadcrumb_manager_keyed_service.h"
  20. #include "components/breadcrumbs/core/breadcrumb_persistent_storage_util.h"
  21. namespace breadcrumbs {
  22. namespace {
  23. const char kEventSeparator[] = "\n";
  24. // Minimum time between breadcrumb writes to disk.
  25. constexpr auto kMinDelayBetweenWrites = base::Milliseconds(250);
  26. // Writes the given |events| to |file_path| at |position|. If |append| is false,
  27. // the existing file will be overwritten.
  28. void DoWriteEventsToFile(const base::FilePath& file_path,
  29. const size_t position,
  30. const std::string& events,
  31. const bool append) {
  32. const base::MemoryMappedFile::Region region = {0, kPersistedFilesizeInBytes};
  33. base::MemoryMappedFile file;
  34. int flags = base::File::FLAG_READ | base::File::FLAG_WRITE;
  35. if (append) {
  36. flags |= base::File::FLAG_OPEN_ALWAYS;
  37. } else {
  38. flags |= base::File::FLAG_CREATE_ALWAYS;
  39. }
  40. const bool file_valid =
  41. file.Initialize(base::File(file_path, flags), region,
  42. base::MemoryMappedFile::READ_WRITE_EXTEND);
  43. if (file_valid) {
  44. const size_t remaining_length = kPersistedFilesizeInBytes - position;
  45. #if BUILDFLAG(IS_CHROMEOS)
  46. // TODO(crbug.com/1327267): Remove this once crashes in this function on
  47. // CrOS are understood. The first and last values are delimiters to aid in
  48. // finding this array on the stack, as CrOS crashes are hard to debug.
  49. size_t debug_data[] = {
  50. 0x1234beef, reinterpret_cast<size_t>(file.data()),
  51. file.length(), position,
  52. events.length(), 0x5678beef};
  53. base::debug::Alias(&debug_data);
  54. #endif // BUILDFLAG(IS_CHROMEOS)
  55. char* data = reinterpret_cast<char*>(file.data());
  56. base::strlcpy(&data[position], events.c_str(), remaining_length);
  57. }
  58. }
  59. // Returns breadcrumb events stored at |file_path|.
  60. std::vector<std::string> DoGetStoredEvents(const base::FilePath& file_path) {
  61. base::File events_file(file_path,
  62. base::File::FLAG_OPEN | base::File::FLAG_READ);
  63. if (!events_file.IsValid()) {
  64. // File may not yet exist.
  65. return std::vector<std::string>();
  66. }
  67. size_t file_size = events_file.GetLength();
  68. if (file_size <= 0)
  69. return std::vector<std::string>();
  70. // Do not read more than |kPersistedFilesizeInBytes|, in case the file was
  71. // corrupted. If |kPersistedFilesizeInBytes| has been reduced since the last
  72. // breadcrumbs file was saved, this could result in a one time loss of the
  73. // oldest breadcrumbs which is ok because the decision has already been made
  74. // to reduce the size of the stored breadcrumbs.
  75. if (file_size > kPersistedFilesizeInBytes)
  76. file_size = kPersistedFilesizeInBytes;
  77. std::vector<uint8_t> data;
  78. data.resize(file_size);
  79. if (!events_file.ReadAndCheck(/*offset=*/0, data))
  80. return std::vector<std::string>();
  81. const std::string persisted_events(data.begin(), data.end());
  82. const std::string all_events =
  83. persisted_events.substr(/*pos=*/0, strlen(persisted_events.c_str()));
  84. return base::SplitString(all_events, kEventSeparator, base::TRIM_WHITESPACE,
  85. base::SPLIT_WANT_NONEMPTY);
  86. }
  87. // Returns the total length of stored breadcrumb events at |file_path|. The
  88. // file is opened and the length of the string contents calculated because
  89. // the file size is always constant. (Due to base::MemoryMappedFile filling the
  90. // unused space with \0s.
  91. size_t DoGetStoredEventsLength(const base::FilePath& file_path) {
  92. base::File events_file(file_path,
  93. base::File::FLAG_OPEN | base::File::FLAG_READ);
  94. if (!events_file.IsValid())
  95. return 0;
  96. size_t file_size = events_file.GetLength();
  97. if (file_size <= 0)
  98. return 0;
  99. // Do not read more than |kPersistedFilesizeInBytes|, in case the file was
  100. // corrupted. If |kPersistedFilesizeInBytes| has been reduced since the last
  101. // breadcrumbs file was saved, this could result in a one time loss of the
  102. // oldest breadcrumbs which is ok because the decision has already been made
  103. // to reduce the size of the stored breadcrumbs.
  104. if (file_size > kPersistedFilesizeInBytes)
  105. file_size = kPersistedFilesizeInBytes;
  106. std::vector<uint8_t> data;
  107. data.resize(file_size);
  108. if (!events_file.ReadAndCheck(/*offset=*/0, data))
  109. return 0;
  110. const std::string persisted_events(data.begin(), data.end());
  111. return strlen(persisted_events.c_str());
  112. }
  113. } // namespace
  114. BreadcrumbPersistentStorageManager::BreadcrumbPersistentStorageManager(
  115. const base::FilePath& directory,
  116. base::RepeatingCallback<bool()> is_metrics_enabled_callback)
  117. : // Ensure first event will not be delayed by initializing with a time in
  118. // the past.
  119. last_written_time_(base::TimeTicks::Now() - kMinDelayBetweenWrites),
  120. breadcrumbs_file_path_(GetBreadcrumbPersistentStorageFilePath(directory)),
  121. breadcrumbs_temp_file_path_(
  122. GetBreadcrumbPersistentStorageTempFilePath(directory)),
  123. is_metrics_enabled_callback_(std::move(is_metrics_enabled_callback)),
  124. task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
  125. {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
  126. base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
  127. weak_ptr_factory_(this) {
  128. task_runner_->PostTaskAndReplyWithResult(
  129. FROM_HERE,
  130. base::BindOnce(&DoGetStoredEventsLength, breadcrumbs_file_path_),
  131. base::BindOnce(
  132. &BreadcrumbPersistentStorageManager::InitializeFilePosition,
  133. weak_ptr_factory_.GetWeakPtr()));
  134. }
  135. BreadcrumbPersistentStorageManager::~BreadcrumbPersistentStorageManager() =
  136. default;
  137. void BreadcrumbPersistentStorageManager::GetStoredEvents(
  138. base::OnceCallback<void(std::vector<std::string>)> callback) {
  139. task_runner_->PostTaskAndReplyWithResult(
  140. FROM_HERE, base::BindOnce(&DoGetStoredEvents, breadcrumbs_file_path_),
  141. std::move(callback));
  142. }
  143. void BreadcrumbPersistentStorageManager::MonitorBreadcrumbManager(
  144. BreadcrumbManager* manager) {
  145. manager->AddObserver(this);
  146. }
  147. void BreadcrumbPersistentStorageManager::MonitorBreadcrumbManagerService(
  148. BreadcrumbManagerKeyedService* service) {
  149. service->AddObserver(this);
  150. }
  151. void BreadcrumbPersistentStorageManager::StopMonitoringBreadcrumbManager(
  152. BreadcrumbManager* manager) {
  153. manager->RemoveObserver(this);
  154. }
  155. void BreadcrumbPersistentStorageManager::StopMonitoringBreadcrumbManagerService(
  156. BreadcrumbManagerKeyedService* service) {
  157. service->RemoveObserver(this);
  158. }
  159. void BreadcrumbPersistentStorageManager::CombineEventsAndRewriteAllBreadcrumbs(
  160. const std::vector<std::string> pending_breadcrumbs,
  161. std::vector<std::string> existing_events) {
  162. if (!CheckForFileConsent())
  163. return;
  164. existing_events.insert(existing_events.end(), pending_breadcrumbs.begin(),
  165. pending_breadcrumbs.end());
  166. std::vector<std::string> breadcrumbs;
  167. for (const std::string& event : base::Reversed(existing_events)) {
  168. // Reduce saved events to only the amount that can be included in a crash
  169. // report. This allows future events to be appended up to
  170. // |kPersistedFilesizeInBytes|, reducing the number of resizes needed.
  171. const int event_with_seperator_size =
  172. event.size() + strlen(kEventSeparator);
  173. if (event_with_seperator_size + file_position_.value() >= kMaxDataLength)
  174. break;
  175. breadcrumbs.push_back(kEventSeparator);
  176. breadcrumbs.push_back(event);
  177. file_position_ = file_position_.value() + event_with_seperator_size;
  178. }
  179. std::reverse(breadcrumbs.begin(), breadcrumbs.end());
  180. const std::string breadcrumbs_string = base::JoinString(breadcrumbs, "");
  181. task_runner_->PostTask(
  182. FROM_HERE,
  183. base::BindOnce(&DoWriteEventsToFile, breadcrumbs_temp_file_path_,
  184. /*position=*/0, breadcrumbs_string, /*append=*/false));
  185. task_runner_->PostTask(
  186. FROM_HERE, base::BindOnce(IgnoreResult(&base::ReplaceFile),
  187. breadcrumbs_temp_file_path_,
  188. breadcrumbs_file_path_, /*error=*/nullptr));
  189. }
  190. void BreadcrumbPersistentStorageManager::RewriteAllExistingBreadcrumbs() {
  191. // Collect breadcrumbs which haven't been written yet to include in this full
  192. // re-write.
  193. std::vector<std::string> pending_breadcrumbs =
  194. base::SplitString(pending_breadcrumbs_, kEventSeparator,
  195. base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
  196. pending_breadcrumbs_.clear();
  197. write_timer_.Stop();
  198. last_written_time_ = base::TimeTicks::Now();
  199. file_position_ = 0;
  200. // Load persisted events directly from file because the correct order can not
  201. // be reconstructed from the multiple BreadcrumbManagers with the partial
  202. // timestamps embedded in each event.
  203. GetStoredEvents(base::BindOnce(&BreadcrumbPersistentStorageManager::
  204. CombineEventsAndRewriteAllBreadcrumbs,
  205. weak_ptr_factory_.GetWeakPtr(),
  206. pending_breadcrumbs));
  207. }
  208. void BreadcrumbPersistentStorageManager::WritePendingBreadcrumbs() {
  209. if (!CheckForFileConsent() || pending_breadcrumbs_.empty())
  210. return;
  211. // Make a copy of |pending_breadcrumbs_| to pass to the DoWriteEventsToFile()
  212. // callback, since |pending_breadcrumbs_| is about to be cleared.
  213. const std::string pending_breadcrumbs = pending_breadcrumbs_;
  214. task_runner_->PostTask(
  215. FROM_HERE, base::BindOnce(&DoWriteEventsToFile, breadcrumbs_file_path_,
  216. file_position_.value(), pending_breadcrumbs,
  217. /*append=*/true));
  218. file_position_ = file_position_.value() + pending_breadcrumbs_.size();
  219. last_written_time_ = base::TimeTicks::Now();
  220. pending_breadcrumbs_.clear();
  221. }
  222. void BreadcrumbPersistentStorageManager::EventAdded(BreadcrumbManager* manager,
  223. const std::string& event) {
  224. pending_breadcrumbs_ += event + kEventSeparator;
  225. WriteEvents();
  226. }
  227. bool BreadcrumbPersistentStorageManager::CheckForFileConsent() {
  228. static bool should_create_files = true;
  229. const bool is_metrics_and_crash_reporting_enabled =
  230. is_metrics_enabled_callback_.Run();
  231. // If metrics consent has been revoked since this was last checked, delete any
  232. // existing breadcrumbs files.
  233. if (should_create_files && !is_metrics_and_crash_reporting_enabled) {
  234. DeleteBreadcrumbFiles(breadcrumbs_file_path_.DirName());
  235. file_position_ = 0;
  236. pending_breadcrumbs_.clear();
  237. }
  238. should_create_files = is_metrics_and_crash_reporting_enabled;
  239. return should_create_files;
  240. }
  241. void BreadcrumbPersistentStorageManager::InitializeFilePosition(
  242. size_t file_size) {
  243. file_position_ = file_size;
  244. // Write any startup events that have accumulated while waiting for this
  245. // function to run.
  246. WriteEvents();
  247. }
  248. void BreadcrumbPersistentStorageManager::WriteEvents() {
  249. // No events can be written to the file until the size of existing breadcrumbs
  250. // is known.
  251. if (!file_position_)
  252. return;
  253. write_timer_.Stop();
  254. const base::TimeDelta time_delta_since_last_write =
  255. base::TimeTicks::Now() - last_written_time_;
  256. if (time_delta_since_last_write < kMinDelayBetweenWrites) {
  257. // If an event was just written, delay writing the event to disk in order to
  258. // limit overhead.
  259. write_timer_.Start(
  260. FROM_HERE, kMinDelayBetweenWrites - time_delta_since_last_write,
  261. base::BindOnce(&BreadcrumbPersistentStorageManager::WriteEvents,
  262. weak_ptr_factory_.GetWeakPtr()));
  263. return;
  264. }
  265. // If the event does not fit within |kPersistedFilesizeInBytes|, rewrite the
  266. // file to trim old events.
  267. if ((file_position_.value() + pending_breadcrumbs_.size())
  268. // Use >= here instead of > to allow space for \0 to terminate file.
  269. >= kPersistedFilesizeInBytes) {
  270. RewriteAllExistingBreadcrumbs();
  271. return;
  272. }
  273. // Otherwise, simply append the pending breadcrumbs.
  274. WritePendingBreadcrumbs();
  275. }
  276. void BreadcrumbPersistentStorageManager::OldEventsRemoved(
  277. BreadcrumbManager* manager) {
  278. RewriteAllExistingBreadcrumbs();
  279. }
  280. } // namespace breadcrumbs