Bug 1509459 - Get the flexbox highlighter state if the highlighter is ready in the...
[gecko.git] / storage / TelemetryVFS.cpp
blobf500a13986194b9c021dfd200e2c1db871041c09
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include <string.h>
8 #include "mozilla/Telemetry.h"
9 #include "mozilla/Preferences.h"
10 #include "sqlite3.h"
11 #include "nsThreadUtils.h"
12 #include "mozilla/dom/quota/PersistenceType.h"
13 #include "mozilla/dom/quota/QuotaManager.h"
14 #include "mozilla/dom/quota/QuotaObject.h"
15 #include "mozilla/net/IOActivityMonitor.h"
16 #include "mozilla/IOInterposer.h"
18 // The last VFS version for which this file has been updated.
19 #define LAST_KNOWN_VFS_VERSION 3
21 // The last io_methods version for which this file has been updated.
22 #define LAST_KNOWN_IOMETHODS_VERSION 3
24 /**
25 * By default use the unix-excl VFS, for the following reasons:
26 * 1. It improves compatibility with NFS shares, whose implementation
27 * is incompatible with SQLite's locking requirements.
28 * Bug 433129 attempted to automatically identify such file-systems,
29 * but a reliable way was not found and the fallback locking is slower than
30 * POSIX locking, so we do not want to do it by default.
31 * 2. It allows wal mode to avoid the memory mapped -shm file, reducing the
32 * likelihood of SIGBUS failures when disk space is exhausted.
33 * 3. It provides some protection from third party database tampering while a
34 * connection is open.
35 * This preference allows to revert to the "unix" VFS, that is not exclusive,
36 * thus it can be used by developers to query a database through the Sqlite
37 * command line while it's already in use.
39 #define PREF_MULTI_PROCESS_ACCESS "storage.multiProcessAccess.enabled"
41 namespace {
43 using namespace mozilla;
44 using namespace mozilla::dom::quota;
45 using namespace mozilla::net;
47 struct Histograms {
48 const char *name;
49 const Telemetry::HistogramID readB;
50 const Telemetry::HistogramID writeB;
51 const Telemetry::HistogramID readMS;
52 const Telemetry::HistogramID writeMS;
53 const Telemetry::HistogramID syncMS;
56 #define SQLITE_TELEMETRY(FILENAME, HGRAM) \
57 { \
58 FILENAME, Telemetry::MOZ_SQLITE_##HGRAM##_READ_B, \
59 Telemetry::MOZ_SQLITE_##HGRAM##_WRITE_B, \
60 Telemetry::MOZ_SQLITE_##HGRAM##_READ_MS, \
61 Telemetry::MOZ_SQLITE_##HGRAM##_WRITE_MS, \
62 Telemetry::MOZ_SQLITE_##HGRAM##_SYNC_MS \
65 Histograms gHistograms[] = {SQLITE_TELEMETRY("places.sqlite", PLACES),
66 SQLITE_TELEMETRY("cookies.sqlite", COOKIES),
67 SQLITE_TELEMETRY("webappsstore.sqlite", WEBAPPS),
68 SQLITE_TELEMETRY(nullptr, OTHER)};
69 #undef SQLITE_TELEMETRY
71 /** RAII class for measuring how long io takes on/off main thread
73 class IOThreadAutoTimer {
74 public:
75 /**
76 * IOThreadAutoTimer measures time spent in IO. Additionally it
77 * automatically determines whether IO is happening on the main
78 * thread and picks an appropriate histogram.
80 * @param id takes a telemetry histogram id. The id+1 must be an
81 * equivalent histogram for the main thread. Eg, MOZ_SQLITE_OPEN_MS
82 * is followed by MOZ_SQLITE_OPEN_MAIN_THREAD_MS.
84 * @param aOp optionally takes an IO operation to report through the
85 * IOInterposer. Filename will be reported as NULL, and reference will be
86 * either "sqlite-mainthread" or "sqlite-otherthread".
88 explicit IOThreadAutoTimer(
89 Telemetry::HistogramID aId,
90 IOInterposeObserver::Operation aOp = IOInterposeObserver::OpNone)
91 : start(TimeStamp::Now()),
92 id(aId)
93 #if defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN)
95 op(aOp)
96 #endif
101 * This constructor is for when we want to report an operation to
102 * IOInterposer but do not require a telemetry probe.
104 * @param aOp IO Operation to report through the IOInterposer.
106 explicit IOThreadAutoTimer(IOInterposeObserver::Operation aOp)
107 : start(TimeStamp::Now()),
108 id(Telemetry::HistogramCount)
109 #if defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN)
111 op(aOp)
112 #endif
116 ~IOThreadAutoTimer() {
117 TimeStamp end(TimeStamp::Now());
118 uint32_t mainThread = NS_IsMainThread() ? 1 : 0;
119 if (id != Telemetry::HistogramCount) {
120 Telemetry::AccumulateTimeDelta(
121 static_cast<Telemetry::HistogramID>(id + mainThread), start, end);
123 // We don't report SQLite I/O on Windows because we have a comprehensive
124 // mechanism for intercepting I/O on that platform that captures a superset
125 // of the data captured here.
126 #if defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN)
127 if (IOInterposer::IsObservedOperation(op)) {
128 const char *main_ref = "sqlite-mainthread";
129 const char *other_ref = "sqlite-otherthread";
131 // Create observation
132 IOInterposeObserver::Observation ob(op, start, end,
133 (mainThread ? main_ref : other_ref));
134 // Report observation
135 IOInterposer::Report(ob);
137 #endif /* defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN) */
140 private:
141 const TimeStamp start;
142 const Telemetry::HistogramID id;
143 #if defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN)
144 IOInterposeObserver::Operation op;
145 #endif
148 struct telemetry_file {
149 // Base class. Must be first
150 sqlite3_file base;
152 // histograms pertaining to this file
153 Histograms *histograms;
155 // quota object for this file
156 RefPtr<QuotaObject> quotaObject;
158 // The chunk size for this file. See the documentation for
159 // sqlite3_file_control() and FCNTL_CHUNK_SIZE.
160 int fileChunkSize;
162 // The filename
163 char *location;
165 // This contains the vfs that actually does work
166 sqlite3_file pReal[1];
169 const char *DatabasePathFromWALPath(const char *zWALName) {
171 * Do some sketchy pointer arithmetic to find the parameter key. The WAL
172 * filename is in the middle of a big allocated block that contains:
174 * - Random Values
175 * - Main Database Path
176 * - \0
177 * - Multiple URI components consisting of:
178 * - Key
179 * - \0
180 * - Value
181 * - \0
182 * - \0
183 * - Journal Path
184 * - \0
185 * - WAL Path (zWALName)
186 * - \0
188 * Because the main database path is preceded by a random value we have to be
189 * careful when trying to figure out when we should terminate this loop.
191 MOZ_ASSERT(zWALName);
193 nsDependentCSubstring dbPath(zWALName, strlen(zWALName));
195 // Chop off the "-wal" suffix.
196 NS_NAMED_LITERAL_CSTRING(kWALSuffix, "-wal");
197 MOZ_ASSERT(StringEndsWith(dbPath, kWALSuffix));
199 dbPath.Rebind(zWALName, dbPath.Length() - kWALSuffix.Length());
200 MOZ_ASSERT(!dbPath.IsEmpty());
202 // We want to scan to the end of the key/value URI pairs. Skip the preceding
203 // null and go to the last char of the journal path.
204 const char *cursor = zWALName - 2;
206 // Make sure we just skipped a null.
207 MOZ_ASSERT(!*(cursor + 1));
209 // Walk backwards over the journal path.
210 while (*cursor) {
211 cursor--;
214 // There should be another null here.
215 cursor--;
216 MOZ_ASSERT(!*cursor);
218 // Back up one more char to the last char of the previous string. It may be
219 // the database path or it may be a key/value URI pair.
220 cursor--;
222 #ifdef DEBUG
224 // Verify that we just walked over the journal path. Account for the two
225 // nulls we just skipped.
226 const char *journalStart = cursor + 3;
228 nsDependentCSubstring journalPath(journalStart, strlen(journalStart));
230 // Chop off the "-journal" suffix.
231 NS_NAMED_LITERAL_CSTRING(kJournalSuffix, "-journal");
232 MOZ_ASSERT(StringEndsWith(journalPath, kJournalSuffix));
234 journalPath.Rebind(journalStart,
235 journalPath.Length() - kJournalSuffix.Length());
236 MOZ_ASSERT(!journalPath.IsEmpty());
238 // Make sure that the database name is a substring of the journal name.
239 MOZ_ASSERT(journalPath == dbPath);
241 #endif
243 // Now we're either at the end of the key/value URI pairs or we're at the
244 // end of the database path. Carefully walk backwards one character at a
245 // time to do this safely without running past the beginning of the database
246 // path.
247 const char *const dbPathStart = dbPath.BeginReading();
248 const char *dbPathCursor = dbPath.EndReading() - 1;
249 bool isDBPath = true;
251 while (true) {
252 MOZ_ASSERT(*dbPathCursor, "dbPathCursor should never see a null char!");
254 if (isDBPath) {
255 isDBPath =
256 dbPathStart <= dbPathCursor && *dbPathCursor == *cursor && *cursor;
259 if (!isDBPath) {
260 // This isn't the database path so it must be a value. Scan past it and
261 // the key also.
262 for (size_t stringCount = 0; stringCount < 2; stringCount++) {
263 // Scan past the string to the preceding null character.
264 while (*cursor) {
265 cursor--;
268 // Back up one more char to the last char of preceding string.
269 cursor--;
272 // Reset and start again.
273 dbPathCursor = dbPath.EndReading() - 1;
274 isDBPath = true;
276 continue;
279 MOZ_ASSERT(isDBPath);
280 MOZ_ASSERT(*cursor);
282 if (dbPathStart == dbPathCursor) {
283 // Found the full database path, we're all done.
284 MOZ_ASSERT(nsDependentCString(cursor) == dbPath);
285 return cursor;
288 // Change the cursors and go through the loop again.
289 cursor--;
290 dbPathCursor--;
293 MOZ_CRASH("Should never get here!");
296 already_AddRefed<QuotaObject> GetQuotaObjectFromNameAndParameters(
297 const char *zName, const char *zURIParameterKey) {
298 MOZ_ASSERT(zName);
299 MOZ_ASSERT(zURIParameterKey);
301 const char *persistenceType =
302 sqlite3_uri_parameter(zURIParameterKey, "persistenceType");
303 if (!persistenceType) {
304 return nullptr;
307 const char *group = sqlite3_uri_parameter(zURIParameterKey, "group");
308 if (!group) {
309 NS_WARNING("SQLite URI had 'persistenceType' but not 'group'?!");
310 return nullptr;
313 const char *origin = sqlite3_uri_parameter(zURIParameterKey, "origin");
314 if (!origin) {
315 NS_WARNING(
316 "SQLite URI had 'persistenceType' and 'group' but not "
317 "'origin'?!");
318 return nullptr;
321 QuotaManager *quotaManager = QuotaManager::Get();
322 MOZ_ASSERT(quotaManager);
324 return quotaManager->GetQuotaObject(
325 PersistenceTypeFromText(nsDependentCString(persistenceType)),
326 nsDependentCString(group), nsDependentCString(origin),
327 NS_ConvertUTF8toUTF16(zName));
330 void MaybeEstablishQuotaControl(const char *zName, telemetry_file *pFile,
331 int flags) {
332 MOZ_ASSERT(pFile);
333 MOZ_ASSERT(!pFile->quotaObject);
335 if (!(flags & (SQLITE_OPEN_URI | SQLITE_OPEN_WAL))) {
336 return;
339 MOZ_ASSERT(zName);
341 const char *zURIParameterKey =
342 (flags & SQLITE_OPEN_WAL) ? DatabasePathFromWALPath(zName) : zName;
344 MOZ_ASSERT(zURIParameterKey);
346 pFile->quotaObject =
347 GetQuotaObjectFromNameAndParameters(zName, zURIParameterKey);
351 ** Close a telemetry_file.
353 int xClose(sqlite3_file *pFile) {
354 telemetry_file *p = (telemetry_file *)pFile;
355 int rc;
356 { // Scope for IOThreadAutoTimer
357 IOThreadAutoTimer ioTimer(IOInterposeObserver::OpClose);
358 rc = p->pReal->pMethods->xClose(p->pReal);
360 if (rc == SQLITE_OK) {
361 delete p->base.pMethods;
362 p->base.pMethods = nullptr;
363 p->quotaObject = nullptr;
364 delete[] p->location;
365 #ifdef DEBUG
366 p->fileChunkSize = 0;
367 #endif
369 return rc;
373 ** Read data from a telemetry_file.
375 int xRead(sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst) {
376 telemetry_file *p = (telemetry_file *)pFile;
377 IOThreadAutoTimer ioTimer(p->histograms->readMS, IOInterposeObserver::OpRead);
378 int rc;
379 rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
380 if (rc == SQLITE_OK && IOActivityMonitor::IsActive()) {
381 IOActivityMonitor::Read(nsDependentCString(p->location), iAmt);
383 // sqlite likes to read from empty files, this is normal, ignore it.
384 if (rc != SQLITE_IOERR_SHORT_READ)
385 Telemetry::Accumulate(p->histograms->readB, rc == SQLITE_OK ? iAmt : 0);
386 return rc;
390 ** Return the current file-size of a telemetry_file.
392 int xFileSize(sqlite3_file *pFile, sqlite_int64 *pSize) {
393 IOThreadAutoTimer ioTimer(IOInterposeObserver::OpStat);
394 telemetry_file *p = (telemetry_file *)pFile;
395 int rc;
396 rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
397 return rc;
401 ** Write data to a telemetry_file.
403 int xWrite(sqlite3_file *pFile, const void *zBuf, int iAmt,
404 sqlite_int64 iOfst) {
405 telemetry_file *p = (telemetry_file *)pFile;
406 IOThreadAutoTimer ioTimer(p->histograms->writeMS,
407 IOInterposeObserver::OpWrite);
408 int rc;
409 if (p->quotaObject) {
410 MOZ_ASSERT(INT64_MAX - iOfst >= iAmt);
411 if (!p->quotaObject->MaybeUpdateSize(iOfst + iAmt, /* aTruncate */ false)) {
412 return SQLITE_FULL;
415 rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
416 if (rc == SQLITE_OK && IOActivityMonitor::IsActive()) {
417 IOActivityMonitor::Write(nsDependentCString(p->location), iAmt);
420 Telemetry::Accumulate(p->histograms->writeB, rc == SQLITE_OK ? iAmt : 0);
421 if (p->quotaObject && rc != SQLITE_OK) {
422 NS_WARNING(
423 "xWrite failed on a quota-controlled file, attempting to "
424 "update its current size...");
425 sqlite_int64 currentSize;
426 if (xFileSize(pFile, &currentSize) == SQLITE_OK) {
427 p->quotaObject->MaybeUpdateSize(currentSize, /* aTruncate */ true);
430 return rc;
434 ** Truncate a telemetry_file.
436 int xTruncate(sqlite3_file *pFile, sqlite_int64 size) {
437 IOThreadAutoTimer ioTimer(Telemetry::MOZ_SQLITE_TRUNCATE_MS);
438 telemetry_file *p = (telemetry_file *)pFile;
439 int rc;
440 Telemetry::AutoTimer<Telemetry::MOZ_SQLITE_TRUNCATE_MS> timer;
441 if (p->quotaObject) {
442 if (p->fileChunkSize > 0) {
443 // Round up to the smallest multiple of the chunk size that will hold all
444 // the data.
445 size =
446 ((size + p->fileChunkSize - 1) / p->fileChunkSize) * p->fileChunkSize;
448 if (!p->quotaObject->MaybeUpdateSize(size, /* aTruncate */ true)) {
449 return SQLITE_FULL;
452 rc = p->pReal->pMethods->xTruncate(p->pReal, size);
453 if (p->quotaObject) {
454 if (rc == SQLITE_OK) {
455 #ifdef DEBUG
456 // Make sure xTruncate set the size exactly as we calculated above.
457 sqlite_int64 newSize;
458 MOZ_ASSERT(xFileSize(pFile, &newSize) == SQLITE_OK);
459 MOZ_ASSERT(newSize == size);
460 #endif
461 } else {
462 NS_WARNING(
463 "xTruncate failed on a quota-controlled file, attempting to "
464 "update its current size...");
465 if (xFileSize(pFile, &size) == SQLITE_OK) {
466 p->quotaObject->MaybeUpdateSize(size, /* aTruncate */ true);
470 return rc;
474 ** Sync a telemetry_file.
476 int xSync(sqlite3_file *pFile, int flags) {
477 telemetry_file *p = (telemetry_file *)pFile;
478 IOThreadAutoTimer ioTimer(p->histograms->syncMS,
479 IOInterposeObserver::OpFSync);
480 return p->pReal->pMethods->xSync(p->pReal, flags);
484 ** Lock a telemetry_file.
486 int xLock(sqlite3_file *pFile, int eLock) {
487 telemetry_file *p = (telemetry_file *)pFile;
488 int rc;
489 rc = p->pReal->pMethods->xLock(p->pReal, eLock);
490 return rc;
494 ** Unlock a telemetry_file.
496 int xUnlock(sqlite3_file *pFile, int eLock) {
497 telemetry_file *p = (telemetry_file *)pFile;
498 int rc;
499 rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
500 return rc;
504 ** Check if another file-handle holds a RESERVED lock on a telemetry_file.
506 int xCheckReservedLock(sqlite3_file *pFile, int *pResOut) {
507 telemetry_file *p = (telemetry_file *)pFile;
508 int rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
509 return rc;
513 ** File control method. For custom operations on a telemetry_file.
515 int xFileControl(sqlite3_file *pFile, int op, void *pArg) {
516 telemetry_file *p = (telemetry_file *)pFile;
517 int rc;
518 // Hook SQLITE_FCNTL_SIZE_HINT for quota-controlled files and do the necessary
519 // work before passing to the SQLite VFS.
520 if (op == SQLITE_FCNTL_SIZE_HINT && p->quotaObject) {
521 sqlite3_int64 hintSize = *static_cast<sqlite3_int64 *>(pArg);
522 sqlite3_int64 currentSize;
523 rc = xFileSize(pFile, &currentSize);
524 if (rc != SQLITE_OK) {
525 return rc;
527 if (hintSize > currentSize) {
528 rc = xTruncate(pFile, hintSize);
529 if (rc != SQLITE_OK) {
530 return rc;
534 rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
535 // Grab the file chunk size after the SQLite VFS has approved.
536 if (op == SQLITE_FCNTL_CHUNK_SIZE && rc == SQLITE_OK) {
537 p->fileChunkSize = *static_cast<int *>(pArg);
539 #ifdef DEBUG
540 if (op == SQLITE_FCNTL_SIZE_HINT && p->quotaObject && rc == SQLITE_OK) {
541 sqlite3_int64 hintSize = *static_cast<sqlite3_int64 *>(pArg);
542 if (p->fileChunkSize > 0) {
543 hintSize = ((hintSize + p->fileChunkSize - 1) / p->fileChunkSize) *
544 p->fileChunkSize;
546 sqlite3_int64 currentSize;
547 MOZ_ASSERT(xFileSize(pFile, &currentSize) == SQLITE_OK);
548 MOZ_ASSERT(currentSize >= hintSize);
550 #endif
551 return rc;
555 ** Return the sector-size in bytes for a telemetry_file.
557 int xSectorSize(sqlite3_file *pFile) {
558 telemetry_file *p = (telemetry_file *)pFile;
559 int rc;
560 rc = p->pReal->pMethods->xSectorSize(p->pReal);
561 return rc;
565 ** Return the device characteristic flags supported by a telemetry_file.
567 int xDeviceCharacteristics(sqlite3_file *pFile) {
568 telemetry_file *p = (telemetry_file *)pFile;
569 int rc;
570 rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
571 return rc;
575 ** Shared-memory operations.
577 int xShmLock(sqlite3_file *pFile, int ofst, int n, int flags) {
578 telemetry_file *p = (telemetry_file *)pFile;
579 return p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
582 int xShmMap(sqlite3_file *pFile, int iRegion, int szRegion, int isWrite,
583 void volatile **pp) {
584 telemetry_file *p = (telemetry_file *)pFile;
585 int rc;
586 rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
587 return rc;
590 void xShmBarrier(sqlite3_file *pFile) {
591 telemetry_file *p = (telemetry_file *)pFile;
592 p->pReal->pMethods->xShmBarrier(p->pReal);
595 int xShmUnmap(sqlite3_file *pFile, int delFlag) {
596 telemetry_file *p = (telemetry_file *)pFile;
597 int rc;
598 rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
599 return rc;
602 int xFetch(sqlite3_file *pFile, sqlite3_int64 iOff, int iAmt, void **pp) {
603 telemetry_file *p = (telemetry_file *)pFile;
604 MOZ_ASSERT(p->pReal->pMethods->iVersion >= 3);
605 return p->pReal->pMethods->xFetch(p->pReal, iOff, iAmt, pp);
608 int xUnfetch(sqlite3_file *pFile, sqlite3_int64 iOff, void *pResOut) {
609 telemetry_file *p = (telemetry_file *)pFile;
610 MOZ_ASSERT(p->pReal->pMethods->iVersion >= 3);
611 return p->pReal->pMethods->xUnfetch(p->pReal, iOff, pResOut);
614 int xOpen(sqlite3_vfs *vfs, const char *zName, sqlite3_file *pFile, int flags,
615 int *pOutFlags) {
616 IOThreadAutoTimer ioTimer(Telemetry::MOZ_SQLITE_OPEN_MS,
617 IOInterposeObserver::OpCreateOrOpen);
618 Telemetry::AutoTimer<Telemetry::MOZ_SQLITE_OPEN_MS> timer;
619 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
620 int rc;
621 telemetry_file *p = (telemetry_file *)pFile;
622 Histograms *h = nullptr;
623 // check if the filename is one we are probing for
624 for (size_t i = 0; i < sizeof(gHistograms) / sizeof(gHistograms[0]); i++) {
625 h = &gHistograms[i];
626 // last probe is the fallback probe
627 if (!h->name) break;
628 if (!zName) continue;
629 const char *match = strstr(zName, h->name);
630 if (!match) continue;
631 char c = match[strlen(h->name)];
632 // include -wal/-journal too
633 if (!c || c == '-') break;
635 p->histograms = h;
637 MaybeEstablishQuotaControl(zName, p, flags);
639 rc = orig_vfs->xOpen(orig_vfs, zName, p->pReal, flags, pOutFlags);
640 if (rc != SQLITE_OK) return rc;
642 if (zName) {
643 p->location = new char[7 + strlen(zName) + 1];
644 strcpy(p->location, "file://");
645 strcpy(p->location + 7, zName);
646 } else {
647 p->location = new char[8];
648 strcpy(p->location, "file://");
651 if (p->pReal->pMethods) {
652 sqlite3_io_methods *pNew = new sqlite3_io_methods;
653 const sqlite3_io_methods *pSub = p->pReal->pMethods;
654 memset(pNew, 0, sizeof(*pNew));
655 // If the io_methods version is higher than the last known one, you should
656 // update this VFS adding appropriate IO methods for any methods added in
657 // the version change.
658 pNew->iVersion = pSub->iVersion;
659 MOZ_ASSERT(pNew->iVersion <= LAST_KNOWN_IOMETHODS_VERSION);
660 pNew->xClose = xClose;
661 pNew->xRead = xRead;
662 pNew->xWrite = xWrite;
663 pNew->xTruncate = xTruncate;
664 pNew->xSync = xSync;
665 pNew->xFileSize = xFileSize;
666 pNew->xLock = xLock;
667 pNew->xUnlock = xUnlock;
668 pNew->xCheckReservedLock = xCheckReservedLock;
669 pNew->xFileControl = xFileControl;
670 pNew->xSectorSize = xSectorSize;
671 pNew->xDeviceCharacteristics = xDeviceCharacteristics;
672 if (pNew->iVersion >= 2) {
673 // Methods added in version 2.
674 pNew->xShmMap = pSub->xShmMap ? xShmMap : 0;
675 pNew->xShmLock = pSub->xShmLock ? xShmLock : 0;
676 pNew->xShmBarrier = pSub->xShmBarrier ? xShmBarrier : 0;
677 pNew->xShmUnmap = pSub->xShmUnmap ? xShmUnmap : 0;
679 if (pNew->iVersion >= 3) {
680 // Methods added in version 3.
681 // SQLite 3.7.17 calls these methods without checking for nullptr first,
682 // so we always define them. Verify that we're not going to call
683 // nullptrs, though.
684 MOZ_ASSERT(pSub->xFetch);
685 pNew->xFetch = xFetch;
686 MOZ_ASSERT(pSub->xUnfetch);
687 pNew->xUnfetch = xUnfetch;
689 pFile->pMethods = pNew;
691 return rc;
694 int xDelete(sqlite3_vfs *vfs, const char *zName, int syncDir) {
695 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
696 int rc;
697 RefPtr<QuotaObject> quotaObject;
699 if (StringEndsWith(nsDependentCString(zName), NS_LITERAL_CSTRING("-wal"))) {
700 const char *zURIParameterKey = DatabasePathFromWALPath(zName);
701 MOZ_ASSERT(zURIParameterKey);
703 quotaObject = GetQuotaObjectFromNameAndParameters(zName, zURIParameterKey);
706 rc = orig_vfs->xDelete(orig_vfs, zName, syncDir);
707 if (rc == SQLITE_OK && quotaObject) {
708 MOZ_ALWAYS_TRUE(quotaObject->MaybeUpdateSize(0, /* aTruncate */ true));
711 return rc;
714 int xAccess(sqlite3_vfs *vfs, const char *zName, int flags, int *pResOut) {
715 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
716 return orig_vfs->xAccess(orig_vfs, zName, flags, pResOut);
719 int xFullPathname(sqlite3_vfs *vfs, const char *zName, int nOut, char *zOut) {
720 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
721 return orig_vfs->xFullPathname(orig_vfs, zName, nOut, zOut);
724 void *xDlOpen(sqlite3_vfs *vfs, const char *zFilename) {
725 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
726 return orig_vfs->xDlOpen(orig_vfs, zFilename);
729 void xDlError(sqlite3_vfs *vfs, int nByte, char *zErrMsg) {
730 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
731 orig_vfs->xDlError(orig_vfs, nByte, zErrMsg);
734 void (*xDlSym(sqlite3_vfs *vfs, void *pHdle, const char *zSym))(void) {
735 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
736 return orig_vfs->xDlSym(orig_vfs, pHdle, zSym);
739 void xDlClose(sqlite3_vfs *vfs, void *pHandle) {
740 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
741 orig_vfs->xDlClose(orig_vfs, pHandle);
744 int xRandomness(sqlite3_vfs *vfs, int nByte, char *zOut) {
745 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
746 return orig_vfs->xRandomness(orig_vfs, nByte, zOut);
749 int xSleep(sqlite3_vfs *vfs, int microseconds) {
750 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
751 return orig_vfs->xSleep(orig_vfs, microseconds);
754 int xCurrentTime(sqlite3_vfs *vfs, double *prNow) {
755 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
756 return orig_vfs->xCurrentTime(orig_vfs, prNow);
759 int xGetLastError(sqlite3_vfs *vfs, int nBuf, char *zBuf) {
760 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
761 return orig_vfs->xGetLastError(orig_vfs, nBuf, zBuf);
764 int xCurrentTimeInt64(sqlite3_vfs *vfs, sqlite3_int64 *piNow) {
765 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
766 return orig_vfs->xCurrentTimeInt64(orig_vfs, piNow);
769 static int xSetSystemCall(sqlite3_vfs *vfs, const char *zName,
770 sqlite3_syscall_ptr pFunc) {
771 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
772 return orig_vfs->xSetSystemCall(orig_vfs, zName, pFunc);
775 static sqlite3_syscall_ptr xGetSystemCall(sqlite3_vfs *vfs, const char *zName) {
776 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
777 return orig_vfs->xGetSystemCall(orig_vfs, zName);
780 static const char *xNextSystemCall(sqlite3_vfs *vfs, const char *zName) {
781 sqlite3_vfs *orig_vfs = static_cast<sqlite3_vfs *>(vfs->pAppData);
782 return orig_vfs->xNextSystemCall(orig_vfs, zName);
785 } // namespace
787 namespace mozilla {
788 namespace storage {
790 const char *GetVFSName() { return "telemetry-vfs"; }
792 sqlite3_vfs *ConstructTelemetryVFS() {
793 #if defined(XP_WIN)
794 #define EXPECTED_VFS "win32"
795 #define EXPECTED_VFS_EXCL "win32"
796 #else
797 #define EXPECTED_VFS "unix"
798 #define EXPECTED_VFS_EXCL "unix-excl"
799 #endif
801 bool expected_vfs;
802 sqlite3_vfs *vfs;
803 if (Preferences::GetBool(PREF_MULTI_PROCESS_ACCESS, false)) {
804 // Use the non-exclusive VFS.
805 vfs = sqlite3_vfs_find(nullptr);
806 expected_vfs = vfs->zName && !strcmp(vfs->zName, EXPECTED_VFS);
807 } else {
808 vfs = sqlite3_vfs_find(EXPECTED_VFS_EXCL);
809 expected_vfs = (vfs != nullptr);
811 if (!expected_vfs) {
812 return nullptr;
815 sqlite3_vfs *tvfs = new ::sqlite3_vfs;
816 memset(tvfs, 0, sizeof(::sqlite3_vfs));
817 // If the VFS version is higher than the last known one, you should update
818 // this VFS adding appropriate methods for any methods added in the version
819 // change.
820 tvfs->iVersion = vfs->iVersion;
821 MOZ_ASSERT(vfs->iVersion <= LAST_KNOWN_VFS_VERSION);
822 tvfs->szOsFile =
823 sizeof(telemetry_file) - sizeof(sqlite3_file) + vfs->szOsFile;
824 tvfs->mxPathname = vfs->mxPathname;
825 tvfs->zName = GetVFSName();
826 tvfs->pAppData = vfs;
827 tvfs->xOpen = xOpen;
828 tvfs->xDelete = xDelete;
829 tvfs->xAccess = xAccess;
830 tvfs->xFullPathname = xFullPathname;
831 tvfs->xDlOpen = xDlOpen;
832 tvfs->xDlError = xDlError;
833 tvfs->xDlSym = xDlSym;
834 tvfs->xDlClose = xDlClose;
835 tvfs->xRandomness = xRandomness;
836 tvfs->xSleep = xSleep;
837 tvfs->xCurrentTime = xCurrentTime;
838 tvfs->xGetLastError = xGetLastError;
839 if (tvfs->iVersion >= 2) {
840 // Methods added in version 2.
841 tvfs->xCurrentTimeInt64 = xCurrentTimeInt64;
843 if (tvfs->iVersion >= 3) {
844 // Methods added in version 3.
845 tvfs->xSetSystemCall = xSetSystemCall;
846 tvfs->xGetSystemCall = xGetSystemCall;
847 tvfs->xNextSystemCall = xNextSystemCall;
849 return tvfs;
852 already_AddRefed<QuotaObject> GetQuotaObjectForFile(sqlite3_file *pFile) {
853 MOZ_ASSERT(pFile);
855 telemetry_file *p = (telemetry_file *)pFile;
856 RefPtr<QuotaObject> result = p->quotaObject;
857 return result.forget();
860 } // namespace storage
861 } // namespace mozilla