Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "sql/connection.h" | 5 #include "sql/connection.h" |
| 6 | 6 |
| 7 #include <string.h> | 7 #include <string.h> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/files/file_path.h" | 10 #include "base/files/file_path.h" |
| 11 #include "base/files/file_util.h" | 11 #include "base/files/file_util.h" |
| 12 #include "base/json/json_file_value_serializer.h" | |
| 12 #include "base/lazy_instance.h" | 13 #include "base/lazy_instance.h" |
| 13 #include "base/logging.h" | 14 #include "base/logging.h" |
| 14 #include "base/message_loop/message_loop.h" | 15 #include "base/message_loop/message_loop.h" |
| 15 #include "base/metrics/histogram.h" | 16 #include "base/metrics/histogram.h" |
| 16 #include "base/metrics/sparse_histogram.h" | 17 #include "base/metrics/sparse_histogram.h" |
| 17 #include "base/strings/string_split.h" | 18 #include "base/strings/string_split.h" |
| 18 #include "base/strings/string_util.h" | 19 #include "base/strings/string_util.h" |
| 19 #include "base/strings/stringprintf.h" | 20 #include "base/strings/stringprintf.h" |
| 20 #include "base/strings/utf_string_conversions.h" | 21 #include "base/strings/utf_string_conversions.h" |
| 21 #include "base/synchronization/lock.h" | 22 #include "base/synchronization/lock.h" |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 250 cache_size); | 251 cache_size); |
| 251 dump->AddScalar("schema_size", | 252 dump->AddScalar("schema_size", |
| 252 base::trace_event::MemoryAllocatorDump::kUnitsBytes, | 253 base::trace_event::MemoryAllocatorDump::kUnitsBytes, |
| 253 schema_size); | 254 schema_size); |
| 254 dump->AddScalar("statement_size", | 255 dump->AddScalar("statement_size", |
| 255 base::trace_event::MemoryAllocatorDump::kUnitsBytes, | 256 base::trace_event::MemoryAllocatorDump::kUnitsBytes, |
| 256 statement_size); | 257 statement_size); |
| 257 return true; | 258 return true; |
| 258 } | 259 } |
| 259 | 260 |
| 261 // Data is persisted in a file shared between databases in the same directory. | |
| 262 // Any error reading or writing the file is considered failure. | |
|
pkotwicz
2015/10/15 23:31:06
This seems to be covered by the comment in the .h
| |
| 263 // | |
| 264 // Top level is a dictionary storing a version number, and an array of histogram | |
| 265 // tags for databases which have been dumped. | |
|
pkotwicz
2015/10/15 23:31:06
It is unclear what "Top level" refers to.
How abou
| |
| 266 bool Connection::RegisterIntentToUpload() const { | |
| 267 static const char* kVersionKey = "version"; | |
| 268 static const char* kDiagnosticDumpsKey = "DiagnosticDumps"; | |
| 269 static int kVersion = 1; | |
| 270 | |
| 271 AssertIOAllowed(); | |
| 272 | |
| 273 if (histogram_tag_.empty()) | |
| 274 return false; | |
| 275 | |
| 276 if (!is_open()) | |
| 277 return false; | |
| 278 | |
| 279 if (in_memory_) | |
| 280 return false; | |
| 281 | |
| 282 const base::FilePath db_path = DbPath(); | |
| 283 if (db_path.empty()) | |
| 284 return false; | |
| 285 | |
| 286 // Put the collection of diagnostic data next to the databases. In most | |
| 287 // cases, this is the profile directory, but safe-browsing stores a Cookies | |
| 288 // file in the directory above the profile directory. | |
| 289 base::FilePath breadcrumb_path( | |
| 290 db_path.DirName().Append(FILE_PATH_LITERAL("sqlite-diag"))); | |
|
Scott Hess - ex-Googler
2015/10/15 21:24:44
Hmm. I am considering using this file to also tra
pkotwicz
2015/10/15 23:31:06
Fair enough
| |
| 291 | |
| 292 // Lock against multiple updates to the diagnostics file. This code should | |
| 293 // seldom be called in the first place, and when called it should seldom be | |
| 294 // called for multiple databases, and when called for multiple databases there | |
| 295 // is _probably_ something systemic wrong with the user's system. So the lock | |
| 296 // should never be contended, but when it is the database experience is | |
| 297 // already bad. | |
| 298 base::AutoLock lock(g_sqlite_init_lock.Get()); | |
| 299 | |
| 300 scoped_ptr<base::Value> root; | |
| 301 if (!base::PathExists(breadcrumb_path)) { | |
| 302 scoped_ptr<base::DictionaryValue> root_dict(new base::DictionaryValue()); | |
| 303 root_dict->SetInteger(kVersionKey, kVersion); | |
| 304 | |
| 305 scoped_ptr<base::ListValue> dumps(new base::ListValue); | |
| 306 dumps->AppendString(histogram_tag_); | |
| 307 root_dict->Set(kDiagnosticDumpsKey, dumps.Pass()); | |
| 308 | |
| 309 root = root_dict.Pass(); | |
| 310 } else { | |
| 311 // Failure to read a valid dictionary implies that something is going wrong | |
| 312 // on the system. | |
| 313 JSONFileValueDeserializer deserializer(breadcrumb_path); | |
| 314 scoped_ptr<base::Value> read_root( | |
| 315 deserializer.Deserialize(nullptr, nullptr)); | |
| 316 if (!read_root.get()) | |
| 317 return false; | |
| 318 scoped_ptr<base::DictionaryValue> root_dict = | |
| 319 base::DictionaryValue::From(read_root.Pass()); | |
| 320 if (!root_dict) | |
| 321 return false; | |
| 322 | |
| 323 // Don't upload if the version is missing or newer. | |
| 324 int version = 0; | |
| 325 if (!root_dict->GetInteger(kVersionKey, &version) || version > kVersion) | |
| 326 return false; | |
| 327 | |
| 328 base::ListValue* dumps = nullptr; | |
| 329 if (!root_dict->GetList(kDiagnosticDumpsKey, &dumps)) | |
| 330 return false; | |
| 331 | |
| 332 const size_t size = dumps->GetSize(); | |
| 333 for (size_t i = 0; i < size; ++i) { | |
| 334 std::string s; | |
| 335 | |
| 336 // Don't upload if the value isn't a string, or indicates a prior upload. | |
| 337 if (!dumps->GetString(i, &s) || s == histogram_tag_) | |
| 338 return false; | |
| 339 } | |
| 340 | |
| 341 // Record intention to proceed with upload. | |
| 342 dumps->AppendString(histogram_tag_); | |
| 343 root = root_dict.Pass(); | |
| 344 } | |
| 345 | |
| 346 const base::FilePath breadcrumb_new = | |
| 347 breadcrumb_path.AddExtension(FILE_PATH_LITERAL("new")); | |
| 348 base::DeleteFile(breadcrumb_new, false); | |
| 349 | |
| 350 // No upload if the breadcrumb file cannot be updated. | |
| 351 // TODO(shess): Consider ImportantFileWriter::WriteFileAtomically() to land | |
| 352 // the data on disk. For now, losing the data is not a big problem, so the | |
| 353 // sync overhead would probably not be worth it. | |
| 354 JSONFileValueSerializer serializer(breadcrumb_new); | |
| 355 if (!serializer.Serialize(*root)) | |
| 356 return false; | |
| 357 if (!base::PathExists(breadcrumb_new)) | |
| 358 return false; | |
| 359 if (!base::ReplaceFile(breadcrumb_new, breadcrumb_path, nullptr)) { | |
| 360 base::DeleteFile(breadcrumb_new, false); | |
| 361 return false; | |
| 362 } | |
| 363 | |
| 364 return true; | |
| 365 } | |
| 366 | |
| 260 // static | 367 // static |
| 261 void Connection::SetErrorIgnorer(Connection::ErrorIgnorerCallback* cb) { | 368 void Connection::SetErrorIgnorer(Connection::ErrorIgnorerCallback* cb) { |
| 262 CHECK(current_ignorer_cb_ == NULL); | 369 CHECK(current_ignorer_cb_ == NULL); |
| 263 current_ignorer_cb_ = cb; | 370 current_ignorer_cb_ = cb; |
| 264 } | 371 } |
| 265 | 372 |
| 266 // static | 373 // static |
| 267 void Connection::ResetErrorIgnorer() { | 374 void Connection::ResetErrorIgnorer() { |
| 268 CHECK(current_ignorer_cb_); | 375 CHECK(current_ignorer_cb_); |
| 269 current_ignorer_cb_ = NULL; | 376 current_ignorer_cb_ = NULL; |
| (...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 575 // If no changes have been made, skip flushing. This allows the first page of | 682 // If no changes have been made, skip flushing. This allows the first page of |
| 576 // the database to remain in cache across multiple reads. | 683 // the database to remain in cache across multiple reads. |
| 577 const int total_changes = sqlite3_total_changes(db_); | 684 const int total_changes = sqlite3_total_changes(db_); |
| 578 if (total_changes == total_changes_at_last_release_) | 685 if (total_changes == total_changes_at_last_release_) |
| 579 return; | 686 return; |
| 580 | 687 |
| 581 total_changes_at_last_release_ = total_changes; | 688 total_changes_at_last_release_ = total_changes; |
| 582 sqlite3_db_release_memory(db_); | 689 sqlite3_db_release_memory(db_); |
| 583 } | 690 } |
| 584 | 691 |
| 692 base::FilePath Connection::DbPath() const { | |
| 693 if (!is_open()) | |
| 694 return base::FilePath(); | |
| 695 | |
| 696 const char* path = sqlite3_db_filename(db_, "main"); | |
| 697 const base::StringPiece db_path(path); | |
| 698 #if defined(OS_WIN) | |
| 699 return base::FilePath(base::UTF8ToWide(db_path)); | |
| 700 #elif defined(OS_POSIX) | |
| 701 return base::FilePath(db_path); | |
| 702 #else | |
| 703 NOTREACHED(); | |
| 704 return base::FilePath(); | |
| 705 #endif | |
| 706 } | |
| 707 | |
| 585 void Connection::TrimMemory(bool aggressively) { | 708 void Connection::TrimMemory(bool aggressively) { |
| 586 if (!db_) | 709 if (!db_) |
| 587 return; | 710 return; |
| 588 | 711 |
| 589 // TODO(shess): investigate using sqlite3_db_release_memory() when possible. | 712 // TODO(shess): investigate using sqlite3_db_release_memory() when possible. |
| 590 int original_cache_size; | 713 int original_cache_size; |
| 591 { | 714 { |
| 592 Statement sql_get_original(GetUniqueStatement("PRAGMA cache_size")); | 715 Statement sql_get_original(GetUniqueStatement("PRAGMA cache_size")); |
| 593 if (!sql_get_original.Step()) { | 716 if (!sql_get_original.Step()) { |
| 594 DLOG(WARNING) << "Could not get cache size " << GetErrorMessage(); | 717 DLOG(WARNING) << "Could not get cache size " << GetErrorMessage(); |
| (...skipping 932 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1527 ignore_result(Execute(kNoWritableSchema)); | 1650 ignore_result(Execute(kNoWritableSchema)); |
| 1528 | 1651 |
| 1529 return ret; | 1652 return ret; |
| 1530 } | 1653 } |
| 1531 | 1654 |
| 1532 base::TimeTicks TimeSource::Now() { | 1655 base::TimeTicks TimeSource::Now() { |
| 1533 return base::TimeTicks::Now(); | 1656 return base::TimeTicks::Now(); |
| 1534 } | 1657 } |
| 1535 | 1658 |
| 1536 } // namespace sql | 1659 } // namespace sql |
| OLD | NEW |