Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(71)

Side by Side Diff: chrome/browser/performance_monitor/database.cc

Issue 547063003: Remove the unmaintained performance monitor. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW
« no previous file with comments | « chrome/browser/performance_monitor/database.h ('k') | chrome/browser/performance_monitor/database_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698