summaryrefslogtreecommitdiffstats
path: root/sql
diff options
context:
space:
mode:
authorshess <shess@chromium.org>2015-12-17 14:07:26 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-17 22:08:06 +0000
commit53adf16ef292bc721a65b47628b1a3008b502363 (patch)
tree9d6021458a9c318586504faf00416e95bd447625 /sql
parent55815ba0440cc8cb1609d15c497b2bfa2b66cb87 (diff)
downloadchromium_src-53adf16ef292bc721a65b47628b1a3008b502363.zip
chromium_src-53adf16ef292bc721a65b47628b1a3008b502363.tar.gz
chromium_src-53adf16ef292bc721a65b47628b1a3008b502363.tar.bz2
[sql] Test mmap operation based on SQLite capabilities.
The mmap mitigation for bug 537742 meant that the test wasn't running in cases where mmap could potentially be enabled but wasn't enabled by default. Change the test to instead run when the platform allows SQLite mmap to be enabled. Add a test to verify that mmap cannot be enabled in cases where it is expected not to work, so that platforms must make an explicit decision about whether to allow mmap. BUG=537742, 554269 Review URL: https://codereview.chromium.org/1529693002 Cr-Commit-Position: refs/heads/master@{#365904}
Diffstat (limited to 'sql')
-rw-r--r--sql/connection_unittest.cc80
-rw-r--r--sql/sqlite_features_unittest.cc104
2 files changed, 104 insertions, 80 deletions
diff --git a/sql/connection_unittest.cc b/sql/connection_unittest.cc
index 7e9d41e..b1946e4 100644
--- a/sql/connection_unittest.cc
+++ b/sql/connection_unittest.cc
@@ -4,12 +4,10 @@
#include "base/bind.h"
#include "base/files/file_util.h"
-#include "base/files/memory_mapped_file.h"
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/metrics/statistics_recorder.h"
-#include "base/strings/stringprintf.h"
#include "base/test/histogram_tester.h"
#include "base/trace_event/process_memory_dump.h"
#include "sql/connection.h"
@@ -1302,84 +1300,6 @@ TEST_F(SQLConnectionTest, TimeUpdateTransaction) {
EXPECT_EQ(0, samples->sum());
}
-// Make sure that OS file writes to a mmap'ed file are reflected in the memory
-// mapping of a memory-mapped file. Normally SQLite writes to memory-mapped
-// files using memcpy(), which should stay consistent. Our SQLite is slightly
-// patched to mmap read only, then write using OS file writes. If the
-// memory-mapped version doesn't reflect the OS file writes, SQLite's
-// memory-mapped I/O should be disabled on this platform.
-#if !defined(MOJO_APPTEST_IMPL)
-TEST_F(SQLConnectionTest, MmapTest) {
- // Skip the test for platforms which don't enable memory-mapped I/O in SQLite,
- // or which don't even support the pragma. The former seems to apply to iOS,
- // the latter to older iOS.
- // TODO(shess): Disable test on iOS? Disable on USE_SYSTEM_SQLITE?
- {
- sql::Statement s(db().GetUniqueStatement("PRAGMA mmap_size"));
- if (!s.Step() || !s.ColumnInt64(0))
- return;
- }
-
- // The test re-uses the database file to make sure it's representative of a
- // SQLite file, but will be storing incompatible data.
- db().Close();
-
- const uint32 kFlags =
- base::File::FLAG_OPEN|base::File::FLAG_READ|base::File::FLAG_WRITE;
- char buf[4096];
-
- // Create a file with a block of '0', a block of '1', and a block of '2'.
- {
- base::File f(db_path(), kFlags);
- ASSERT_TRUE(f.IsValid());
- memset(buf, '0', sizeof(buf));
- ASSERT_EQ(f.Write(0*sizeof(buf), buf, sizeof(buf)), (int)sizeof(buf));
-
- memset(buf, '1', sizeof(buf));
- ASSERT_EQ(f.Write(1*sizeof(buf), buf, sizeof(buf)), (int)sizeof(buf));
-
- memset(buf, '2', sizeof(buf));
- ASSERT_EQ(f.Write(2*sizeof(buf), buf, sizeof(buf)), (int)sizeof(buf));
- }
-
- // mmap the file and verify that everything looks right.
- {
- base::MemoryMappedFile m;
- ASSERT_TRUE(m.Initialize(db_path()));
-
- memset(buf, '0', sizeof(buf));
- ASSERT_EQ(0, memcmp(buf, m.data() + 0*sizeof(buf), sizeof(buf)));
-
- memset(buf, '1', sizeof(buf));
- ASSERT_EQ(0, memcmp(buf, m.data() + 1*sizeof(buf), sizeof(buf)));
-
- memset(buf, '2', sizeof(buf));
- ASSERT_EQ(0, memcmp(buf, m.data() + 2*sizeof(buf), sizeof(buf)));
-
- // Scribble some '3' into the first page of the file, and verify that it
- // looks the same in the memory mapping.
- {
- base::File f(db_path(), kFlags);
- ASSERT_TRUE(f.IsValid());
- memset(buf, '3', sizeof(buf));
- ASSERT_EQ(f.Write(0*sizeof(buf), buf, sizeof(buf)), (int)sizeof(buf));
- }
- ASSERT_EQ(0, memcmp(buf, m.data() + 0*sizeof(buf), sizeof(buf)));
-
- // Repeat with a single '4' in case page-sized blocks are different.
- const size_t kOffset = 1*sizeof(buf) + 123;
- ASSERT_NE('4', m.data()[kOffset]);
- {
- base::File f(db_path(), kFlags);
- ASSERT_TRUE(f.IsValid());
- buf[0] = '4';
- ASSERT_EQ(f.Write(kOffset, buf, 1), 1);
- }
- ASSERT_EQ('4', m.data()[kOffset]);
- }
-}
-#endif
-
TEST_F(SQLConnectionTest, OnMemoryDump) {
base::trace_event::ProcessMemoryDump pmd(nullptr);
base::trace_event::MemoryDumpArgs args = {
diff --git a/sql/sqlite_features_unittest.cc b/sql/sqlite_features_unittest.cc
index 20e002d..db60072 100644
--- a/sql/sqlite_features_unittest.cc
+++ b/sql/sqlite_features_unittest.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/files/file_util.h"
+#include "base/files/memory_mapped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "sql/connection.h"
#include "sql/statement.h"
@@ -143,4 +144,107 @@ TEST_F(SQLiteFeaturesTest, ForeignKeySupport) {
EXPECT_EQ(0u, rows);
}
+#if defined(MOJO_APPTEST_IMPL) || defined(OS_IOS)
+// If the platform cannot support SQLite mmap'ed I/O, make sure SQLite isn't
+// offering to support it.
+TEST_F(SQLiteFeaturesTest, NoMmap) {
+ // For recent versions of SQLite, SQLITE_MAX_MMAP_SIZE=0 can be used to
+ // disable mmap support. Alternately, sqlite3_config() could be used. In
+ // that case, the pragma will run successfully, but the size will always be 0.
+ //
+ // The SQLite embedded in older iOS releases predates the addition of mmap
+ // support. In that case the pragma will run without error, but no results
+ // are returned when querying the value.
+ //
+ // MojoVFS implements a no-op for xFileControl(). PRAGMA mmap_size is
+ // implemented in terms of SQLITE_FCNTL_MMAP_SIZE. In that case, the pragma
+ // will succeed but with no effect.
+ ignore_result(db().Execute("PRAGMA mmap_size = 1048576"));
+ sql::Statement s(db().GetUniqueStatement("PRAGMA mmap_size"));
+ ASSERT_TRUE(!s.Step() || !s.ColumnInt64(0));
+}
+#else
+// Verify that OS file writes are reflected in the memory mapping of a
+// memory-mapped file. Normally SQLite writes to memory-mapped files using
+// memcpy(), which should stay consistent. Our SQLite is slightly patched to
+// mmap read only, then write using OS file writes. If the memory-mapped
+// version doesn't reflect the OS file writes, SQLite's memory-mapped I/O should
+// be disabled on this platform using SQLITE_MAX_MMAP_SIZE=0.
+TEST_F(SQLiteFeaturesTest, Mmap) {
+ // Try to turn on mmap'ed I/O.
+ ignore_result(db().Execute("PRAGMA mmap_size = 1048576"));
+ {
+ sql::Statement s(db().GetUniqueStatement("PRAGMA mmap_size"));
+
+#if !defined(USE_SYSTEM_SQLITE)
+ // With Chromium's version of SQLite, the setting should always be non-zero.
+ ASSERT_TRUE(s.Step());
+ ASSERT_GT(s.ColumnInt64(0), 0);
+#else
+ // With the system SQLite, don't verify underlying mmap functionality if the
+ // SQLite is too old to support mmap, or if mmap is disabled (see NoMmap
+ // test). USE_SYSTEM_SQLITE is not bundled into the NoMmap case because
+ // whether mmap is enabled or not is outside of Chromium's control.
+ if (!s.Step() || !s.ColumnInt64(0))
+ return;
+#endif
+ }
+ db().Close();
+
+ const uint32 kFlags =
+ base::File::FLAG_OPEN|base::File::FLAG_READ|base::File::FLAG_WRITE;
+ char buf[4096];
+
+ // Create a file with a block of '0', a block of '1', and a block of '2'.
+ {
+ base::File f(db_path(), kFlags);
+ ASSERT_TRUE(f.IsValid());
+ memset(buf, '0', sizeof(buf));
+ ASSERT_EQ(f.Write(0*sizeof(buf), buf, sizeof(buf)), (int)sizeof(buf));
+
+ memset(buf, '1', sizeof(buf));
+ ASSERT_EQ(f.Write(1*sizeof(buf), buf, sizeof(buf)), (int)sizeof(buf));
+
+ memset(buf, '2', sizeof(buf));
+ ASSERT_EQ(f.Write(2*sizeof(buf), buf, sizeof(buf)), (int)sizeof(buf));
+ }
+
+ // mmap the file and verify that everything looks right.
+ {
+ base::MemoryMappedFile m;
+ ASSERT_TRUE(m.Initialize(db_path()));
+
+ memset(buf, '0', sizeof(buf));
+ ASSERT_EQ(0, memcmp(buf, m.data() + 0*sizeof(buf), sizeof(buf)));
+
+ memset(buf, '1', sizeof(buf));
+ ASSERT_EQ(0, memcmp(buf, m.data() + 1*sizeof(buf), sizeof(buf)));
+
+ memset(buf, '2', sizeof(buf));
+ ASSERT_EQ(0, memcmp(buf, m.data() + 2*sizeof(buf), sizeof(buf)));
+
+ // Scribble some '3' into the first page of the file, and verify that it
+ // looks the same in the memory mapping.
+ {
+ base::File f(db_path(), kFlags);
+ ASSERT_TRUE(f.IsValid());
+ memset(buf, '3', sizeof(buf));
+ ASSERT_EQ(f.Write(0*sizeof(buf), buf, sizeof(buf)), (int)sizeof(buf));
+ }
+ ASSERT_EQ(0, memcmp(buf, m.data() + 0*sizeof(buf), sizeof(buf)));
+
+ // Repeat with a single '4' in case page-sized blocks are different.
+ const size_t kOffset = 1*sizeof(buf) + 123;
+ ASSERT_NE('4', m.data()[kOffset]);
+ {
+ base::File f(db_path(), kFlags);
+ ASSERT_TRUE(f.IsValid());
+ buf[0] = '4';
+ ASSERT_EQ(f.Write(kOffset, buf, 1), 1);
+ }
+ ASSERT_EQ('4', m.data()[kOffset]);
+ }
+}
+#endif
+
} // namespace