Skip to content

Commit

Permalink
M100: quota: Log SQLite errors.
Browse files Browse the repository at this point in the history
Merge conflict resolution:

1) In order to get the code building in M100,
this CL brings in test helpers from the following CLs:
* https://crrev.com/c/3508287 - QuotaDatabase::CorruptForTesting()
* https://crrev.com/c/3490088 - sql::test::CorruptIndexRootPage()
  and its dependencies ReadPageSize() and GetRootPage() in
  //sql/test/test_helpers.cc's anonymous namespace

2) https://crrev.com/c/3526707 fixed the histogram name mismatch in
   histograms.xml. This merge CL uses the corrected name.

This CL adds //sql helpers for UMA logging of SQLite error codes, and
uses the helpers to log quota database errors.

(cherry picked from commit b28cb1d)

Bug: 1306332
Change-Id: Ia174596603042d1a8d3f7a29137598dbf3482d14
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3524101
Reviewed-by: Ayu Ishii <ayui@chromium.org>
Reviewed-by: Marijn Kruisselbrink <mek@chromium.org>
Commit-Queue: Victor Costan <pwnall@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#980950}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3531247
Auto-Submit: Victor Costan <pwnall@chromium.org>
Cr-Commit-Position: refs/branch-heads/4896@{#642}
Cr-Branched-From: 1f63ff4-refs/heads/main@{#972766}
  • Loading branch information
pwnall authored and Chromium LUCI CQ committed Mar 17, 2022
1 parent e2de726 commit e017485
Show file tree
Hide file tree
Showing 11 changed files with 826 additions and 0 deletions.
3 changes: 3 additions & 0 deletions sql/BUILD.gn
Expand Up @@ -12,6 +12,8 @@ component("sql") {
"database_memory_dump_provider.h",
"error_delegate_util.cc",
"error_delegate_util.h",
"error_metrics.cc",
"error_metrics.h",
"init_status.h",
"initialization.cc",
"initialization.h",
Expand Down Expand Up @@ -109,6 +111,7 @@ bundle_data("sql_unittests_bundle_data") {
test("sql_unittests") {
sources = [
"database_unittest.cc",
"error_metrics_unittest.cc",
"meta_table_unittest.cc",
"recover_module/module_unittest.cc",
"recovery_unittest.cc",
Expand Down
259 changes: 259 additions & 0 deletions sql/error_metrics.cc
@@ -0,0 +1,259 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "sql/error_metrics.h"

#include <ostream> // Needed to compile NOTREACHED() with operator <<.
#include <utility>

#include "base/check_op.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/ranges/algorithm.h"
#include "third_party/sqlite/sqlite3.h"

namespace sql {

namespace {

constexpr std::pair<int, SqliteLoggedResultCode> kResultCodeMapping[] = {
// Entries are ordered by SQLite result code value. This should match the
// ordering in https://www.sqlite.org/rescode.html.

{SQLITE_OK, SqliteLoggedResultCode::kNoError},
{SQLITE_ERROR, SqliteLoggedResultCode::kGeneric},
{SQLITE_INTERNAL, SqliteLoggedResultCode::kUnusedSqlite},
{SQLITE_PERM, SqliteLoggedResultCode::kPermission},
{SQLITE_ABORT, SqliteLoggedResultCode::kAbort},

// Chrome features shouldn't execute conflicting statements concurrently.
{SQLITE_LOCKED, SqliteLoggedResultCode::kUnusedChrome},

// Chrome should crash on OOM.
{SQLITE_NOMEM, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_READONLY, SqliteLoggedResultCode::kReadOnly},

// Chrome doesn't use sqlite3_interrupt().
{SQLITE_INTERRUPT, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_IOERR, SqliteLoggedResultCode::kIo},
{SQLITE_CORRUPT, SqliteLoggedResultCode::kCorrupt},

// Chrome only passes a few known-good opcodes to sqlite3_file_control().
{SQLITE_NOTFOUND, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_FULL, SqliteLoggedResultCode::kFullDisk},
{SQLITE_CANTOPEN, SqliteLoggedResultCode::kCantOpen},
{SQLITE_PROTOCOL, SqliteLoggedResultCode::kLockingProtocol},
{SQLITE_EMPTY, SqliteLoggedResultCode::kUnusedSqlite},
{SQLITE_SCHEMA, SqliteLoggedResultCode::kSchemaChanged},
{SQLITE_TOOBIG, SqliteLoggedResultCode::kTooBig},
{SQLITE_CONSTRAINT, SqliteLoggedResultCode::kConstraint},
{SQLITE_MISMATCH, SqliteLoggedResultCode::kTypeMismatch},

// Chrome should not misuse SQLite's API.
{SQLITE_MISUSE, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_NOLFS, SqliteLoggedResultCode::kNoLargeFileSupport},

// Chrome does not set an authorizer callback.
{SQLITE_AUTH, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_FORMAT, SqliteLoggedResultCode::kUnusedSqlite},

// Chrome should not use invalid column indexes in sqlite3_{bind,column}*().
{SQLITE_RANGE, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_NOTADB, SqliteLoggedResultCode::kNotADatabase},
{SQLITE_NOTICE, SqliteLoggedResultCode::kUnusedSqlite},
{SQLITE_WARNING, SqliteLoggedResultCode::kUnusedSqlite},
{SQLITE_ROW, SqliteLoggedResultCode::kNoError},
{SQLITE_DONE, SqliteLoggedResultCode::kNoError},
{SQLITE_OK_LOAD_PERMANENTLY, SqliteLoggedResultCode::kUnusedSqlite},

// Chrome should not use collating sequence names in SQL statements.
{SQLITE_ERROR_MISSING_COLLSEQ, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_BUSY_RECOVERY, SqliteLoggedResultCode::kBusyRecovery},

// Chrome does not use a shared page cache.
{SQLITE_LOCKED_SHAREDCACHE, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_READONLY_RECOVERY, SqliteLoggedResultCode::kReadOnlyRecovery},
{SQLITE_IOERR_READ, SqliteLoggedResultCode::kIoRead},

// Chrome does not use a virtual table that signals corruption. We only use
// a
// virtual table code for recovery. That code does not use this error.
{SQLITE_CORRUPT_VTAB, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_CANTOPEN_NOTEMPDIR, SqliteLoggedResultCode::kUnusedSqlite},
{SQLITE_CONSTRAINT_CHECK, SqliteLoggedResultCode::kConstraintCheck},

// Chrome does not set an authorizer callback.
{SQLITE_AUTH_USER, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_NOTICE_RECOVER_WAL, SqliteLoggedResultCode::kUnusedSqlite},
{SQLITE_WARNING_AUTOINDEX, SqliteLoggedResultCode::kUnusedSqlite},
{SQLITE_ERROR_RETRY, SqliteLoggedResultCode::kUnusedSqlite},
{SQLITE_ABORT_ROLLBACK, SqliteLoggedResultCode::kAbortRollback},
{SQLITE_BUSY_SNAPSHOT, SqliteLoggedResultCode::kBusySnapshot},

// Chrome does not use a virtual table that signals conflicts. We only use a
// virtual table code for recovery. That code does not use this error.
{SQLITE_LOCKED_VTAB, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_READONLY_CANTLOCK, SqliteLoggedResultCode::kReadOnlyCantLock},
{SQLITE_IOERR_SHORT_READ, SqliteLoggedResultCode::kIoShortRead},
{SQLITE_CORRUPT_SEQUENCE, SqliteLoggedResultCode::kCorruptSequence},
{SQLITE_CANTOPEN_ISDIR, SqliteLoggedResultCode::kCantOpenIsDir},

// Chrome does not use commit hook callbacks.
{SQLITE_CONSTRAINT_COMMITHOOK, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_NOTICE_RECOVER_ROLLBACK, SqliteLoggedResultCode::kUnusedSqlite},

// Chrome does not use sqlite3_snapshot_open().
{SQLITE_ERROR_SNAPSHOT, SqliteLoggedResultCode::kUnusedChrome},
#ifdef SQLITE_ENABLE_SNAPSHOT
#error "This code assumes that Chrome does not use sqlite3_snapshot_open()"
#endif

// Chrome does not use blocking Posix advisory file lock requests.
{SQLITE_ERROR_SNAPSHOT, SqliteLoggedResultCode::kUnusedChrome},
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
#error "This code assumes that Chrome does not use
#endif

{SQLITE_READONLY_ROLLBACK, SqliteLoggedResultCode::kReadOnlyRollback},
{SQLITE_IOERR_WRITE, SqliteLoggedResultCode::kIoWrite},
{SQLITE_CORRUPT_INDEX, SqliteLoggedResultCode::kCorruptIndex},

// Chrome should always pass full paths to SQLite.
{SQLITE_CANTOPEN_FULLPATH, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_CONSTRAINT_FOREIGNKEY,
SqliteLoggedResultCode::kConstraintForeignKey},
{SQLITE_READONLY_DBMOVED, SqliteLoggedResultCode::kReadOnlyDbMoved},
{SQLITE_IOERR_FSYNC, SqliteLoggedResultCode::kIoFsync},

// Chrome does not support Cygwin and does not use its VFS.
{SQLITE_CANTOPEN_CONVPATH, SqliteLoggedResultCode::kUnusedChrome},

// Chrome does not use extension functions.
{SQLITE_CONSTRAINT_FUNCTION, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_READONLY_CANTINIT, SqliteLoggedResultCode::kUnusedSqlite},
{SQLITE_IOERR_DIR_FSYNC, SqliteLoggedResultCode::kIoDirFsync},
{SQLITE_CANTOPEN_DIRTYWAL, SqliteLoggedResultCode::kUnusedSqlite},
{SQLITE_CONSTRAINT_NOTNULL, SqliteLoggedResultCode::kConstraintNotNull},
{SQLITE_READONLY_DIRECTORY, SqliteLoggedResultCode::kReadOnlyDirectory},
{SQLITE_IOERR_TRUNCATE, SqliteLoggedResultCode::kIoTruncate},

// Chrome does not use the SQLITE_OPEN_NOFOLLOW flag.
{SQLITE_CANTOPEN_SYMLINK, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_CONSTRAINT_PRIMARYKEY,
SqliteLoggedResultCode::kConstraintPrimaryKey},
{SQLITE_IOERR_FSTAT, SqliteLoggedResultCode::kIoFstat},

// Chrome unconditionally disables database triggers via
// sqlite3_db_config(SQLITE_DBCONFIG_ENABLE_TRIGGER).
{SQLITE_CONSTRAINT_TRIGGER, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_IOERR_UNLOCK, SqliteLoggedResultCode::kIoUnlock},
{SQLITE_CONSTRAINT_UNIQUE, SqliteLoggedResultCode::kConstraintUnique},
{SQLITE_IOERR_RDLOCK, SqliteLoggedResultCode::kIoReadLock},

// Chrome does not use a virtual table that signals constraints. We only use
// a virtual table code for recovery. That code does not use this error.
{SQLITE_CONSTRAINT_VTAB, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_IOERR_DELETE, SqliteLoggedResultCode::kIoDelete},
{SQLITE_CONSTRAINT_ROWID, SqliteLoggedResultCode::kConstraintRowId},
{SQLITE_IOERR_BLOCKED, SqliteLoggedResultCode::kUnusedSqlite},

// Chrome unconditionally disables database triggers via
// sqlite3_db_config(SQLITE_DBCONFIG_ENABLE_TRIGGER).
{SQLITE_CONSTRAINT_PINNED, SqliteLoggedResultCode::kUnusedChrome},

// The SQLite docus claim that this error code is "normally" converted to
// SQLITE_NOMEM. This doesn't seem 100% categorical, so we're flagging this
// as "unused in Chrome" per the same rationale as SQLITE_NOMEM.
{SQLITE_IOERR_NOMEM, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_CONSTRAINT_DATATYPE, SqliteLoggedResultCode::kConstraintDataType},
{SQLITE_IOERR_ACCESS, SqliteLoggedResultCode::kIoAccess},
{SQLITE_IOERR_CHECKRESERVEDLOCK,
SqliteLoggedResultCode::kIoCheckReservedLock},
{SQLITE_IOERR_LOCK, SqliteLoggedResultCode::kIoLock},
{SQLITE_IOERR_CLOSE, SqliteLoggedResultCode::kIoClose},
{SQLITE_IOERR_DIR_CLOSE, SqliteLoggedResultCode::kUnusedSqlite},

// Chrome will only allow enabling WAL on databases with exclusive locking.
{SQLITE_IOERR_SHMOPEN, SqliteLoggedResultCode::kUnusedChrome},

// Chrome will only allow enabling WAL on databases with exclusive locking.
{SQLITE_IOERR_SHMSIZE, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_IOERR_SHMLOCK, SqliteLoggedResultCode::kUnusedSqlite},

// Chrome will only allow enabling WAL on databases with exclusive locking.
{SQLITE_IOERR_SHMMAP, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_IOERR_SEEK, SqliteLoggedResultCode::kIoSeek},
{SQLITE_IOERR_DELETE_NOENT, SqliteLoggedResultCode::kIoDeleteNoEntry},
{SQLITE_IOERR_MMAP, SqliteLoggedResultCode::kIoMemoryMapping},
{SQLITE_IOERR_GETTEMPPATH, SqliteLoggedResultCode::kIoGetTemporaryPath},

// Chrome does not support Cygwin and does not use its VFS.
{SQLITE_IOERR_CONVPATH, SqliteLoggedResultCode::kUnusedChrome},

// Chrome does not use SQLite extensions.
{SQLITE_IOERR_VNODE, SqliteLoggedResultCode::kUnusedChrome},

// Chrome does not use SQLite extensions.
{SQLITE_IOERR_AUTH, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_IOERR_BEGIN_ATOMIC, SqliteLoggedResultCode::kIoBeginAtomic},
{SQLITE_IOERR_COMMIT_ATOMIC, SqliteLoggedResultCode::kIoCommitAtomic},
{SQLITE_IOERR_ROLLBACK_ATOMIC, SqliteLoggedResultCode::kIoRollbackAtomic},

// Chrome does not use the checksum VFS shim.
{SQLITE_IOERR_DATA, SqliteLoggedResultCode::kUnusedChrome},

{SQLITE_IOERR_CORRUPTFS, SqliteLoggedResultCode::kIoCorruptFileSystem},
};

} // namespace

SqliteLoggedResultCode CreateSqliteLoggedResultCode(int sqlite_result_code) {
const auto* mapping_it = base::ranges::find_if(
kResultCodeMapping,
[&sqlite_result_code](const std::pair<int, SqliteLoggedResultCode>& rhs) {
return sqlite_result_code == rhs.first;
});

if (mapping_it == base::ranges::end(kResultCodeMapping)) {
NOTREACHED() << "Unsupported SQLite result code: " << sqlite_result_code;
return SqliteLoggedResultCode::kUnusedChrome;
}
SqliteLoggedResultCode logged_code = mapping_it->second;

DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedSqlite)
<< "SQLite reported code marked for internal use: " << sqlite_result_code;
DCHECK_NE(logged_code, SqliteLoggedResultCode::kUnusedChrome)
<< "SQLite reported code that should never show up in Chrome: "
<< sqlite_result_code;
return logged_code;
}

void UmaHistogramSqliteResult(const char* histogram_name,
int sqlite_result_code) {
auto logged_code = CreateSqliteLoggedResultCode(sqlite_result_code);
base::UmaHistogramEnumeration(histogram_name, logged_code);
}

} // namespace sql

0 comments on commit e017485

Please sign in to comment.