| // Copyright (c) 2012 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 CHROME_BROWSER_PERFORMANCE_MONITOR_DATABASE_H_ |
| #define CHROME_BROWSER_PERFORMANCE_MONITOR_DATABASE_H_ |
| |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/files/file_path.h" |
| #include "base/gtest_prod_util.h" |
| #include "base/memory/linked_ptr.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/performance_monitor/constants.h" |
| #include "chrome/browser/performance_monitor/event.h" |
| #include "chrome/browser/performance_monitor/metric.h" |
| #include "third_party/leveldatabase/src/include/leveldb/db.h" |
| |
| namespace performance_monitor { |
| |
| struct TimeRange { |
| TimeRange(); |
| TimeRange(base::Time start_time, base::Time end_time); |
| ~TimeRange(); |
| |
| base::Time start; |
| base::Time end; |
| }; |
| |
| class KeyBuilder; |
| class DatabaseTestHelper; |
| |
| // The class supporting all performance monitor storage. This class wraps |
| // multiple leveldb::DB objects. All methods must be called from a background |
| // thread. Callers should use BrowserThread::PostBlockingPoolSequencedTask using |
| // performance_monitor::kDBSequenceToken as the sequence token. |
| // |
| // Different schemas are used for the different leveldb::DB's based off of the |
| // structure of the data and the common ways that it will need to be accessed. |
| // The following specifies the schema of each type of leveldb::DB. Delimiters |
| // are denoted with a '-'. |
| // |
| // State DB: |
| // Stores information about the configuration or 'state' of the browser. Things |
| // like browser version go in here. |
| // Key: Unique Identifier |
| // Value: State Value |
| // |
| // Active Interval DB: |
| // Stores information about when there is data in the database. When the |
| // database is constructed, the time is noted as the start of the active |
| // interval. Then, every write operation the current time is marked as the end |
| // of the current active interval. If the database has no write operations for |
| // a certain amount of time, then the database is considered inactive for that |
| // time period and a new start time is noted. Having the key be the beginning |
| // of the active interval allows for efficient upserts to the current active |
| // interval. If the end of the active interval was in the key, then every update |
| // to the active interval would have to remove a key and insert a new one. |
| // Key: Beginning of ActiveInterval |
| // Value: End of ActiveInterval |
| // |
| // Event DB: |
| // Stores all events. A time and type is enough to uniquely identify an event. |
| // Using the time that the event took place as the beginning of the key allows |
| // us to efficiently answer the question: "What are all the events that took |
| // place in this time range?". |
| // Key: Time - Type |
| // Value: Event in JSON |
| // |
| // Recent DB: |
| // Stores the most recent metric statistics to go into the database. There is |
| // only ever one entry per (metric, activity) pair. |recent_map_| keeps an |
| // in-memory version of this database with a mapping from a concatenation of |
| // metric and activity to the key used in the recent db. |recent_map_| allows us |
| // to quickly find the key that must be replaced in the recent db. This |
| // database becomes useful when it is necessary to find all the active metrics |
| // within a timerange. Without it, all the metric databases would need to be |
| // searched to see if that metric is active. |
| // Key: Time - Metric - Activity |
| // Value: Statistic |
| // |
| // Max Value DB: |
| // Stores the max metric statistics that have been inserted into the database. |
| // There is only ever one entry per (metric, activity) pair. |max_value_map_| |
| // keeps an in-memory version of this database with a mapping from a |
| // concatenation of metric and activity to the max metric. |
| // Key: Metric - Activity |
| // Value: Statistic |
| // |
| // Metric DB: |
| // Stores the statistics for different metrics. Having the time before the |
| // activity ensures that the search space can only be as large as the time |
| // interval. |
| // Key: Metric - Time - Activity |
| // Value: Statistic |
| class Database { |
| public: |
| typedef std::set<EventType> EventTypeSet; |
| typedef std::vector<linked_ptr<Event> > EventVector; |
| typedef std::set<MetricType> MetricTypeSet; |
| typedef std::vector<Metric> MetricVector; |
| typedef std::map<std::string, linked_ptr<MetricVector> > MetricVectorMap; |
| |
| static const char kDatabaseSequenceToken[]; |
| |
| // 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 { |
| public: |
| Clock() {} |
| virtual ~Clock() {} |
| virtual base::Time GetTime() = 0; |
| }; |
| |
| virtual ~Database(); |
| |
| static scoped_ptr<Database> Create(base::FilePath path); |
| |
| // A "state" value is anything that can only have one value at a time, and |
| // usually describes the state of the browser eg. version. |
| bool AddStateValue(const std::string& key, const std::string& value); |
| |
| std::string GetStateValue(const std::string& key); |
| |
| // 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. |
| EventVector GetEvents(EventType type, |
| const base::Time& start, |
| const base::Time& end); |
| |
| EventVector GetEvents(const base::Time& start, const base::Time& end) { |
| return GetEvents(EVENT_UNDEFINED, start, end); |
| } |
| |
| EventVector GetEvents(EventType type) { |
| return GetEvents(type, base::Time(), clock_->GetTime()); |
| } |
| |
| EventVector 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()); |
| } |
| |
| // Add a metric instance to the database. |
| bool AddMetric(const std::string& activity, const Metric& metric); |
| |
| bool AddMetric(const Metric& metric) { |
| return AddMetric(kProcessChromeAggregate, metric); |
| } |
| |
| // Get the metrics that are active for the given process between |start| |
| // (inclusive) and |end| (exclusive). |
| MetricTypeSet GetActiveMetrics(const base::Time& start, |
| const base::Time& end); |
| |
| // Get the activities that are active for the given metric after |start|. |
| std::set<std::string> GetActiveActivities(MetricType metric_type, |
| const base::Time& start); |
| |
| // Get the max value for the given metric in the db. |
| double GetMaxStatsForActivityAndMetric(const std::string& activity, |
| MetricType metric_type); |
| double GetMaxStatsForActivityAndMetric(MetricType metric_type) { |
| return GetMaxStatsForActivityAndMetric(kProcessChromeAggregate, |
| metric_type); |
| } |
| |
| // Populate info with the most recent activity. Return false if populate |
| // was unsuccessful. |
| bool GetRecentStatsForActivityAndMetric(const std::string& activity, |
| MetricType metric_type, |
| Metric* metric); |
| |
| bool GetRecentStatsForActivityAndMetric(MetricType metric_type, |
| Metric* metric) { |
| return GetRecentStatsForActivityAndMetric(kProcessChromeAggregate, |
| metric_type, |
| metric); |
| } |
| |
| // Query given |metric_type| and |activity|. |
| scoped_ptr<MetricVector> GetStatsForActivityAndMetric( |
| const std::string& activity, |
| MetricType metric_type, |
| const base::Time& start, |
| const base::Time& end); |
| |
| scoped_ptr<MetricVector> GetStatsForActivityAndMetric( |
| MetricType metric_type, const base::Time& start, const base::Time& end) { |
| return GetStatsForActivityAndMetric(kProcessChromeAggregate, metric_type, |
| start, end); |
| } |
| |
| scoped_ptr<MetricVector> GetStatsForActivityAndMetric( |
| const std::string& activity, MetricType metric_type) { |
| return GetStatsForActivityAndMetric(activity, metric_type, base::Time(), |
| clock_->GetTime()); |
| } |
| |
| scoped_ptr<MetricVector> GetStatsForActivityAndMetric( |
| MetricType metric_type) { |
| return GetStatsForActivityAndMetric(kProcessChromeAggregate, metric_type, |
| base::Time(), clock_->GetTime()); |
| } |
| |
| // Query given |metric_type|. The returned map is keyed by activity. |
| MetricVectorMap GetStatsForMetricByActivity(MetricType metric_type, |
| const base::Time& start, |
| const base::Time& end); |
| |
| MetricVectorMap GetStatsForMetricByActivity(MetricType metric_type) { |
| return GetStatsForMetricByActivity( |
| metric_type, base::Time(), clock_->GetTime()); |
| } |
| |
| // Returns the active time intervals that overlap with the time interval |
| // defined by |start| and |end|. |
| std::vector<TimeRange> GetActiveIntervals(const base::Time& start, |
| const base::Time& end); |
| |
| base::FilePath path() const { return path_; } |
| |
| void set_clock(scoped_ptr<Clock> clock) { |
| clock_ = clock.Pass(); |
| } |
| |
| private: |
| friend class DatabaseTestHelper; |
| |
| typedef std::map<std::string, std::string> RecentMap; |
| typedef std::map<std::string, double> MaxValueMap; |
| |
| // By default, the database uses a clock that simply returns the current time. |
| class SystemClock : public Clock { |
| public: |
| SystemClock() {} |
| virtual ~SystemClock() {} |
| virtual base::Time GetTime() OVERRIDE; |
| }; |
| |
| explicit Database(const base::FilePath& path); |
| |
| bool InitDBs(); |
| |
| // Attempts to open a database, and tries to fix it if it is corrupt or |
| // damaged (if |fix_if_damaged| is true). Returns a scoped_ptr to the |
| // database on success, or NULL on failure. |
| scoped_ptr<leveldb::DB> SafelyOpenDatabase( |
| const leveldb::Options& options, |
| const std::string& path, |
| bool fix_if_damaged); |
| |
| bool Close(); |
| |
| // Load recent info from the db into recent_map_. |
| void LoadRecents(); |
| // Load max values from the db into the max_value_map_. |
| void LoadMaxValues(); |
| |
| // Mark the database as being active for the current time. |
| void UpdateActiveInterval(); |
| // Updates the max_value_map_ and max_value_db_ if the value is greater than |
| // the current max value for the given activity and metric. |
| bool UpdateMaxValue(const std::string& activity, |
| MetricType metric, |
| const std::string& value); |
| |
| scoped_ptr<KeyBuilder> key_builder_; |
| |
| // A mapping of id,metric to the last inserted key for those parameters |
| // is maintained to prevent having to search through the recent db every |
| // insert. |
| RecentMap recent_map_; |
| |
| MaxValueMap max_value_map_; |
| |
| // The directory where all the databases will reside. |
| base::FilePath path_; |
| |
| // The key for the beginning of the active interval. |
| std::string start_time_key_; |
| |
| // The last time the database had a transaction. |
| base::Time last_update_time_; |
| |
| scoped_ptr<Clock> clock_; |
| |
| scoped_ptr<leveldb::DB> recent_db_; |
| |
| scoped_ptr<leveldb::DB> max_value_db_; |
| |
| scoped_ptr<leveldb::DB> state_db_; |
| |
| scoped_ptr<leveldb::DB> active_interval_db_; |
| |
| scoped_ptr<leveldb::DB> metric_db_; |
| |
| scoped_ptr<leveldb::DB> event_db_; |
| |
| leveldb::ReadOptions read_options_; |
| leveldb::WriteOptions write_options_; |
| |
| // Indicates whether or not the database successfully initialized. If false, |
| // the Create() call will return NULL. |
| bool valid_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Database); |
| }; |
| |
| } // namespace performance_monitor |
| |
| #endif // CHROME_BROWSER_PERFORMANCE_MONITOR_DATABASE_H_ |