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