Commit 2e263192 authored by Mike Dougherty's avatar Mike Dougherty Committed by Commit Bot

[iOS] Add BreadcrumbManager

Add BreadcrumbManager to locally store and discard breadcrumb events.

Bug: 1003922
Change-Id: I20ac153f22b5350129107f5efd0ac622edb07a62
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1804803
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#706685}
parent 9b899bf5
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
import("//ios/build/config.gni")
source_set("feature_flags") { source_set("feature_flags") {
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
sources = [ sources = [
...@@ -12,3 +14,31 @@ source_set("feature_flags") { ...@@ -12,3 +14,31 @@ source_set("feature_flags") {
"//base", "//base",
] ]
} }
source_set("breadcrumbs") {
deps = [
"//base",
]
sources = [
"breadcrumb_manager.cc",
"breadcrumb_manager.h",
]
configs += [ "//build/config/compiler:enable_arc" ]
}
source_set("unit_tests") {
configs += [ "//build/config/compiler:enable_arc" ]
testonly = true
deps = [
":breadcrumbs",
"//base",
"//base/test:test_support",
"//testing/gtest",
]
sources = [
"breadcrumb_manager_unittest.mm",
]
}
eugenebut@chromium.org
michaeldo@chromium.org
# TEAM: ios-directory-owners@chromium.org
# OS: iOS
// Copyright 2019 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 "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h"
#include "base/strings/stringprintf.h"
#include "base/time/time_to_iso8601.h"
namespace {
// The minimum number of event buckets to keep, even if they are expired.
const int kMinEventsBuckets = 2;
// Returns a Time used to bucket events for easier discarding of expired events.
base::Time EventBucket(const base::Time& time) {
base::Time::Exploded exploded;
time.LocalExplode(&exploded);
exploded.millisecond = 0;
exploded.second = 0;
base::Time bucket_time;
bool converted = base::Time::FromLocalExploded(exploded, &bucket_time);
DCHECK(converted);
return bucket_time;
}
} // namespace
std::list<std::string> BreadcrumbManager::GetEvents(size_t event_count_limit) {
DropOldEvents();
std::list<std::string> events;
for (auto it = event_buckets_.rbegin(); it != event_buckets_.rend(); ++it) {
std::list<std::string> bucket_events = it->second;
for (auto event_it = bucket_events.rbegin();
event_it != bucket_events.rend(); ++event_it) {
std::string event = *event_it;
events.push_front(event);
if (event_count_limit > 0 && events.size() >= event_count_limit) {
return events;
}
}
}
return events;
}
void BreadcrumbManager::AddEvent(const std::string& event) {
base::Time time = base::Time::Now();
base::Time bucket_time = EventBucket(time);
// If bucket exists, it will be at the end of the list.
if (event_buckets_.empty() || event_buckets_.back().first != bucket_time) {
std::pair<base::Time, std::list<std::string>> bucket(
bucket_time, std::list<std::string>());
event_buckets_.push_back(bucket);
}
std::string timestamp = base::TimeToISO8601(time);
std::string event_log =
base::StringPrintf("%s %s", timestamp.c_str(), event.c_str());
event_buckets_.back().second.push_back(event_log);
DropOldEvents();
}
void BreadcrumbManager::DropOldEvents() {
static const base::TimeDelta kMessageExpirationTime =
base::TimeDelta::FromMinutes(20);
base::Time now = base::Time::Now();
while (event_buckets_.size() > kMinEventsBuckets) {
base::Time oldest_bucket_time = event_buckets_.front().first;
if (now - oldest_bucket_time < kMessageExpirationTime) {
break;
}
event_buckets_.pop_front();
}
}
BreadcrumbManager::BreadcrumbManager() = default;
BreadcrumbManager::~BreadcrumbManager() = default;
// Copyright 2019 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.
#ifndef IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_MANAGER_H_
#define IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_MANAGER_H_
#include <list>
#include <map>
#include <string>
#import "base/time/time.h"
// Stores events logged with |AddEvent| in memory which can later be retrieved
// with |GetEvents|. Events will be silently dropped after a certain amount of
// time has passed unless no more recent events are available. The internal
// management of events aims to keep relevant events available while clearing
// stale data.
class BreadcrumbManager {
public:
// Returns a list of the collected breadcrumb events which are still relevant
// up to |event_count_limit|. Passing zero for |event_count_limit| signifies
// no limit. Events returned will have a timestamp prepended to the original
// |event| string representing when |AddEvent| was called.
std::list<std::string> GetEvents(size_t event_count_limit);
// Logs a breadcrumb event with message data |event|.
void AddEvent(const std::string& event);
BreadcrumbManager();
virtual ~BreadcrumbManager();
private:
// Drops events which are considered stale. Note that stale events are not
// guaranteed to be removed. Explicitly, stale events will be retained while
// newer events are limited.
void DropOldEvents();
// List of events, paired with the time which they were logged to minute
// resolution. Newer events are at the end of the list.
std::list<std::pair<base::Time, std::list<std::string>>> event_buckets_;
DISALLOW_COPY_AND_ASSIGN(BreadcrumbManager);
};
#endif // IOS_CHROME_BROWSER_CRASH_REPORT_BREADCRUMBS_BREADCRUMB_MANAGER_H_
// Copyright 2019 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 "ios/chrome/browser/crash_report/breadcrumbs/breadcrumb_manager.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#include "testing/platform_test.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// Test fixture for testing BreadcrumbManager class.
class BreadcrumbManagerTest : public PlatformTest {
protected:
BreadcrumbManagerTest() {}
base::test::TaskEnvironment task_env_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
BreadcrumbManager breadcrumb_manager_;
};
// Tests that an event is logged and returned.
TEST_F(BreadcrumbManagerTest, AddEvent) {
std::string event_message = "event";
breadcrumb_manager_.AddEvent(event_message);
std::list<std::string> events = breadcrumb_manager_.GetEvents(0);
ASSERT_EQ(1ul, events.size());
// Events returned from |GetEvents| will have a timestamp prepended.
EXPECT_NE(std::string::npos, events.front().find(event_message));
}
// Tests that returned events returned by |GetEvents| are limited by the
// |event_count_limit| parameter.
TEST_F(BreadcrumbManagerTest, EventCountLimited) {
breadcrumb_manager_.AddEvent("event1");
breadcrumb_manager_.AddEvent("event2");
breadcrumb_manager_.AddEvent("event3");
breadcrumb_manager_.AddEvent("event4");
std::list<std::string> events = breadcrumb_manager_.GetEvents(2);
ASSERT_EQ(2ul, events.size());
EXPECT_NE(std::string::npos, events.front().find("event3"));
events.pop_front();
EXPECT_NE(std::string::npos, events.front().find("event4"));
}
// Tests that old events are dropped.
TEST_F(BreadcrumbManagerTest, OldEventsDropped) {
// Log an event from one and two hours ago.
breadcrumb_manager_.AddEvent("event1");
task_env_.FastForwardBy(base::TimeDelta::FromHours(1));
breadcrumb_manager_.AddEvent("event2");
task_env_.FastForwardBy(base::TimeDelta::FromHours(1));
// Log three events separated by three minutes to ensure they receive their
// own event bucket. Otherwise, some old events may be returned to ensure a
// minimum number of available events. See |MinimumEventsReturned| test below.
breadcrumb_manager_.AddEvent("event3");
task_env_.FastForwardBy(base::TimeDelta::FromMinutes(3));
breadcrumb_manager_.AddEvent("event4");
task_env_.FastForwardBy(base::TimeDelta::FromMinutes(3));
breadcrumb_manager_.AddEvent("event5");
std::list<std::string> events = breadcrumb_manager_.GetEvents(0);
ASSERT_EQ(3ul, events.size());
// Validate the three most recent events are the ones which were returned.
EXPECT_NE(std::string::npos, events.front().find("event3"));
events.pop_front();
EXPECT_NE(std::string::npos, events.front().find("event4"));
events.pop_front();
EXPECT_NE(std::string::npos, events.front().find("event5"));
}
// Tests that expired events are returned if not enough new events exist.
TEST_F(BreadcrumbManagerTest, MinimumEventsReturned) {
// Log an event from one and two hours ago.
breadcrumb_manager_.AddEvent("event1");
task_env_.FastForwardBy(base::TimeDelta::FromHours(1));
breadcrumb_manager_.AddEvent("event2");
task_env_.FastForwardBy(base::TimeDelta::FromHours(1));
breadcrumb_manager_.AddEvent("event3");
std::list<std::string> events = breadcrumb_manager_.GetEvents(0);
EXPECT_EQ(2ul, events.size());
}
...@@ -165,6 +165,7 @@ test("ios_chrome_unittests") { ...@@ -165,6 +165,7 @@ test("ios_chrome_unittests") {
"//ios/chrome/browser/browsing_data:unit_tests", "//ios/chrome/browser/browsing_data:unit_tests",
"//ios/chrome/browser/complex_tasks:unit_tests", "//ios/chrome/browser/complex_tasks:unit_tests",
"//ios/chrome/browser/crash_report:unit_tests", "//ios/chrome/browser/crash_report:unit_tests",
"//ios/chrome/browser/crash_report/breadcrumbs:unit_tests",
"//ios/chrome/browser/device_sharing:unit_tests", "//ios/chrome/browser/device_sharing:unit_tests",
"//ios/chrome/browser/download:unit_tests", "//ios/chrome/browser/download:unit_tests",
"//ios/chrome/browser/drag_and_drop:unit_tests", "//ios/chrome/browser/drag_and_drop:unit_tests",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment