Upstream version 10.38.222.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / performance_monitor / database.cc
1 // Copyright (c) 2012 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
5 #include "chrome/browser/performance_monitor/database.h"
6
7 #include "base/file_util.h"
8 #include "base/files/file_path.h"
9 #include "base/json/json_reader.h"
10 #include "base/json/json_writer.h"
11 #include "base/logging.h"
12 #include "base/path_service.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/time/time.h"
17 #include "chrome/browser/performance_monitor/key_builder.h"
18 #include "chrome/common/chrome_paths.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "third_party/leveldatabase/src/include/leveldb/db.h"
21 #include "third_party/leveldatabase/src/include/leveldb/iterator.h"
22 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
23
24 namespace performance_monitor {
25 namespace {
26 const char kDbDir[] = "Performance Monitor Databases";
27 const char kRecentDb[] = "Recent Metrics";
28 const char kMaxValueDb[] = "Max Value Metrics";
29 const char kEventDb[] = "Events";
30 const char kStateDb[] = "Configuration";
31 const char kActiveIntervalDb[] = "Active Interval";
32 const char kMetricDb[] = "Metrics";
33 const double kDefaultMaxValue = 0.0;
34
35 // If the db is quiet for this number of minutes, then it is considered down.
36 const base::TimeDelta kActiveIntervalTimeout() {
37   return base::TimeDelta::FromMinutes(5);
38 }
39
40 TimeRange ActiveIntervalToTimeRange(const std::string& start_time,
41                                     const std::string& end_time) {
42   int64 start_time_int = 0;
43   int64 end_time_int = 0;
44   base::StringToInt64(start_time, &start_time_int);
45   base::StringToInt64(end_time, &end_time_int);
46   return TimeRange(base::Time::FromInternalValue(start_time_int),
47                    base::Time::FromInternalValue(end_time_int));
48 }
49
50 double StringToDouble(const std::string& s) {
51   double value = 0.0;
52   if (!base::StringToDouble(s, &value))
53     LOG(ERROR) << "Failed to convert " << s << " to double.";
54   return value;
55 }
56
57 // Returns an event from the given JSON string; the scoped_ptr will be NULL if
58 // we are unable to properly parse the JSON.
59 scoped_ptr<Event> EventFromJSON(const std::string& data) {
60   base::Value* value = base::JSONReader::Read(data);
61   base::DictionaryValue* dict = NULL;
62   if (!value || !value->GetAsDictionary(&dict))
63     return scoped_ptr<Event>();
64
65   return Event::FromValue(scoped_ptr<base::DictionaryValue>(dict));
66 }
67
68 }  // namespace
69
70 const char Database::kDatabaseSequenceToken[] =
71     "_performance_monitor_db_sequence_token_";
72
73 TimeRange::TimeRange() {
74 }
75
76 TimeRange::TimeRange(base::Time start_time, base::Time end_time)
77     : start(start_time),
78       end(end_time) {
79 }
80
81 TimeRange::~TimeRange() {
82 }
83
84 base::Time Database::SystemClock::GetTime() {
85   return base::Time::Now();
86 }
87
88 // Static
89 scoped_ptr<Database> Database::Create(base::FilePath path) {
90   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
91   if (path.empty()) {
92     CHECK(PathService::Get(chrome::DIR_USER_DATA, &path));
93     path = path.AppendASCII(kDbDir);
94   }
95   scoped_ptr<Database> database;
96   if (!base::DirectoryExists(path) && !base::CreateDirectory(path))
97     return database.Pass();
98   database.reset(new Database(path));
99
100   // If the database did not initialize correctly, return a NULL scoped_ptr.
101   if (!database->valid_)
102     database.reset();
103   return database.Pass();
104 }
105
106 bool Database::AddStateValue(const std::string& key, const std::string& value) {
107   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
108   UpdateActiveInterval();
109   leveldb::Status insert_status = state_db_->Put(write_options_, key, value);
110   return insert_status.ok();
111 }
112
113 std::string Database::GetStateValue(const std::string& key) {
114   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
115   std::string result;
116   state_db_->Get(read_options_, key, &result);
117   return result;
118 }
119
120 bool Database::AddEvent(const Event& event) {
121   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
122   UpdateActiveInterval();
123   std::string value;
124   base::JSONWriter::Write(event.data(), &value);
125   std::string key = key_builder_->CreateEventKey(event.time(), event.type());
126   leveldb::Status status = event_db_->Put(write_options_, key, value);
127   return status.ok();
128 }
129
130 std::vector<TimeRange> Database::GetActiveIntervals(const base::Time& start,
131                                                     const base::Time& end) {
132   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
133   std::vector<TimeRange> results;
134   std::string start_key = key_builder_->CreateActiveIntervalKey(start);
135   std::string end_key = key_builder_->CreateActiveIntervalKey(end);
136   scoped_ptr<leveldb::Iterator> it(active_interval_db_->NewIterator(
137         read_options_));
138   it->Seek(start_key);
139   // If the interator is valid, we check the previous value in case we jumped
140   // into the middle of an active interval. If the iterator is not valid, then
141   // the key may be in the current active interval.
142   if (it->Valid())
143     it->Prev();
144   else
145     it->SeekToLast();
146   if (it->Valid() && it->value().ToString() > start_key) {
147     results.push_back(ActiveIntervalToTimeRange(it->key().ToString(),
148                                                 it->value().ToString()));
149   }
150
151   for (it->Seek(start_key);
152        it->Valid() && it->key().ToString() < end_key;
153        it->Next()) {
154     results.push_back(ActiveIntervalToTimeRange(it->key().ToString(),
155                                                 it->value().ToString()));
156   }
157   return results;
158 }
159
160 Database::EventVector Database::GetEvents(EventType type,
161                                           const base::Time& start,
162                                           const base::Time& end) {
163   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
164   EventVector events;
165   std::string start_key =
166       key_builder_->CreateEventKey(start, EVENT_UNDEFINED);
167   std::string end_key =
168       key_builder_->CreateEventKey(end, EVENT_NUMBER_OF_EVENTS);
169   leveldb::WriteBatch invalid_entries;
170   scoped_ptr<leveldb::Iterator> it(event_db_->NewIterator(read_options_));
171   for (it->Seek(start_key);
172        it->Valid() && it->key().ToString() <= end_key;
173        it->Next()) {
174     if (type != EVENT_UNDEFINED) {
175       EventType key_type =
176           key_builder_->EventKeyToEventType(it->key().ToString());
177       if (key_type != type)
178         continue;
179     }
180     scoped_ptr<Event> event = EventFromJSON(it->value().ToString());
181     if (!event.get()) {
182       invalid_entries.Delete(it->key());
183       LOG(ERROR) << "Found invalid event in the database. JSON: '"
184                  <<  it->value().ToString()
185                  << "'. Erasing event from the database.";
186       continue;
187     }
188     events.push_back(linked_ptr<Event>(event.release()));
189   }
190   event_db_->Write(write_options_, &invalid_entries);
191   return events;
192 }
193
194 Database::EventTypeSet Database::GetEventTypes(const base::Time& start,
195                                                const base::Time& end) {
196   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
197   EventTypeSet results;
198   std::string start_key =
199       key_builder_->CreateEventKey(start, EVENT_UNDEFINED);
200   std::string end_key =
201       key_builder_->CreateEventKey(end, EVENT_NUMBER_OF_EVENTS);
202   scoped_ptr<leveldb::Iterator> it(event_db_->NewIterator(read_options_));
203   for (it->Seek(start_key);
204        it->Valid() && it->key().ToString() <= end_key;
205        it->Next()) {
206     EventType key_type =
207         key_builder_->EventKeyToEventType(it->key().ToString());
208     results.insert(key_type);
209   }
210   return results;
211 }
212
213 bool Database::AddMetric(const std::string& activity,
214                          const Metric& metric) {
215   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
216   if (!metric.IsValid()) {
217     DLOG(ERROR) << "Metric to be added is invalid. Type: " << metric.type
218                 << ", Time: " << metric.time.ToInternalValue()
219                 << ", Value: " << metric.value << ". Ignoring.";
220     return false;
221   }
222
223   UpdateActiveInterval();
224   std::string recent_key =
225       key_builder_->CreateRecentKey(metric.time, metric.type, activity);
226   std::string metric_key =
227       key_builder_->CreateMetricKey(metric.time, metric.type, activity);
228   std::string recent_map_key =
229       key_builder_->CreateRecentMapKey(metric.type, activity);
230   // Use recent_map_ to quickly find the key that must be removed.
231   RecentMap::iterator old_it = recent_map_.find(recent_map_key);
232   if (old_it != recent_map_.end())
233     recent_db_->Delete(write_options_, old_it->second);
234   recent_map_[recent_map_key] = recent_key;
235   leveldb::Status recent_status =
236       recent_db_->Put(write_options_, recent_key, metric.ValueAsString());
237   leveldb::Status metric_status =
238       metric_db_->Put(write_options_, metric_key, metric.ValueAsString());
239
240   bool max_value_success =
241       UpdateMaxValue(activity, metric.type, metric.ValueAsString());
242   return recent_status.ok() && metric_status.ok() && max_value_success;
243 }
244
245 bool Database::UpdateMaxValue(const std::string& activity,
246                               MetricType metric,
247                               const std::string& value) {
248   std::string max_value_key(
249       key_builder_->CreateMaxValueKey(metric, activity));
250   bool has_key = ContainsKey(max_value_map_, max_value_key);
251   if ((has_key && StringToDouble(value) > max_value_map_[max_value_key]) ||
252       !has_key) {
253     max_value_map_[max_value_key] = StringToDouble(value);
254     return max_value_db_->Put(write_options_, max_value_key, value).ok();
255   }
256
257   return true;
258 }
259
260 Database::MetricTypeSet Database::GetActiveMetrics(const base::Time& start,
261                                                    const base::Time& end) {
262   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
263   std::string recent_start_key = key_builder_->CreateRecentKey(
264       start, static_cast<MetricType>(0), std::string());
265   std::string recent_end_key = key_builder_->CreateRecentKey(
266       end, METRIC_NUMBER_OF_METRICS, std::string());
267   std::string recent_end_of_time_key = key_builder_->CreateRecentKey(
268       clock_->GetTime(), METRIC_NUMBER_OF_METRICS, std::string());
269
270   MetricTypeSet active_metrics;
271   // Get all the guaranteed metrics.
272   scoped_ptr<leveldb::Iterator> recent_it(
273       recent_db_->NewIterator(read_options_));
274   for (recent_it->Seek(recent_start_key);
275        recent_it->Valid() && recent_it->key().ToString() <= recent_end_key;
276        recent_it->Next()) {
277     RecentKey split_key =
278         key_builder_->SplitRecentKey(recent_it->key().ToString());
279     active_metrics.insert(split_key.type);
280   }
281   // Get all the possible metrics (metrics that may have been updated after
282   // |end|).
283   MetricTypeSet possible_metrics;
284   for (recent_it->Seek(recent_end_key);
285        recent_it->Valid() &&
286        recent_it->key().ToString() <= recent_end_of_time_key;
287        recent_it->Next()) {
288     RecentKey split_key =
289         key_builder_->SplitRecentKey(recent_it->key().ToString());
290     possible_metrics.insert(split_key.type);
291   }
292   MetricTypeSet::iterator possible_it;
293   scoped_ptr<leveldb::Iterator> metric_it(
294       metric_db_->NewIterator(read_options_));
295   for (possible_it = possible_metrics.begin();
296        possible_it != possible_metrics.end();
297        ++possible_it) {
298     std::string metric_start_key =
299         key_builder_->CreateMetricKey(start, *possible_it,std::string());
300     std::string metric_end_key =
301         key_builder_->CreateMetricKey(end, *possible_it, std::string());
302     metric_it->Seek(metric_start_key);
303     // Stats in the timerange from any activity makes the metric active.
304     if (metric_it->Valid() && metric_it->key().ToString() <= metric_end_key) {
305       active_metrics.insert(*possible_it);
306     }
307   }
308
309   return active_metrics;
310 }
311
312 std::set<std::string> Database::GetActiveActivities(MetricType metric_type,
313                                                     const base::Time& start) {
314   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
315   std::set<std::string> results;
316   std::string start_key = key_builder_->CreateRecentKey(
317       start, static_cast<MetricType>(0), std::string());
318   scoped_ptr<leveldb::Iterator> it(recent_db_->NewIterator(read_options_));
319   for (it->Seek(start_key); it->Valid(); it->Next()) {
320     RecentKey split_key =
321         key_builder_->SplitRecentKey(it->key().ToString());
322     if (split_key.type == metric_type)
323       results.insert(split_key.activity);
324   }
325   return results;
326 }
327
328 double Database::GetMaxStatsForActivityAndMetric(const std::string& activity,
329                                                  MetricType metric) {
330   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
331   std::string max_value_key(
332       key_builder_->CreateMaxValueKey(metric, activity));
333   if (ContainsKey(max_value_map_, max_value_key))
334     return max_value_map_[max_value_key];
335   return kDefaultMaxValue;
336 }
337
338 bool Database::GetRecentStatsForActivityAndMetric(const std::string& activity,
339                                                   MetricType metric_type,
340                                                   Metric* metric) {
341   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
342   std::string recent_map_key =
343       key_builder_->CreateRecentMapKey(metric_type, activity);
344   if (!ContainsKey(recent_map_, recent_map_key))
345     return false;
346   std::string recent_key = recent_map_[recent_map_key];
347
348   std::string result;
349   leveldb::Status status = recent_db_->Get(read_options_, recent_key, &result);
350   if (status.ok())
351     *metric = Metric(metric_type,
352                      key_builder_->SplitRecentKey(recent_key).time,
353                      result);
354   return status.ok();
355 }
356
357 scoped_ptr<Database::MetricVector> Database::GetStatsForActivityAndMetric(
358     const std::string& activity,
359     MetricType metric_type,
360     const base::Time& start,
361     const base::Time& end) {
362   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
363   scoped_ptr<MetricVector> results(new MetricVector());
364   std::string start_key =
365       key_builder_->CreateMetricKey(start, metric_type, activity);
366   std::string end_key =
367       key_builder_->CreateMetricKey(end, metric_type, activity);
368   leveldb::WriteBatch invalid_entries;
369   scoped_ptr<leveldb::Iterator> it(metric_db_->NewIterator(read_options_));
370   for (it->Seek(start_key);
371        it->Valid() && it->key().ToString() <= end_key;
372        it->Next()) {
373     MetricKey split_key =
374         key_builder_->SplitMetricKey(it->key().ToString());
375     if (split_key.activity == activity) {
376       Metric metric(metric_type, split_key.time, it->value().ToString());
377       if (!metric.IsValid()) {
378         invalid_entries.Delete(it->key());
379         LOG(ERROR) << "Found bad metric in the database. Type: "
380                    << metric.type << ", Time: " << metric.time.ToInternalValue()
381                    << ", Value: " << metric.value
382                    << ". Erasing metric from database.";
383         continue;
384       }
385       results->push_back(metric);
386     }
387   }
388   metric_db_->Write(write_options_, &invalid_entries);
389   return results.Pass();
390 }
391
392 Database::MetricVectorMap Database::GetStatsForMetricByActivity(
393     MetricType metric_type,
394     const base::Time& start,
395     const base::Time& end) {
396   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
397   MetricVectorMap results;
398   std::string start_key =
399       key_builder_->CreateMetricKey(start, metric_type, std::string());
400   std::string end_key =
401       key_builder_->CreateMetricKey(end, metric_type, std::string());
402   leveldb::WriteBatch invalid_entries;
403   scoped_ptr<leveldb::Iterator> it(metric_db_->NewIterator(read_options_));
404   for (it->Seek(start_key);
405        it->Valid() && it->key().ToString() <= end_key;
406        it->Next()) {
407     MetricKey split_key = key_builder_->SplitMetricKey(it->key().ToString());
408     if (!results[split_key.activity].get()) {
409       results[split_key.activity] =
410           linked_ptr<MetricVector >(new MetricVector());
411     }
412     Metric metric(metric_type, split_key.time, it->value().ToString());
413     if (!metric.IsValid()) {
414       invalid_entries.Delete(it->key());
415       LOG(ERROR) << "Found bad metric in the database. Type: "
416                  << metric.type << ", Time: " << metric.time.ToInternalValue()
417                  << ", Value: " << metric.value
418                  << ". Erasing metric from database.";
419       continue;
420     }
421     results[split_key.activity]->push_back(metric);
422   }
423   metric_db_->Write(write_options_, &invalid_entries);
424   return results;
425 }
426
427 Database::Database(const base::FilePath& path)
428     : key_builder_(new KeyBuilder()),
429       path_(path),
430       read_options_(leveldb::ReadOptions()),
431       write_options_(leveldb::WriteOptions()),
432       valid_(false) {
433   if (!InitDBs())
434     return;
435   LoadRecents();
436   LoadMaxValues();
437   clock_ = scoped_ptr<Clock>(new SystemClock());
438   valid_ = true;
439 }
440
441 Database::~Database() {
442 }
443
444 bool Database::InitDBs() {
445   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
446   leveldb::Options open_options;
447   open_options.max_open_files = 0;  // Use minimum.
448   open_options.create_if_missing = true;
449
450   // TODO (rdevlin.cronin): This code is ugly. Fix it.
451   recent_db_ = SafelyOpenDatabase(open_options,
452                                   kRecentDb,
453                                   true);  // fix if damaged
454   max_value_db_ = SafelyOpenDatabase(open_options,
455                                      kMaxValueDb,
456                                      true);  // fix if damaged
457   state_db_ = SafelyOpenDatabase(open_options,
458                                  kStateDb,
459                                  true);  // fix if damaged
460   active_interval_db_ = SafelyOpenDatabase(open_options,
461                                            kActiveIntervalDb,
462                                            true);  // fix if damaged
463   metric_db_ = SafelyOpenDatabase(open_options,
464                                   kMetricDb,
465                                   true);  // fix if damaged
466   event_db_ = SafelyOpenDatabase(open_options,
467                                  kEventDb,
468                                  true);  // fix if damaged
469   return recent_db_ && max_value_db_ && state_db_ &&
470          active_interval_db_ && metric_db_ && event_db_;
471 }
472
473 scoped_ptr<leveldb::DB> Database::SafelyOpenDatabase(
474     const leveldb::Options& options,
475     const std::string& path,
476     bool fix_if_damaged) {
477 #if defined(OS_POSIX)
478   std::string name = path_.AppendASCII(path).value();
479 #elif defined(OS_WIN)
480   std::string name = base::WideToUTF8(path_.AppendASCII(path).value());
481 #endif
482
483   leveldb::DB* database;
484   leveldb::Status status = leveldb::DB::Open(options, name, &database);
485   // If all goes well, return the database.
486   if (status.ok())
487     return scoped_ptr<leveldb::DB>(database);
488
489   // Return NULL and print the error if we either didn't find the database and
490   // don't want to create it, or if we don't want to try to fix it.
491   if ((status.IsNotFound() && !options.create_if_missing) || !fix_if_damaged) {
492     LOG(ERROR) << status.ToString();
493     return scoped_ptr<leveldb::DB>();
494   }
495   // Otherwise, we have an error (corruption, io error, or a not found error
496   // even if we tried to create it).
497   //
498   // First, we try again.
499   LOG(ERROR) << "Database error: " << status.ToString() << ". Trying again.";
500   status = leveldb::DB::Open(options, name, &database);
501   // If we fail on corruption, we can try to repair it.
502   if (status.IsCorruption()) {
503     LOG(ERROR) << "Database corrupt (second attempt). Trying to repair.";
504     status = leveldb::RepairDB(name, options);
505     // If the repair succeeds and we can open the database, return the
506     // database. Otherwise, continue on.
507     if (status.ok()) {
508       status = leveldb::DB::Open(options, name, &database);
509       if (status.ok())
510         return scoped_ptr<leveldb::DB>(database);
511     }
512     LOG(ERROR) << "Repair failed. Deleting database.";
513   }
514   // Next, try to delete and recreate the database. Return NULL if we fail
515   // on either of these steps.
516   status = leveldb::DestroyDB(name, options);
517   if (!status.ok()) {
518     LOG(ERROR) << "Failed to delete database. " << status.ToString();
519     return scoped_ptr<leveldb::DB>();
520   }
521   // If we don't have the create_if_missing option, add it (it's safe to
522   // assume this is okay, since we have permission to |fix_if_damaged|).
523   if (!options.create_if_missing) {
524     leveldb::Options create_options(options);
525     create_options.create_if_missing = true;
526     status = leveldb::DB::Open(create_options, name, &database);
527   } else {
528     status = leveldb::DB::Open(options, name, &database);
529   }
530   // There's nothing else we can try at this point.
531   if (status.ok())
532     return scoped_ptr<leveldb::DB>(database);
533   // Return the database if we succeeded, or NULL on failure.
534   LOG(ERROR) << "Failed to recreate database. " << status.ToString();
535   return scoped_ptr<leveldb::DB>();
536 }
537
538 bool Database::Close() {
539   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
540   metric_db_.reset();
541   event_db_.reset();
542   recent_db_.reset();
543   max_value_db_.reset();
544   state_db_.reset();
545   active_interval_db_.reset();
546   start_time_key_.clear();
547   return true;
548 }
549
550 void Database::LoadRecents() {
551   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
552   recent_map_.clear();
553   scoped_ptr<leveldb::Iterator> it(recent_db_->NewIterator(read_options_));
554   for (it->SeekToFirst(); it->Valid(); it->Next()) {
555     RecentKey split_key = key_builder_->SplitRecentKey(it->key().ToString());
556     recent_map_[key_builder_->
557         CreateRecentMapKey(split_key.type, split_key.activity)] =
558         it->key().ToString();
559   }
560 }
561
562 void Database::LoadMaxValues() {
563   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
564   max_value_map_.clear();
565   scoped_ptr<leveldb::Iterator> it(max_value_db_->NewIterator(read_options_));
566   for (it->SeekToFirst(); it->Valid(); it->Next()) {
567     max_value_map_[it->key().ToString()] =
568         StringToDouble(it->value().ToString());
569   }
570 }
571
572 // TODO(chebert): Only update the active interval under certian circumstances
573 // eg. every 10 times or when forced.
574 void Database::UpdateActiveInterval() {
575   CHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
576   base::Time current_time = clock_->GetTime();
577   std::string end_time;
578   // If the last update was too long ago.
579   if (start_time_key_.empty() ||
580       current_time - last_update_time_ > kActiveIntervalTimeout()) {
581     start_time_key_ = key_builder_->CreateActiveIntervalKey(current_time);
582     end_time = start_time_key_;
583   } else {
584     end_time = key_builder_->CreateActiveIntervalKey(clock_->GetTime());
585   }
586   last_update_time_ = current_time;
587   active_interval_db_->Put(write_options_, start_time_key_, end_time);
588 }
589
590 }  // namespace performance_monitor