diff options
author | eaugusti@chromium.org <eaugusti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-17 21:07:18 +0000 |
---|---|---|
committer | eaugusti@chromium.org <eaugusti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-17 21:07:18 +0000 |
commit | 9578c70a20f75ba2eaf0b78050144ff7834b3578 (patch) | |
tree | 162c2bfb466b1c5f0b4ce5d5094a8fcbe960c1cb /chrome/browser/performance_monitor | |
parent | 910f0f34f7a420a476326d3eebb26feed7d862a3 (diff) | |
download | chromium_src-9578c70a20f75ba2eaf0b78050144ff7834b3578.zip chromium_src-9578c70a20f75ba2eaf0b78050144ff7834b3578.tar.gz chromium_src-9578c70a20f75ba2eaf0b78050144ff7834b3578.tar.bz2 |
Chrome Performance Monitor Database -- Events
Extends the core performance monitor database to support adding and
querying events.
BUG=130212
Review URL: https://chromiumcodereview.appspot.com/10539140
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@142652 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/performance_monitor')
-rw-r--r-- | chrome/browser/performance_monitor/database.cc | 137 | ||||
-rw-r--r-- | chrome/browser/performance_monitor/database.h | 40 | ||||
-rw-r--r-- | chrome/browser/performance_monitor/database_unittest.cc | 124 |
3 files changed, 266 insertions, 35 deletions
diff --git a/chrome/browser/performance_monitor/database.cc b/chrome/browser/performance_monitor/database.cc index ffc37df..be8b0f2 100644 --- a/chrome/browser/performance_monitor/database.cc +++ b/chrome/browser/performance_monitor/database.cc @@ -7,9 +7,12 @@ #include "base/file_path.h" #include "base/file_util.h" #include "base/format_macros.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" #include "base/logging.h" #include "base/path_service.h" #include "base/string_number_conversions.h" +#include "base/string_split.h" #include "base/stringprintf.h" #include "base/time.h" #include "base/utf_string_conversions.h" @@ -26,6 +29,12 @@ const char kActiveIntervalDb[] = "Active Interval"; const char kMetricDb[] = "Metrics"; const char kDelimiter = '!'; +// The position of different elements in the key for the event db. +enum EventKeyPosition { + EVENT_TIME, // The time the event was generated. + EVENT_TYPE // The type of event. +}; + // If the db is quiet for this number of microseconds, then it is considered // down. const base::TimeDelta kActiveIntervalTimeout = base::TimeDelta::FromSeconds(5); @@ -34,6 +43,21 @@ const base::TimeDelta kActiveIntervalTimeout = base::TimeDelta::FromSeconds(5); std::string CreateActiveIntervalKey(const base::Time& time) { return StringPrintf("%016" PRId64, time.ToInternalValue()); } + +// Create the database key for a particular event. +std::string CreateEventKey(const base::Time& time, + performance_monitor::EventType type) { + return StringPrintf("%016" PRId64 "%c%04d", time.ToInternalValue(), + kDelimiter, static_cast<int>(type)); +} + +int EventKeyToType(const std::string& event_key) { + std::vector<std::string> split; + base::SplitString(event_key, kDelimiter, &split); + int event_type; + base::StringToInt(split[EVENT_TYPE], &event_type); + return event_type; +} } // namespace namespace performance_monitor { @@ -52,11 +76,23 @@ TimeRange::~TimeRange() { base::Time Database::SystemClock::GetTime() { return base::Time::Now(); } + +// Static +scoped_refptr<Database> Database::Create(FilePath path) { + CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + if (path.empty()) { + CHECK(PathService::Get(chrome::DIR_USER_DATA, &path)); + path = path.AppendASCII(kDbDir); + } + if (!file_util::DirectoryExists(path) && !file_util::CreateDirectory(path)) + return scoped_refptr<Database>(); + return scoped_refptr<Database>(new Database(path)); +} + bool Database::AddStateValue(const std::string& key, const std::string& value) { CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); UpdateActiveInterval(); - leveldb::Status insert_status = - state_db_->Put(write_options_, key, value); + leveldb::Status insert_status = state_db_->Put(write_options_, key, value); return insert_status.ok(); } @@ -67,16 +103,14 @@ std::string Database::GetStateValue(const std::string& key) { return result; } -void Database::Clear() { +bool Database::AddEvent(const Event& event) { CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - metric_db_.reset(); - recent_db_.reset(); - state_db_.reset(); - active_interval_db_.reset(); - // Recursively delete all the databases. - if (!file_util::Delete(path_, true /* recursive */) || - !file_util::CreateDirectory(path_)) - LOG(ERROR) << "Failed to clear the performance monitor databases."; + UpdateActiveInterval(); + std::string value; + base::JSONWriter::Write(event.data(), &value); + std::string key = CreateEventKey(event.time(), event.type()); + leveldb::Status status = event_db_->Put(write_options_, key, value); + return status.ok(); } std::vector<TimeRange> Database::GetActiveIntervals(const base::Time& start, @@ -112,10 +146,66 @@ std::vector<TimeRange> Database::GetActiveIntervals(const base::Time& start, return results; } +Database::EventList Database::GetEvents(EventType type, const base::Time& start, + const base::Time& end) { + CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + EventList events; + std::string start_key = CreateEventKey(start, EVENT_UNDEFINED); + std::string end_key = CreateEventKey(end, EVENT_UNDEFINED); + scoped_ptr<leveldb::Iterator> it(event_db_->NewIterator(read_options_)); + for (it->Seek(start_key); + it->Valid() && it->key().ToString() <= end_key; + it->Next()) { + if (type != EVENT_UNDEFINED) { + int key_type = EventKeyToType(it->key().ToString()); + if (key_type != type) + continue; + } + base::DictionaryValue* dict = NULL; + if (!base::JSONReader::Read( + it->value().ToString())->GetAsDictionary(&dict)) { + LOG(ERROR) << "Unable to convert database event to JSON."; + continue; + } + scoped_ptr<Event> event = + Event::FromValue(scoped_ptr<base::DictionaryValue>(dict)); + if (!event.get()) + continue; + events.push_back(linked_ptr<Event>(event.release())); + } + return events; +} + +Database::EventTypeSet Database::GetEventTypes(const base::Time& start, + const base::Time& end) { + CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + EventTypeSet results; + std::string start_key = CreateEventKey(start, EVENT_UNDEFINED); + std::string end_key = CreateEventKey(end, EVENT_UNDEFINED); + scoped_ptr<leveldb::Iterator> it(event_db_->NewIterator(read_options_)); + for (it->Seek(start_key); + it->Valid() && it->key().ToString() <= end_key; + it->Next()) { + int key_type = EventKeyToType(it->key().ToString()); + results.insert(static_cast<EventType>(key_type)); + } + return results; +} + Database::Database(const FilePath& path) : path_(path), read_options_(leveldb::ReadOptions()), write_options_(leveldb::WriteOptions()) { + InitDBs(); + clock_ = scoped_ptr<Clock>(new SystemClock()); +} + +Database::~Database() { + Close(); +} + +void Database::InitDBs() { + CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); leveldb::DB* new_db = NULL; leveldb::Options open_options; open_options.create_if_missing = true; @@ -133,6 +223,9 @@ Database::Database(const FilePath& path) leveldb::DB::Open(open_options, path_.AppendASCII(kMetricDb).value(), &new_db); metric_db_ = scoped_ptr<leveldb::DB>(new_db); + leveldb::DB::Open(open_options, path_.AppendASCII(kEventDb).value(), + &new_db); + event_db_ = scoped_ptr<leveldb::DB>(new_db); #elif defined(OS_WIN) leveldb::DB::Open(open_options, WideToUTF8(path_.AppendASCII(kRecentDb).value()), &new_db); @@ -147,35 +240,19 @@ Database::Database(const FilePath& path) leveldb::DB::Open(open_options, WideToUTF8(path_.AppendASCII(kMetricDb).value()), &new_db); metric_db_ = scoped_ptr<leveldb::DB>(new_db); + leveldb::DB::Open(open_options, + WideToUTF8(path_.AppendASCII(kEventDb).value()), &new_db); + event_db_ = scoped_ptr<leveldb::DB>(new_db); #endif - clock_ = scoped_ptr<Clock>(new SystemClock()); -} - -Database::~Database() { - Close(); -} - -// Static -scoped_refptr<Database> Database::Create(FilePath path) { - CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - if (path.empty()) { - CHECK(PathService::Get(chrome::DIR_USER_DATA, &path)); - path = path.AppendASCII(kDbDir); - } - if (!file_util::DirectoryExists(path) && !file_util::CreateDirectory(path)) - return scoped_refptr<Database>(); - return scoped_refptr<Database>(new Database(path)); } bool Database::Close() { CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - metric_db_.reset(); recent_db_.reset(); state_db_.reset(); active_interval_db_.reset(); start_time_key_.clear(); - return true; } diff --git a/chrome/browser/performance_monitor/database.h b/chrome/browser/performance_monitor/database.h index b938f84..b117417 100644 --- a/chrome/browser/performance_monitor/database.h +++ b/chrome/browser/performance_monitor/database.h @@ -7,14 +7,16 @@ #pragma once #include <vector> +#include <set> #include <string> #include "base/file_path.h" #include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "base/memory/scoped_vector.h" +#include "base/memory/linked_ptr.h" #include "base/time.h" +#include "chrome/browser/performance_monitor/event.h" #include "third_party/leveldatabase/src/include/leveldb/db.h" namespace performance_monitor { @@ -81,6 +83,9 @@ struct TimeRange { // Value: Statistic class Database : public base::RefCountedThreadSafe<Database> { public: + typedef std::vector<linked_ptr<Event> > EventList; + typedef std::set<EventType> EventTypeSet; + // The class that the database will use to infer time. Abstracting out the // time mechanism allows for easy testing and mock data insetion. class Clock { @@ -98,12 +103,35 @@ class Database : public base::RefCountedThreadSafe<Database> { std::string GetStateValue(const std::string& key); - // Erase everything in the database. Warning - No undo! - void Clear(); + // Add an event to the database. + bool AddEvent(const Event& event); + + // Retrieve the events from the database. These methods populate the provided + // vector, and will search on the given criteria. + EventList GetEvents(EventType type, const base::Time& start, + const base::Time& end); + + EventList GetEvents(const base::Time& start, const base::Time& end) { + return GetEvents(EVENT_UNDEFINED, start, end); + } + + EventList GetEvents(EventType type) { + return GetEvents(type, base::Time(), clock_->GetTime()); + } + + EventList GetEvents() { + return GetEvents(EVENT_UNDEFINED, base::Time(), clock_->GetTime()); + } + + EventTypeSet GetEventTypes(const base::Time& start, const base::Time& end); + + EventTypeSet GetEventTypes() { + return GetEventTypes(base::Time(), clock_->GetTime()); + } // Returns the times for which there is data in the database. std::vector<TimeRange> GetActiveIntervals(const base::Time& start, - const base::Time& end); + const base::Time& end); FilePath path() const { return path_; } @@ -128,6 +156,8 @@ class Database : public base::RefCountedThreadSafe<Database> { explicit Database(const FilePath& path); virtual ~Database(); + void InitDBs(); + bool Close(); // Mark the database as being active for the current time. @@ -152,6 +182,8 @@ class Database : public base::RefCountedThreadSafe<Database> { scoped_ptr<leveldb::DB> metric_db_; + scoped_ptr<leveldb::DB> event_db_; + leveldb::ReadOptions read_options_; leveldb::WriteOptions write_options_; diff --git a/chrome/browser/performance_monitor/database_unittest.cc b/chrome/browser/performance_monitor/database_unittest.cc index cfab99a..d54ced2 100644 --- a/chrome/browser/performance_monitor/database_unittest.cc +++ b/chrome/browser/performance_monitor/database_unittest.cc @@ -10,8 +10,12 @@ #include "base/memory/scoped_ptr.h" #include "base/time.h" #include "chrome/browser/performance_monitor/database.h" +#include "chrome/browser/performance_monitor/performance_monitor_util.h" +#include "chrome/common/extensions/extension.h" #include "testing/gtest/include/gtest/gtest.h" +using extensions::Extension; + namespace performance_monitor { // A clock that increments every access. Great for testing. @@ -31,6 +35,54 @@ class TestingClock : public Database::Clock { int64 counter_; }; +class PerformanceMonitorDatabaseEventTest : public ::testing::Test { + protected: + PerformanceMonitorDatabaseEventTest() { + clock_ = new TestingClock(); + file_util::CreateNewTempDirectory(FilePath::StringType(), &temp_path_); + db_ = Database::Create(temp_path_); + CHECK(db_.get()); + db_->set_clock(scoped_ptr<Database::Clock>(clock_)); + } + + void SetUp() { + ASSERT_TRUE(db_); + PopulateDB(); + } + + void PopulateDB() { + InitEvents(); + db_->AddEvent(*install_event_1_.get()); + db_->AddEvent(*install_event_2_.get()); + db_->AddEvent(*uninstall_event_1_.get()); + db_->AddEvent(*uninstall_event_2_.get()); + } + + scoped_refptr<Database> db_; + Database::Clock* clock_; + FilePath temp_path_; + scoped_ptr<Event> install_event_1_; + scoped_ptr<Event> install_event_2_; + scoped_ptr<Event> uninstall_event_1_; + scoped_ptr<Event> uninstall_event_2_; + + private: + void InitEvents() { + install_event_1_ = util::CreateExtensionInstallEvent( + clock_->GetTime(), "a", "extension 1", "http://foo.com", + static_cast<int>(Extension::LOAD), "0.1", "Test Test"); + install_event_2_ = util::CreateExtensionInstallEvent( + clock_->GetTime(), "b", "extension 2", "http://bar.com", + static_cast<int>(Extension::LOAD), "0.1", "Test Test"); + uninstall_event_1_ = util::CreateExtensionUninstallEvent( + clock_->GetTime(), "a", "extension 1", "http://foo.com", + static_cast<int>(Extension::LOAD), "0.1", "Test Test"); + uninstall_event_2_ = util::CreateExtensionUninstallEvent( + clock_->GetTime(), "b", "extension 2", "http://bar.com", + static_cast<int>(Extension::LOAD), "0.1", "Test Test"); + } +}; + ////// PerformanceMonitorDatabaseSetupTests //////////////////////////////////// TEST(PerformanceMonitorDatabaseSetupTest, OpenCloseTest) { FilePath alternate_path; @@ -69,11 +121,81 @@ TEST(PerformanceMonitorDatabaseSetupTest, ActiveIntervalTest) { ASSERT_TRUE(db_3); std::vector<TimeRange> active_interval = db_3->GetActiveIntervals(start_time, - end_time); + end_time); ASSERT_EQ(active_interval.size(), static_cast<size_t>(2)); ASSERT_TRUE(active_interval[0].start > start_time && active_interval[0].end < mid_time); ASSERT_TRUE(active_interval[1].start > mid_time && active_interval[1].end < end_time); } + +////// PerformanceMonitorDatabaseEventTests //////////////////////////////////// +TEST_F(PerformanceMonitorDatabaseEventTest, GetAllEventsTest) { + std::vector<linked_ptr<Event> > events = db_->GetEvents(); + ASSERT_EQ(4u, events.size()); + EXPECT_TRUE(events[0]->data()->Equals(install_event_1_->data())); + EXPECT_TRUE(events[1]->data()->Equals(install_event_2_->data())); + EXPECT_TRUE(events[2]->data()->Equals(uninstall_event_1_->data())); + EXPECT_TRUE(events[3]->data()->Equals(uninstall_event_2_->data())); +} + +TEST_F(PerformanceMonitorDatabaseEventTest, GetAllEventTypesTest) { + std::set<EventType> types = db_->GetEventTypes(); + ASSERT_EQ(2u, types.size()); + ASSERT_EQ(1u, types.count(EVENT_EXTENSION_INSTALL)); + ASSERT_EQ(1u, types.count(EVENT_EXTENSION_UNINSTALL)); +} + +TEST_F(PerformanceMonitorDatabaseEventTest, GetEventInTimeRange) { + base::Time start_time = clock_->GetTime(); + scoped_ptr<Event> crash_event = util::CreateRendererFreezeEvent( + clock_->GetTime(), "chrome://freeze"); + db_->AddEvent(*crash_event.get()); + std::vector<linked_ptr<Event> > events = + db_->GetEvents(start_time, clock_->GetTime()); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(events[0]->data()->Equals(crash_event->data())); +} + +TEST_F(PerformanceMonitorDatabaseEventTest, GetInstallEvents) { + std::vector<linked_ptr<Event> > events = + db_->GetEvents(EVENT_EXTENSION_INSTALL); + ASSERT_EQ(2u, events.size()); + EXPECT_TRUE(events[0]->data()->Equals(install_event_1_->data())); + EXPECT_TRUE(events[1]->data()->Equals(install_event_2_->data())); +} + +TEST_F(PerformanceMonitorDatabaseEventTest, GetUnusedEventType) { + std::vector<linked_ptr<Event> > events = + db_->GetEvents(EVENT_EXTENSION_UNLOAD); + ASSERT_TRUE(events.empty()); + events = db_->GetEvents(EVENT_EXTENSION_UNLOAD, + clock_->GetTime(), + clock_->GetTime()); + ASSERT_TRUE(events.empty()); +} + +TEST_F(PerformanceMonitorDatabaseEventTest, GetEventsTimeRange) { + base::Time start_time = clock_->GetTime(); + scoped_ptr<Event> new_install_event = + util::CreateExtensionInstallEvent( + clock_->GetTime(), "c", "test extension", "http://foo.com", + static_cast<int>(Extension::LOAD), "0.1", "Test Test"); + scoped_ptr<Event> new_uninstall_event = + util::CreateExtensionUninstallEvent( + clock_->GetTime(), "c", "test extension", "http://foo.com", + static_cast<int>(Extension::LOAD), "0.1", "Test Test"); + base::Time end_time = clock_->GetTime(); + db_->AddEvent(*new_install_event.get()); + db_->AddEvent(*new_uninstall_event.get()); + std::vector<linked_ptr<Event> > events = + db_->GetEvents(start_time, end_time); + ASSERT_EQ(2u, events.size()); + EXPECT_TRUE(events[0]->data()->Equals(new_install_event->data())); + EXPECT_TRUE(events[1]->data()->Equals(new_uninstall_event->data())); + events = db_->GetEvents( + EVENT_EXTENSION_INSTALL, start_time, end_time); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(events[0]->data()->Equals(new_install_event->data())); +} } // namespace performance_monitor |