blob: 273bda47fefacf050058d2b4cadd022147a58bd5 [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
5#include <stdio.h>
6#include <stdlib.h>
7
8#include <set>
9
10#include "base/file_util.h"
11#include "base/logging.h"
12#include "base/path_service.h"
13#include "base/perftimer.h"
14#include "base/string_util.h"
[email protected]e14b1572008-11-25 23:29:5615#include "base/test_file_util.h"
initial.commit09911bf2008-07-26 23:55:2916#include "chrome/browser/safe_browsing/safe_browsing_database.h"
17#include "chrome/common/chrome_paths.h"
18#include "chrome/common/sqlite_compiled_statement.h"
19#include "chrome/common/sqlite_utils.h"
initial.commit09911bf2008-07-26 23:55:2920#include "testing/gtest/include/gtest/gtest.h"
21
22// These tests are slow, especially the ones that create databases. So disable
23// them by default.
24//#define SAFE_BROWSING_DATABASE_TESTS_ENABLED
25#ifdef SAFE_BROWSING_DATABASE_TESTS_ENABLED
26
27namespace {
28
29// Base class for a safebrowsing database. Derived classes can implement
30// different types of tables to compare performance characteristics.
31class Database {
32 public:
33 Database() : db_(NULL) {
34 }
35
36 ~Database() {
37 if (db_) {
38 statement_cache_.Cleanup();
39 sqlite3_close(db_);
40 db_ = NULL;
41 }
42 }
43
44 bool Init(const std::string& name, bool create) {
45 // get an empty file for the test DB
46 std::wstring filename;
47 PathService::Get(base::DIR_TEMP, &filename);
[email protected]ceeb87e2008-12-04 20:46:0648 filename.push_back(FilePath::kSeparators[0]);
initial.commit09911bf2008-07-26 23:55:2949 filename.append(ASCIIToWide(name));
50
51 if (create) {
52 DeleteFile(filename.c_str());
53 } else {
54 DLOG(INFO) << "evicting " << name << " ...";
55 file_util::EvictFileFromSystemCache(filename.c_str());
56 DLOG(INFO) << "... evicted";
57 }
58
59 if (sqlite3_open(WideToUTF8(filename).c_str(), &db_) != SQLITE_OK)
60 return false;
61
62 statement_cache_.set_db(db_);
63
64 if (!create)
65 return true;
66
67 return CreateTable();
68 }
69
70 virtual bool CreateTable() = 0;
71 virtual bool Add(int host_key, int* prefixes, int count) = 0;
72 virtual bool Read(int host_key, int* prefixes, int size, int* count) = 0;
73 virtual int Count() = 0;
74 virtual std::string GetDBSuffix() = 0;
75
76 sqlite3* db() { return db_; }
77
78 protected:
79 // The database connection.
80 sqlite3* db_;
81
82 // Cache of compiled statements for our database.
83 SqliteStatementCache statement_cache_;
84};
85
86class SimpleDatabase : public Database {
87 public:
88 virtual bool CreateTable() {
89 if (DoesSqliteTableExist(db_, "hosts"))
90 return false;
91
92 return sqlite3_exec(db_, "CREATE TABLE hosts ("
93 "host INTEGER,"
94 "prefixes BLOB)",
95 NULL, NULL, NULL) == SQLITE_OK;
96 }
97
98 virtual bool Add(int host_key, int* prefixes, int count) {
99 SQLITE_UNIQUE_STATEMENT(statement, statement_cache_,
100 "INSERT OR REPLACE INTO hosts"
101 "(host,prefixes)"
102 "VALUES (?,?)");
103 if (!statement.is_valid())
104 return false;
105
106 statement->bind_int(0, host_key);
107 statement->bind_blob(1, prefixes, count*sizeof(int));
108 return statement->step() == SQLITE_DONE;
109 }
110
111 virtual bool Read(int host_key, int* prefixes, int size, int* count) {
112 SQLITE_UNIQUE_STATEMENT(statement, statement_cache_,
113 "SELECT host, prefixes FROM hosts WHERE host=?");
114 if (!statement.is_valid())
115 return false;
116
117 statement->bind_int(0, host_key);
118
119 int rv = statement->step();
120 if (rv == SQLITE_DONE) {
121 // no hostkey found, not an error
122 *count = -1;
123 return true;
124 }
125
126 if (rv != SQLITE_ROW)
127 return false;
128
129 *count = statement->column_bytes(1);
130 if (*count > size)
131 return false;
132
133 memcpy(prefixes, statement->column_blob(0), *count);
134 return true;
135 }
136
137 int Count() {
138 SQLITE_UNIQUE_STATEMENT(statement, statement_cache_,
139 "SELECT COUNT(*) FROM hosts");
140 if (!statement.is_valid()) {
141 EXPECT_TRUE(false);
142 return -1;
143 }
144
145 if (statement->step() != SQLITE_ROW) {
146 EXPECT_TRUE(false);
147 return -1;
148 }
149
150 return statement->column_int(0);
151 }
152
153 std::string GetDBSuffix() {
154 return "Simple";
155 }
156};
157
158class IndexedDatabase : public SimpleDatabase {
159 public:
160 virtual bool CreateTable() {
161 return sqlite3_exec(db_, "CREATE TABLE hosts ("
162 "host INTEGER PRIMARY KEY,"
163 "prefixes BLOB)",
164 NULL, NULL, NULL) == SQLITE_OK;
165 }
166
167 std::string GetDBSuffix() {
168 return "Indexed";
169 }
170};
171
172class IndexedWithIDDatabase : public SimpleDatabase {
173 public:
174 virtual bool CreateTable() {
175 return sqlite3_exec(db_, "CREATE TABLE hosts ("
176 "id INTEGER PRIMARY KEY AUTOINCREMENT,"
177 "host INTEGER UNIQUE,"
178 "prefixes BLOB)",
179 NULL, NULL, NULL) == SQLITE_OK;
180 }
181
182 virtual bool Add(int host_key, int* prefixes, int count) {
183 SQLITE_UNIQUE_STATEMENT(statement, statement_cache_,
184 "INSERT OR REPLACE INTO hosts"
185 "(id,host,prefixes)"
186 "VALUES (NULL,?,?)");
187 if (!statement.is_valid())
188 return false;
189
190 statement->bind_int(0, host_key);
191 statement->bind_blob(1, prefixes, count * sizeof(int));
192 return statement->step() == SQLITE_DONE;
193 }
194
195 std::string GetDBSuffix() {
196 return "IndexedWithID";
197 }
198};
199
200}
201
202class SafeBrowsing: public testing::Test {
203 protected:
204 // Get the test parameters from the test case's name.
205 virtual void SetUp() {
206 logging::InitLogging(
207 NULL, logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
208 logging::LOCK_LOG_FILE,
209 logging::DELETE_OLD_LOG_FILE);
210
211 const testing::TestInfo* const test_info =
212 testing::UnitTest::GetInstance()->current_test_info();
213 std::string test_name = test_info->name();
214
215 TestType type;
216 if (test_name.find("Write") != std::string::npos) {
217 type = WRITE;
218 } else if (test_name.find("Read") != std::string::npos) {
219 type = READ;
220 } else {
221 type = COUNT;
222 }
223
224 if (test_name.find("IndexedWithID") != std::string::npos) {
225 db_ = new IndexedWithIDDatabase();
226 } else if (test_name.find("Indexed") != std::string::npos) {
227 db_ = new IndexedDatabase();
228 } else {
229 db_ = new SimpleDatabase();
230 }
231
232
233 char multiplier_letter = test_name[test_name.size() - 1];
234 int multiplier = 0;
235 if (multiplier_letter == 'K') {
236 multiplier = 1000;
237 } else if (multiplier_letter == 'M') {
238 multiplier = 1000000;
239 } else {
240 NOTREACHED();
241 }
242
243 size_t index = test_name.size() - 1;
244 while (index != 0 && test_name[index] != '_')
245 index--;
246
247 DCHECK(index);
248 const char* count_start = test_name.c_str() + ++index;
249 int count = atoi(count_start);
250 int size = count * multiplier;
251
252 db_name_ = StringPrintf("TestSafeBrowsing");
253 db_name_.append(count_start);
254 db_name_.append(db_->GetDBSuffix());
255
256 ASSERT_TRUE(db_->Init(db_name_, type == WRITE));
257
258 if (type == WRITE) {
259 WriteEntries(size);
260 } else if (type == READ) {
261 ReadEntries(100);
262 } else {
263 CountEntries();
264 }
265 }
266
267 virtual void TearDown() {
268 delete db_;
269 }
270
271 // This writes the given number of entries to the database.
272 void WriteEntries(int count) {
273 int prefixes[4];
274
275 SQLTransaction transaction(db_->db());
276 transaction.Begin();
277
278 int inc = kint32max / count;
279 for (int i = 0; i < count; i++) {
280 int hostkey;
281 rand_s((unsigned int*)&hostkey);
282 ASSERT_TRUE(db_->Add(hostkey, prefixes, 1));
283 }
284
285 transaction.Commit();
286 }
287
288 // Read the given number of entries from the database.
289 void ReadEntries(int count) {
290 int prefixes[4];
291
292 int64 total_ms = 0;
293
294 for (int i = 0; i < count; ++i) {
295 int key;
296 rand_s((unsigned int*)&key);
297
298 PerfTimer timer;
299
300 int read;
301 ASSERT_TRUE(db_->Read(key, prefixes, sizeof(prefixes), &read));
302
303 int64 time_ms = timer.Elapsed().InMilliseconds();
304 total_ms += time_ms;
305 DLOG(INFO) << "Read in " << time_ms << " ms.";
306 }
307
308 DLOG(INFO) << db_name_ << " read " << count << " entries in average of " <<
309 total_ms/count << " ms.";
310 }
311
312 // Counts how many entries are in the database, which effectively does a full
313 // table scan.
314 void CountEntries() {
315 PerfTimer timer;
316
317 int count = db_->Count();
318
319 DLOG(INFO) << db_name_ << " counted " << count << " entries in " <<
320 timer.Elapsed().InMilliseconds() << " ms";
321 }
322
323 enum TestType {
324 WRITE,
325 READ,
326 COUNT,
327 };
328
329 private:
330
331 Database* db_;
332 std::string db_name_;
333};
334
335TEST_F(SafeBrowsing, Write_100K) {
336}
337
338TEST_F(SafeBrowsing, Read_100K) {
339}
340
341TEST_F(SafeBrowsing, WriteIndexed_100K) {
342}
343
344TEST_F(SafeBrowsing, ReadIndexed_100K) {
345}
346
347TEST_F(SafeBrowsing, WriteIndexed_250K) {
348}
349
350TEST_F(SafeBrowsing, ReadIndexed_250K) {
351}
352
353TEST_F(SafeBrowsing, WriteIndexed_500K) {
354}
355
356TEST_F(SafeBrowsing, ReadIndexed_500K) {
357}
358
359TEST_F(SafeBrowsing, ReadIndexedWithID_250K) {
360}
361
362TEST_F(SafeBrowsing, WriteIndexedWithID_250K) {
363}
364
365TEST_F(SafeBrowsing, ReadIndexedWithID_500K) {
366}
367
368TEST_F(SafeBrowsing, WriteIndexedWithID_500K) {
369}
370
371TEST_F(SafeBrowsing, CountIndexed_250K) {
372}
373
374TEST_F(SafeBrowsing, CountIndexed_500K) {
375}
376
377TEST_F(SafeBrowsing, CountIndexedWithID_250K) {
378}
379
380TEST_F(SafeBrowsing, CountIndexedWithID_500K) {
381}
382
383
384class SafeBrowsingDatabaseTest {
385 public:
386 SafeBrowsingDatabaseTest(const std::wstring& name) {
387 logging::InitLogging(
388 NULL, logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
389 logging::LOCK_LOG_FILE,
390 logging::DELETE_OLD_LOG_FILE);
391
392 PathService::Get(base::DIR_TEMP, &filename_);
[email protected]ceeb87e2008-12-04 20:46:06393 filename_.push_back(FilePath::kSeparators[0]);
initial.commit09911bf2008-07-26 23:55:29394 filename_.append(name);
395 }
396
397 void Create(int size) {
398 DeleteFile(filename_.c_str());
399
400 SafeBrowsingDatabase database;
401 database.set_synchronous();
402 EXPECT_TRUE(database.Init(filename_));
403
404 int chunk_id = 0;
405 int total_host_keys = size;
406 int host_keys_per_chunk = 100;
407
408 std::deque<SBChunk>* chunks = new std::deque<SBChunk>;
409
410 for (int i = 0; i < total_host_keys / host_keys_per_chunk; ++i) {
411 chunks->push_back(SBChunk());
412 chunks->back().chunk_number = ++chunk_id;
413
414 for (int j = 0; j < host_keys_per_chunk; ++j) {
415 SBChunkHost host;
416 rand_s((unsigned int*)&host.host);
417 host.entry = SBEntry::Create(SBEntry::ADD_PREFIX, 2);
418 host.entry->SetPrefixAt(0, 0x2425525);
419 host.entry->SetPrefixAt(1, 0x1536366);
420
421 chunks->back().hosts.push_back(host);
422 }
423 }
424
425 database.InsertChunks("goog-malware", chunks);
426 }
427
428 void Read(bool use_bloom_filter) {
429 int keys_to_read = 500;
430 file_util::EvictFileFromSystemCache(filename_.c_str());
431
432 SafeBrowsingDatabase database;
433 database.set_synchronous();
434 EXPECT_TRUE(database.Init(filename_));
435
436 PerfTimer total_timer;
437 int64 db_ms = 0;
438 int keys_from_db = 0;
439 for (int i = 0; i < keys_to_read; ++i) {
440 int key;
441 rand_s((unsigned int*)&key);
442
443 std::string url = StringPrintf("http://www.%d.com/blah.html", key);
444
445 std::string matching_list;
446 std::vector<SBPrefix> prefix_hits;
447 GURL gurl(url);
448 if (!use_bloom_filter || database.NeedToCheckUrl(gurl)) {
449 PerfTimer timer;
450 database.ContainsUrl(gurl, &matching_list, &prefix_hits);
451
452 int64 time_ms = timer.Elapsed().InMilliseconds();
453
454 DLOG(INFO) << "Read from db in " << time_ms << " ms.";
455
456 db_ms += time_ms;
457 keys_from_db++;
458 }
459 }
460
461 int64 total_ms = total_timer.Elapsed().InMilliseconds();
462
463 DLOG(INFO) << WideToASCII(file_util::GetFilenameFromPath(filename_)) <<
464 " read " << keys_to_read << " entries in " << total_ms << " ms. " <<
465 keys_from_db << " keys were read from the db, with average read taking " <<
466 db_ms / keys_from_db << " ms";
467 }
468
469 void BuildBloomFilter() {
470 file_util::EvictFileFromSystemCache(filename_.c_str());
471 file_util::Delete(SafeBrowsingDatabase::BloomFilterFilename(filename_), false);
472
473 PerfTimer total_timer;
474
475 SafeBrowsingDatabase database;
476 database.set_synchronous();
477 EXPECT_TRUE(database.Init(filename_));
478
479 int64 total_ms = total_timer.Elapsed().InMilliseconds();
480
481 DLOG(INFO) << WideToASCII(file_util::GetFilenameFromPath(filename_)) <<
482 " built bloom filter in " << total_ms << " ms.";
483 }
484
485 private:
486 std::wstring filename_;
487};
488
489// Adds 100K host records.
490TEST(SafeBrowsingDatabase, FillUp100K) {
491 SafeBrowsingDatabaseTest db(L"SafeBrowsing100K");
492 db.Create(100000);
493}
494
495// Adds 250K host records.
496TEST(SafeBrowsingDatabase, FillUp250K) {
497 SafeBrowsingDatabaseTest db(L"SafeBrowsing250K");
498 db.Create(250000);
499}
500
501// Adds 500K host records.
502TEST(SafeBrowsingDatabase, FillUp500K) {
503 SafeBrowsingDatabaseTest db(L"SafeBrowsing500K");
504 db.Create(500000);
505}
506
507// Reads 500 entries and prints the timing.
508TEST(SafeBrowsingDatabase, ReadFrom250K) {
509 SafeBrowsingDatabaseTest db(L"SafeBrowsing250K");
510 db.Read(false);
511}
512
513TEST(SafeBrowsingDatabase, ReadFrom500K) {
514 SafeBrowsingDatabaseTest db(L"SafeBrowsing500K");
515 db.Read(false);
516}
517
518// Read 500 entries with a bloom filter and print the timing.
519TEST(SafeBrowsingDatabase, BloomReadFrom250K) {
520 SafeBrowsingDatabaseTest db(L"SafeBrowsing250K");
521 db.Read(true);
522}
523
524TEST(SafeBrowsingDatabase, BloomReadFrom500K) {
525 SafeBrowsingDatabaseTest db(L"SafeBrowsing500K");
526 db.Read(true);
527}
528
529// Test how long bloom filter creation takes.
530TEST(SafeBrowsingDatabase, BuildBloomFilter250K) {
531 SafeBrowsingDatabaseTest db(L"SafeBrowsing250K");
532 db.BuildBloomFilter();
533}
534
535TEST(SafeBrowsingDatabase, BuildBloomFilter500K) {
536 SafeBrowsingDatabaseTest db(L"SafeBrowsing500K");
537 db.BuildBloomFilter();
538}
539
license.botbf09a502008-08-24 00:55:55540#endif