DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (1aeaa33a64f9)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
/* -*- 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