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

Side by Side Diff: sql/connection.cc

Issue 1393393007: [sql] Track uploads of diagnostic data to prevent duplication. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: missing FILE_PATH_LITERAL Created 5 years, 2 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
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 1504 matching lines...) Expand 10 before | Expand all | Expand 10 after
1526 const char kNoWritableSchema[] = "PRAGMA writable_schema = OFF"; 1527 const char kNoWritableSchema[] = "PRAGMA writable_schema = OFF";
1527 ignore_result(Execute(kNoWritableSchema)); 1528 ignore_result(Execute(kNoWritableSchema));
1528 1529
1529 return ret; 1530 return ret;
1530 } 1531 }
1531 1532
1532 base::TimeTicks TimeSource::Now() { 1533 base::TimeTicks TimeSource::Now() {
1533 return base::TimeTicks::Now(); 1534 return base::TimeTicks::Now();
1534 } 1535 }
1535 1536
1537 base::FilePath Connection::DbPath() const {
1538 if (!is_open())
1539 return base::FilePath();
1540
1541 const char* path = sqlite3_db_filename(db_, "main");
1542 const base::StringPiece db_path(path);
1543 #if defined(OS_WIN)
1544 return base::FilePath(base::UTF8ToWide(db_path));
1545 #elif defined(OS_POSIX)
1546 return base::FilePath(db_path);
1547 #else
1548 NOTREACHED();
1549 return base::FilePath();
1550 #endif
1551 }
1552
1553 // Decide whether to upload based on a shared breadcrumb file in the database
1554 // directory. The breadcrumb file records whether a past upload has occurred,
1555 // and also acts as a probe to determine if basic filesystem actions work in the
1556 // profile directory.
1557 //
1558 // Top level is a dictionary storing a version number, and an array of databases
1559 // which have been dumped.
1560 bool Connection::ShouldUploadDiagnosticDump() const {
pkotwicz 2015/10/15 18:02:29 Nit: Can you please move this function so that it
Scott Hess - ex-Googler 2015/10/15 21:12:20 I can put it after OnMemoryDump(), but the functio
1561 static const char* kVersionKey = "version";
1562 static const char* kDiagnosticDumpsKey = "DiagnosticDumps";
1563 static int kVersion = 1;
1564
1565 AssertIOAllowed();
1566
1567 if (histogram_tag_.empty())
1568 return false;
1569
1570 if (!is_open())
1571 return false;
1572
1573 if (in_memory_)
1574 return false;
1575
1576 const base::FilePath db_path = DbPath();
1577 if (db_path.empty())
1578 return false;
1579
1580 // Put the collection of diagnostic data next to the databases. In most
1581 // cases, this is the profile directory, but safe-browsing stores a Cookies
1582 // file in the directory above the profile directory.
1583 base::FilePath breadcrumb_path(
1584 db_path.DirName().Append(FILE_PATH_LITERAL("sqlite-diag")));
1585
1586 // Lock against multiple updates to the diagnostics file. This code should
1587 // seldom be called in the first place, and when called it should seldom be
1588 // called for multiple databases, and when called for multiple databases there
1589 // is _probably_ something systemic wrong with the user's system. So the lock
1590 // should never be contended, but when it is the database experience is
1591 // already bad.
1592 base::AutoLock lock(g_sqlite_init_lock.Get());
1593
1594 scoped_ptr<base::Value> root;
1595 if (!base::PathExists(breadcrumb_path)) {
1596 scoped_ptr<base::DictionaryValue> root_dict(new base::DictionaryValue());
1597 root_dict->SetInteger(kVersionKey, kVersion);
1598
1599 scoped_ptr<base::ListValue> dumps(new base::ListValue);
1600 dumps->AppendString(histogram_tag_);
1601 root_dict->Set(kDiagnosticDumpsKey, dumps.Pass());
1602
1603 root = root_dict.Pass();
1604 } else {
1605 // Failure to read a valid dictionary implies that something is going wrong
1606 // on the system.
1607 JSONFileValueDeserializer deserializer(breadcrumb_path);
1608 scoped_ptr<base::Value> read_root(deserializer.Deserialize(NULL, NULL));
pkotwicz 2015/10/15 18:02:30 Nit: NULL -> nullptr
Scott Hess - ex-Googler 2015/10/15 21:12:21 Acknowledged.
1609 if (!read_root.get())
1610 return false;
1611 scoped_ptr<base::DictionaryValue> root_dict =
1612 base::DictionaryValue::From(read_root.Pass());
1613 if (!root_dict)
1614 return false;
1615
1616 // Don't upload if the version is missing or newer.
1617 int version = 0;
1618 if (!root_dict->GetInteger(kVersionKey, &version) || version > kVersion)
1619 return false;
1620
1621 base::ListValue* dumps = NULL;
pkotwicz 2015/10/15 18:02:29 Nit: NULL -> nullptr
Scott Hess - ex-Googler 2015/10/15 21:12:21 Acknowledged.
1622 if (!root_dict->GetList(kDiagnosticDumpsKey, &dumps))
1623 return false;
1624
1625 const size_t size = dumps->GetSize();
1626 for (size_t i = 0; i < size; ++i) {
1627 std::string s;
1628
1629 // Don't upload if the value isn't a string, or indicates a prior upload.
1630 if (!dumps->GetString(i, &s) || s == histogram_tag_)
1631 return false;
1632 }
1633
1634 // Record intention to proceed with upload.
1635 dumps->AppendString(histogram_tag_);
1636 root = root_dict.Pass();
1637 }
1638
1639 const base::FilePath breadcrumb_new =
1640 breadcrumb_path.AddExtension(FILE_PATH_LITERAL("new"));
pkotwicz 2015/10/15 18:02:29 Should we use base::CreateTemporaryFile() instead?
Scott Hess - ex-Googler 2015/10/15 21:12:20 I'll assume you intended CreateTemporaryFileInDir(
pkotwicz 2015/10/15 23:31:06 Fair enough
1641 base::DeleteFile(breadcrumb_new, false);
1642
1643 // No upload if the breadcrumb file cannot be updated.
1644 // TODO(shess): Consider ImportantFileWriter::WriteFileAtomically() to land
1645 // the data on disk. For now, losing the data is not a big problem, so the
1646 // sync overhead would probably not be worth it.
1647 JSONFileValueSerializer serializer(breadcrumb_new);
1648 if (!serializer.Serialize(*root))
1649 return false;
1650 if (!base::PathExists(breadcrumb_new))
1651 return false;
1652 if (!base::ReplaceFile(breadcrumb_new, breadcrumb_path, nullptr)) {
1653 base::DeleteFile(breadcrumb_new, false);
1654 return false;
1655 }
1656
1657 return true;
1658 }
1659
1536 } // namespace sql 1660 } // namespace sql
OLDNEW
« sql/connection.h ('K') | « sql/connection.h ('k') | sql/connection_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698