| OLD | NEW |
| (Empty) |
| 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/files/file_path.h" | |
| 8 #include "base/files/file_util.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 | |
| OLD | NEW |