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/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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |