Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/xre/AutoSQLiteLifetime.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "nsDebug.h"
7
#include "AutoSQLiteLifetime.h"
8
#include "sqlite3.h"
9
10
#ifdef MOZ_STORAGE_MEMORY
11
#  include "mozmemory.h"
12
#  ifdef MOZ_DMD
13
#    include "DMD.h"
14
#  endif
15
16
namespace {
17
18
// By default, SQLite tracks the size of all its heap blocks by adding an extra
19
// 8 bytes at the start of the block to hold the size.  Unfortunately, this
20
// causes a lot of 2^N-sized allocations to be rounded up by jemalloc
21
// allocator, wasting memory.  For example, a request for 1024 bytes has 8
22
// bytes added, becoming a request for 1032 bytes, and jemalloc rounds this up
23
// to 2048 bytes, wasting 1012 bytes.  (See bug 676189 for more details.)
24
//
25
// So we register jemalloc as the malloc implementation, which avoids this
26
// 8-byte overhead, and thus a lot of waste.  This requires us to provide a
27
// function, sqliteMemRoundup(), which computes the actual size that will be
28
// allocated for a given request.  SQLite uses this function before all
29
// allocations, and may be able to use any excess bytes caused by the rounding.
30
//
31
// Note: the wrappers for malloc, realloc and moz_malloc_usable_size are
32
// necessary because the sqlite_mem_methods type signatures differ slightly
33
// from the standard ones -- they use int instead of size_t.  But we don't need
34
// a wrapper for free.
35
36
#ifdef MOZ_DMD
37
38
// sqlite does its own memory accounting, and we use its numbers in our memory
39
// reporters.  But we don't want sqlite's heap blocks to show up in DMD's
40
// output as unreported, so we mark them as reported when they're allocated and
41
// mark them as unreported when they are freed.
42
//
43
// In other words, we are marking all sqlite heap blocks as reported even
44
// though we're not reporting them ourselves.  Instead we're trusting that
45
// sqlite is fully and correctly accounting for all of its heap blocks via its
46
// own memory accounting.  Well, we don't have to trust it entirely, because
47
// it's easy to keep track (while doing this DMD-specific marking) of exactly
48
// how much memory SQLite is using.  And we can compare that against what
49
// SQLite reports it is using.
50
51
MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(SqliteMallocSizeOfOnAlloc)
52
MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(SqliteMallocSizeOfOnFree)
53
54
#endif
55
56
static void *sqliteMemMalloc(int n)
57
{
58
  void* p = ::malloc(n);
59
#ifdef MOZ_DMD
60
  gSqliteMemoryUsed += SqliteMallocSizeOfOnAlloc(p);
61
#endif
62
  return p;
63
}
64
65
static void sqliteMemFree(void *p)
66
{
67
#ifdef MOZ_DMD
68
  gSqliteMemoryUsed -= SqliteMallocSizeOfOnFree(p);
69
#endif
70
  ::free(p);
71
}
72
73
static void *sqliteMemRealloc(void *p, int n)
74
{
75
#ifdef MOZ_DMD
76
  gSqliteMemoryUsed -= SqliteMallocSizeOfOnFree(p);
77
  void *pnew = ::realloc(p, n);
78
  if (pnew) {
79
    gSqliteMemoryUsed += SqliteMallocSizeOfOnAlloc(pnew);
80
  } else {
81
    // realloc failed;  undo the SqliteMallocSizeOfOnFree from above
82
    gSqliteMemoryUsed += SqliteMallocSizeOfOnAlloc(p);
83
  }
84
  return pnew;
85
#else
86
  return ::realloc(p, n);
87
#endif
88
}
89
90
static int sqliteMemSize(void *p)
91
{
92
  return ::moz_malloc_usable_size(p);
93
}
94
95
static int sqliteMemRoundup(int n)
96
{
97
  n = malloc_good_size(n);
98
99
  // jemalloc can return blocks of size 2 and 4, but SQLite requires that all
100
  // allocations be 8-aligned.  So we round up sub-8 requests to 8.  This
101
  // wastes a small amount of memory but is obviously safe.
102
  return n <= 8 ? 8 : n;
103
}
104
105
static int sqliteMemInit(void *p)
106
{
107
  return 0;
108
}
109
110
static void sqliteMemShutdown(void *p)
111
{
112
}
113
114
const sqlite3_mem_methods memMethods = {
115
  &sqliteMemMalloc,
116
  &sqliteMemFree,
117
  &sqliteMemRealloc,
118
  &sqliteMemSize,
119
  &sqliteMemRoundup,
120
  &sqliteMemInit,
121
  &sqliteMemShutdown,
122
  nullptr
123
};
124
125
} // namespace
126
127
#endif  // MOZ_STORAGE_MEMORY
128
129
namespace mozilla {
130
131
AutoSQLiteLifetime::AutoSQLiteLifetime()
132
3
{
133
3
  if (++AutoSQLiteLifetime::sSingletonEnforcer != 1) {
134
0
    MOZ_CRASH("multiple instances of AutoSQLiteLifetime constructed!");
135
0
  }
136
3
137
#ifdef MOZ_STORAGE_MEMORY
138
  sResult = ::sqlite3_config(SQLITE_CONFIG_MALLOC, &memMethods);
139
#else
140
3
  sResult = SQLITE_OK;
141
3
#endif
142
3
143
3
  if (sResult == SQLITE_OK) {
144
3
    // TODO (bug 1191405): do not preallocate the connections caches until we
145
3
    // have figured the impact on our consumers and memory.
146
3
    sqlite3_config(SQLITE_CONFIG_PAGECACHE, NULL, 0, 0);
147
3
148
3
    // Explicitly initialize sqlite3.  Although this is implicitly called by
149
3
    // various sqlite3 functions (and the sqlite3_open calls in our case),
150
3
    // the documentation suggests calling this directly.  So we do.
151
3
    sResult = ::sqlite3_initialize();
152
3
  }
153
3
}
154
155
AutoSQLiteLifetime::~AutoSQLiteLifetime()
156
0
{
157
0
  // Shutdown the sqlite3 API.  Warn if shutdown did not turn out okay, but
158
0
  // there is nothing actionable we can do in that case.
159
0
  sResult = ::sqlite3_shutdown();
160
0
  NS_WARNING_ASSERTION(sResult == SQLITE_OK,
161
0
                       "sqlite3 did not shutdown cleanly.");
162
0
}
163
164
int AutoSQLiteLifetime::sSingletonEnforcer = 0;
165
int AutoSQLiteLifetime::sResult = SQLITE_MISUSE;
166
167
} // namespace mozilla