/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 |