root/chrome/browser/performance_monitor/database.h

/* [<][>][^][v][top][bottom][index][help] */

INCLUDED FROM


// 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_

/* [<][>][^][v][top][bottom][index][help] */