зеркало из https://github.com/mozilla/gecko-dev.git
168 строки
5.0 KiB
C++
168 строки
5.0 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "nsDebug.h"
|
|
#include "AutoSQLiteLifetime.h"
|
|
#include "sqlite3.h"
|
|
|
|
#ifdef MOZ_STORAGE_MEMORY
|
|
# include "mozmemory.h"
|
|
# ifdef MOZ_DMD
|
|
# include "DMD.h"
|
|
# endif
|
|
|
|
namespace {
|
|
|
|
// By default, SQLite tracks the size of all its heap blocks by adding an extra
|
|
// 8 bytes at the start of the block to hold the size. Unfortunately, this
|
|
// causes a lot of 2^N-sized allocations to be rounded up by jemalloc
|
|
// allocator, wasting memory. For example, a request for 1024 bytes has 8
|
|
// bytes added, becoming a request for 1032 bytes, and jemalloc rounds this up
|
|
// to 2048 bytes, wasting 1012 bytes. (See bug 676189 for more details.)
|
|
//
|
|
// So we register jemalloc as the malloc implementation, which avoids this
|
|
// 8-byte overhead, and thus a lot of waste. This requires us to provide a
|
|
// function, sqliteMemRoundup(), which computes the actual size that will be
|
|
// allocated for a given request. SQLite uses this function before all
|
|
// allocations, and may be able to use any excess bytes caused by the rounding.
|
|
//
|
|
// Note: the wrappers for malloc, realloc and moz_malloc_usable_size are
|
|
// necessary because the sqlite_mem_methods type signatures differ slightly
|
|
// from the standard ones -- they use int instead of size_t. But we don't need
|
|
// a wrapper for free.
|
|
|
|
#ifdef MOZ_DMD
|
|
|
|
// sqlite does its own memory accounting, and we use its numbers in our memory
|
|
// reporters. But we don't want sqlite's heap blocks to show up in DMD's
|
|
// output as unreported, so we mark them as reported when they're allocated and
|
|
// mark them as unreported when they are freed.
|
|
//
|
|
// In other words, we are marking all sqlite heap blocks as reported even
|
|
// though we're not reporting them ourselves. Instead we're trusting that
|
|
// sqlite is fully and correctly accounting for all of its heap blocks via its
|
|
// own memory accounting. Well, we don't have to trust it entirely, because
|
|
// it's easy to keep track (while doing this DMD-specific marking) of exactly
|
|
// how much memory SQLite is using. And we can compare that against what
|
|
// SQLite reports it is using.
|
|
|
|
MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(SqliteMallocSizeOfOnAlloc)
|
|
MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(SqliteMallocSizeOfOnFree)
|
|
|
|
#endif
|
|
|
|
static void *sqliteMemMalloc(int n)
|
|
{
|
|
void* p = ::malloc(n);
|
|
#ifdef MOZ_DMD
|
|
gSqliteMemoryUsed += SqliteMallocSizeOfOnAlloc(p);
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
static void sqliteMemFree(void *p)
|
|
{
|
|
#ifdef MOZ_DMD
|
|
gSqliteMemoryUsed -= SqliteMallocSizeOfOnFree(p);
|
|
#endif
|
|
::free(p);
|
|
}
|
|
|
|
static void *sqliteMemRealloc(void *p, int n)
|
|
{
|
|
#ifdef MOZ_DMD
|
|
gSqliteMemoryUsed -= SqliteMallocSizeOfOnFree(p);
|
|
void *pnew = ::realloc(p, n);
|
|
if (pnew) {
|
|
gSqliteMemoryUsed += SqliteMallocSizeOfOnAlloc(pnew);
|
|
} else {
|
|
// realloc failed; undo the SqliteMallocSizeOfOnFree from above
|
|
gSqliteMemoryUsed += SqliteMallocSizeOfOnAlloc(p);
|
|
}
|
|
return pnew;
|
|
#else
|
|
return ::realloc(p, n);
|
|
#endif
|
|
}
|
|
|
|
static int sqliteMemSize(void *p)
|
|
{
|
|
return ::moz_malloc_usable_size(p);
|
|
}
|
|
|
|
static int sqliteMemRoundup(int n)
|
|
{
|
|
n = malloc_good_size(n);
|
|
|
|
// jemalloc can return blocks of size 2 and 4, but SQLite requires that all
|
|
// allocations be 8-aligned. So we round up sub-8 requests to 8. This
|
|
// wastes a small amount of memory but is obviously safe.
|
|
return n <= 8 ? 8 : n;
|
|
}
|
|
|
|
static int sqliteMemInit(void *p)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void sqliteMemShutdown(void *p)
|
|
{
|
|
}
|
|
|
|
const sqlite3_mem_methods memMethods = {
|
|
&sqliteMemMalloc,
|
|
&sqliteMemFree,
|
|
&sqliteMemRealloc,
|
|
&sqliteMemSize,
|
|
&sqliteMemRoundup,
|
|
&sqliteMemInit,
|
|
&sqliteMemShutdown,
|
|
nullptr
|
|
};
|
|
|
|
} // namespace
|
|
|
|
#endif // MOZ_STORAGE_MEMORY
|
|
|
|
namespace mozilla {
|
|
|
|
AutoSQLiteLifetime::AutoSQLiteLifetime()
|
|
{
|
|
if (++AutoSQLiteLifetime::sSingletonEnforcer != 1) {
|
|
MOZ_CRASH("multiple instances of AutoSQLiteLifetime constructed!");
|
|
}
|
|
|
|
#ifdef MOZ_STORAGE_MEMORY
|
|
sResult = ::sqlite3_config(SQLITE_CONFIG_MALLOC, &memMethods);
|
|
#else
|
|
sResult = SQLITE_OK;
|
|
#endif
|
|
|
|
if (sResult == SQLITE_OK) {
|
|
// TODO (bug 1191405): do not preallocate the connections caches until we
|
|
// have figured the impact on our consumers and memory.
|
|
sqlite3_config(SQLITE_CONFIG_PAGECACHE, NULL, 0, 0);
|
|
|
|
// Explicitly initialize sqlite3. Although this is implicitly called by
|
|
// various sqlite3 functions (and the sqlite3_open calls in our case),
|
|
// the documentation suggests calling this directly. So we do.
|
|
sResult = ::sqlite3_initialize();
|
|
}
|
|
}
|
|
|
|
AutoSQLiteLifetime::~AutoSQLiteLifetime()
|
|
{
|
|
// Shutdown the sqlite3 API. Warn if shutdown did not turn out okay, but
|
|
// there is nothing actionable we can do in that case.
|
|
sResult = ::sqlite3_shutdown();
|
|
NS_WARNING_ASSERTION(sResult == SQLITE_OK,
|
|
"sqlite3 did not shutdown cleanly.");
|
|
}
|
|
|
|
int AutoSQLiteLifetime::sSingletonEnforcer = 0;
|
|
int AutoSQLiteLifetime::sResult = SQLITE_MISUSE;
|
|
|
|
} // namespace mozilla
|