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

Side by Side Diff: sql/connection.cc

Issue 1442753004: [sql] Validate database files before enabling memory-mapping. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 1 month 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
« no previous file with comments | « sql/connection.h ('k') | tools/metrics/histograms/histograms.xml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/debug/dump_without_crashing.h" 10 #include "base/debug/dump_without_crashing.h"
(...skipping 536 matching lines...) Expand 10 before | Expand all | Expand 10 after
547 if (rc != SQLITE_OK) 547 if (rc != SQLITE_OK)
548 return; 548 return;
549 549
550 // Don't preload more than the file contains. 550 // Don't preload more than the file contains.
551 if (preload_size > file_size) 551 if (preload_size > file_size)
552 preload_size = file_size; 552 preload_size = file_size;
553 553
554 scoped_ptr<char[]> buf(new char[page_size]); 554 scoped_ptr<char[]> buf(new char[page_size]);
555 for (sqlite3_int64 pos = 0; pos < preload_size; pos += page_size) { 555 for (sqlite3_int64 pos = 0; pos < preload_size; pos += page_size) {
556 rc = file->pMethods->xRead(file, buf.get(), page_size, pos); 556 rc = file->pMethods->xRead(file, buf.get(), page_size, pos);
557
558 // TODO(shess): Consider calling OnSqliteError().
557 if (rc != SQLITE_OK) 559 if (rc != SQLITE_OK)
558 return; 560 return;
559 } 561 }
560 } 562 }
561 563
562 // SQLite keeps unused pages associated with a connection in a cache. It asks 564 // SQLite keeps unused pages associated with a connection in a cache. It asks
563 // the cache for pages by an id, and if the page is present and the database is 565 // the cache for pages by an id, and if the page is present and the database is
564 // unchanged, it considers the content of the page valid and doesn't read it 566 // unchanged, it considers the content of the page valid and doesn't read it
565 // from disk. When memory-mapped I/O is enabled, on read SQLite uses page 567 // from disk. When memory-mapped I/O is enabled, on read SQLite uses page
566 // structures created from the memory map data before consulting the cache. On 568 // structures created from the memory map data before consulting the cache. On
(...skipping 304 matching lines...) Expand 10 before | Expand all | Expand 10 after
871 // keep close to the 2000-character size limit for dumping. 873 // keep close to the 2000-character size limit for dumping.
872 const size_t kMaxMessages = 20; 874 const size_t kMaxMessages = 20;
873 for (size_t i = 0; i < kMaxMessages && i < messages.size(); ++i) { 875 for (size_t i = 0; i < kMaxMessages && i < messages.size(); ++i) {
874 base::StringAppendF(&debug_info, "%s\n", messages[i].c_str()); 876 base::StringAppendF(&debug_info, "%s\n", messages[i].c_str());
875 } 877 }
876 } 878 }
877 879
878 return debug_info; 880 return debug_info;
879 } 881 }
880 882
883 size_t Connection::GetAppropriateMmapSize() {
884 AssertIOAllowed();
885
886 // TODO(shess): Using sql::MetaTable seems indicated, but mixing
887 // sql::MetaTable and direct access seems error-prone. It might make sense to
888 // simply integrate sql::MetaTable functionality into sql::Connection.
889
890 #if defined(OS_IOS)
891 // iOS SQLite does not support memory mapping.
892 return 0;
893 #endif
894
895 // If the database doesn't have a place to track progress, assume the worst.
896 // This will happen when new databases are created.
897 if (!DoesTableExist("meta")) {
898 RecordOneEvent(EVENT_MMAP_META_MISSING);
899 return 0;
900 }
901
902 // Key into meta table to get status from a previous run. The value
903 // represents how much data in bytes has successfully been read from the
904 // database. |kMmapFailure| indicates that there was a read error and the
905 // database should not be memory-mapped, while |kMmapSuccess| indicates that
906 // the entire file was read at some point and can be memory-mapped without
907 // constraint.
908 const char* kMmapStatusKey = "mmap_status";
909 static const sqlite3_int64 kMmapFailure = -2;
910 static const sqlite3_int64 kMmapSuccess = -1;
911
912 // Start reading from 0 unless status is found in meta table.
913 sqlite3_int64 mmap_ofs = 0;
914
915 // Retrieve the current status. It is fine for the status to be missing
916 // entirely, but any error prevents memory-mapping.
917 {
918 const char* kMmapStatusSql = "SELECT value FROM meta WHERE key = ?";
919 Statement s(GetUniqueStatement(kMmapStatusSql));
920 s.BindString(0, kMmapStatusKey);
921 if (s.Step()) {
922 mmap_ofs = s.ColumnInt64(0);
923 } else if (!s.Succeeded()) {
924 RecordOneEvent(EVENT_MMAP_META_FAILURE_READ);
925 return 0;
926 }
927 }
928
929 // Database read failed in the past, don't memory map.
930 if (mmap_ofs == kMmapFailure) {
931 RecordOneEvent(EVENT_MMAP_FAILED);
932 return 0;
933 } else if (mmap_ofs != kMmapSuccess) {
934 // Continue reading from previous offset.
935 DCHECK_GE(mmap_ofs, 0);
936
937 // TODO(shess): Could this reading code be shared with Preload()? It would
938 // require locking twice (this code wouldn't be able to access |db_size| so
939 // the helper would have to return amount read).
940
941 // Read more of the database looking for errors. The VFS interface is used
942 // to assure that the reads are valid for SQLite. |g_reads_allowed| is used
943 // to limit checking to 20MB per run of Chromium.
944 sqlite3_file* file = NULL;
945 sqlite3_int64 db_size = 0;
946 if (SQLITE_OK != GetSqlite3FileAndSize(db_, &file, &db_size)) {
947 RecordOneEvent(EVENT_MMAP_VFS_FAILURE);
948 return 0;
949 }
950
951 // Read the data left, or |g_reads_allowed|, whichever is smaller.
952 // |g_reads_allowed| limits the total amount of I/O to spend verifying data
953 // in a single Chromium run.
954 sqlite3_int64 amount = db_size - mmap_ofs;
955 if (amount < 0)
956 amount = 0;
957 if (amount > 0) {
958 base::AutoLock lock(g_sqlite_init_lock.Get());
959 static sqlite3_int64 g_reads_allowed = 20 * 1024 * 1024;
960 if (g_reads_allowed < amount)
961 amount = g_reads_allowed;
962 g_reads_allowed -= amount;
963 }
964
965 // |amount| can be <= 0 if |g_reads_allowed| ran out of quota, or if the
966 // database was truncated after a previous pass.
967 if (amount <= 0 && mmap_ofs < db_size) {
968 DCHECK_EQ(0, amount);
969 RecordOneEvent(EVENT_MMAP_SUCCESS_NO_PROGRESS);
970 } else {
971 static const int kPageSize = 4096;
972 char buf[kPageSize];
973 while (amount > 0) {
974 int rc = file->pMethods->xRead(file, buf, sizeof(buf), mmap_ofs);
975 if (rc == SQLITE_OK) {
976 mmap_ofs += sizeof(buf);
977 amount -= sizeof(buf);
978 } else if (rc == SQLITE_IOERR_SHORT_READ) {
979 // Reached EOF for a database with page size < |kPageSize|.
980 mmap_ofs = db_size;
981 break;
982 } else {
983 // TODO(shess): Consider calling OnSqliteError().
984 mmap_ofs = kMmapFailure;
985 break;
986 }
987 }
988
989 // Log these events after update to distinguish meta update failure.
990 Events event;
991 if (mmap_ofs >= db_size) {
992 mmap_ofs = kMmapSuccess;
993 event = EVENT_MMAP_SUCCESS_NEW;
994 } else if (mmap_ofs > 0) {
995 event = EVENT_MMAP_SUCCESS_PARTIAL;
996 } else {
997 DCHECK_EQ(kMmapFailure, mmap_ofs);
998 event = EVENT_MMAP_FAILED_NEW;
999 }
1000
1001 const char* kMmapUpdateStatusSql = "REPLACE INTO meta VALUES (?, ?)";
1002 Statement s(GetUniqueStatement(kMmapUpdateStatusSql));
1003 s.BindString(0, kMmapStatusKey);
1004 s.BindInt64(1, mmap_ofs);
1005 if (!s.Run()) {
1006 RecordOneEvent(EVENT_MMAP_META_FAILURE_UPDATE);
1007 return 0;
1008 }
1009
1010 RecordOneEvent(event);
1011 }
1012 }
1013
1014 if (mmap_ofs == kMmapFailure)
1015 return 0;
1016 if (mmap_ofs == kMmapSuccess)
1017 return 256 * 1024 * 1024;
1018 return mmap_ofs;
1019 }
1020
881 void Connection::TrimMemory(bool aggressively) { 1021 void Connection::TrimMemory(bool aggressively) {
882 if (!db_) 1022 if (!db_)
883 return; 1023 return;
884 1024
885 // TODO(shess): investigate using sqlite3_db_release_memory() when possible. 1025 // TODO(shess): investigate using sqlite3_db_release_memory() when possible.
886 int original_cache_size; 1026 int original_cache_size;
887 { 1027 {
888 Statement sql_get_original(GetUniqueStatement("PRAGMA cache_size")); 1028 Statement sql_get_original(GetUniqueStatement("PRAGMA cache_size"));
889 if (!sql_get_original.Step()) { 1029 if (!sql_get_original.Step()) {
890 DLOG(WARNING) << "Could not get cache size " << GetErrorMessage(); 1030 DLOG(WARNING) << "Could not get cache size " << GetErrorMessage();
(...skipping 801 matching lines...) Expand 10 before | Expand all | Expand 10 after
1692 sqlite3_int64 db_size = 0; 1832 sqlite3_int64 db_size = 0;
1693 int rc = GetSqlite3FileAndSize(db_, &file, &db_size); 1833 int rc = GetSqlite3FileAndSize(db_, &file, &db_size);
1694 if (rc == SQLITE_OK && db_size > 16 * 1024) { 1834 if (rc == SQLITE_OK && db_size > 16 * 1024) {
1695 int chunk_size = 4 * 1024; 1835 int chunk_size = 4 * 1024;
1696 if (db_size > 128 * 1024) 1836 if (db_size > 128 * 1024)
1697 chunk_size = 32 * 1024; 1837 chunk_size = 32 * 1024;
1698 sqlite3_file_control(db_, NULL, SQLITE_FCNTL_CHUNK_SIZE, &chunk_size); 1838 sqlite3_file_control(db_, NULL, SQLITE_FCNTL_CHUNK_SIZE, &chunk_size);
1699 } 1839 }
1700 1840
1701 // Enable memory-mapped access. The explicit-disable case is because SQLite 1841 // Enable memory-mapped access. The explicit-disable case is because SQLite
1702 // can be built to default-enable mmap. This value will be capped by 1842 // can be built to default-enable mmap. GetAppropriateMmapSize() calculates a
1703 // SQLITE_MAX_MMAP_SIZE, which could be different between 32-bit and 64-bit 1843 // safe range to memory-map based on past regular I/O. This value will be
1704 // platforms. 1844 // capped by SQLITE_MAX_MMAP_SIZE, which could be different between 32-bit and
1705 if (mmap_disabled_) { 1845 // 64-bit platforms.
1706 ignore_result(Execute("PRAGMA mmap_size = 0")); 1846 size_t mmap_size = mmap_disabled_ ? 0 : GetAppropriateMmapSize();
1707 } else { 1847 std::string mmap_sql =
1708 ignore_result(Execute("PRAGMA mmap_size = 268435456")); // 256MB. 1848 base::StringPrintf("PRAGMA mmap_size = %" PRIuS, mmap_size);
1709 } 1849 ignore_result(Execute(mmap_sql.c_str()));
1710 1850
1711 // Determine if memory-mapping has actually been enabled. The Execute() above 1851 // Determine if memory-mapping has actually been enabled. The Execute() above
1712 // can succeed without changing the amount mapped. 1852 // can succeed without changing the amount mapped.
1713 mmap_enabled_ = false; 1853 mmap_enabled_ = false;
1714 { 1854 {
1715 Statement s(GetUniqueStatement("PRAGMA mmap_size")); 1855 Statement s(GetUniqueStatement("PRAGMA mmap_size"));
1716 if (s.Step() && s.ColumnInt64(0) > 0) 1856 if (s.Step() && s.ColumnInt64(0) > 0)
1717 mmap_enabled_ = true; 1857 mmap_enabled_ = true;
1718 } 1858 }
1719 1859
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
1853 ignore_result(Execute(kNoWritableSchema)); 1993 ignore_result(Execute(kNoWritableSchema));
1854 1994
1855 return ret; 1995 return ret;
1856 } 1996 }
1857 1997
1858 base::TimeTicks TimeSource::Now() { 1998 base::TimeTicks TimeSource::Now() {
1859 return base::TimeTicks::Now(); 1999 return base::TimeTicks::Now();
1860 } 2000 }
1861 2001
1862 } // namespace sql 2002 } // namespace sql
OLDNEW
« no previous file with comments | « sql/connection.h ('k') | tools/metrics/histograms/histograms.xml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698