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/. */
8 #include "mozilla/Telemetry.h"
9 #include "mozilla/Preferences.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"
19 // The last VFS version for which this file has been updated.
20 #define LAST_KNOWN_VFS_VERSION 3
22 // The last io_methods version for which this file has been updated.
23 #define LAST_KNOWN_IOMETHODS_VERSION 3
26 * By default use the unix-excl VFS, for the following reasons:
27 * 1. It improves compatibility with NFS shares, whose implementation
28 * is incompatible with SQLite's locking requirements.
29 * Bug 433129 attempted to automatically identify such file-systems,
30 * but a reliable way was not found and the fallback locking is slower than
31 * POSIX locking, so we do not want to do it by default.
32 * 2. It allows wal mode to avoid the memory mapped -shm file, reducing the
33 * likelihood of SIGBUS failures when disk space is exhausted.
34 * 3. It provides some protection from third party database tampering while a
36 * This preference allows to revert to the "unix" VFS, that is not exclusive,
37 * thus it can be used by developers to query a database through the Sqlite
38 * command line while it's already in use.
40 #define PREF_MULTI_PROCESS_ACCESS "storage.multiProcessAccess.enabled"
44 using namespace mozilla
;
45 using namespace mozilla::dom::quota
;
46 using namespace mozilla::net
;
50 const Telemetry::HistogramID readB
;
51 const Telemetry::HistogramID writeB
;
52 const Telemetry::HistogramID readMS
;
53 const Telemetry::HistogramID writeMS
;
54 const Telemetry::HistogramID syncMS
;
57 #define SQLITE_TELEMETRY(FILENAME, HGRAM) \
59 FILENAME, Telemetry::MOZ_SQLITE_##HGRAM##_READ_B, \
60 Telemetry::MOZ_SQLITE_##HGRAM##_WRITE_B, \
61 Telemetry::MOZ_SQLITE_##HGRAM##_READ_MS, \
62 Telemetry::MOZ_SQLITE_##HGRAM##_WRITE_MS, \
63 Telemetry::MOZ_SQLITE_##HGRAM##_SYNC_MS \
66 Histograms gHistograms
[] = {SQLITE_TELEMETRY("places.sqlite", PLACES
),
67 SQLITE_TELEMETRY("cookies.sqlite", COOKIES
),
68 SQLITE_TELEMETRY("webappsstore.sqlite", WEBAPPS
),
69 SQLITE_TELEMETRY(nullptr, OTHER
)};
70 #undef SQLITE_TELEMETRY
72 /** RAII class for measuring how long io takes on/off main thread
74 class IOThreadAutoTimer
{
77 * IOThreadAutoTimer measures time spent in IO. Additionally it
78 * automatically determines whether IO is happening on the main
79 * thread and picks an appropriate histogram.
81 * @param id takes a telemetry histogram id. The id+1 must be an
82 * equivalent histogram for the main thread. Eg, MOZ_SQLITE_OPEN_MS
83 * is followed by MOZ_SQLITE_OPEN_MAIN_THREAD_MS.
85 * @param aOp optionally takes an IO operation to report through the
86 * IOInterposer. Filename will be reported as NULL, and reference will be
87 * either "sqlite-mainthread" or "sqlite-otherthread".
89 explicit IOThreadAutoTimer(
90 Telemetry::HistogramID aId
,
91 IOInterposeObserver::Operation aOp
= IOInterposeObserver::OpNone
)
92 : start(TimeStamp::Now()),
94 #if defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN)
102 * This constructor is for when we want to report an operation to
103 * IOInterposer but do not require a telemetry probe.
105 * @param aOp IO Operation to report through the IOInterposer.
107 explicit IOThreadAutoTimer(IOInterposeObserver::Operation aOp
)
108 : start(TimeStamp::Now()),
109 id(Telemetry::HistogramCount
)
110 #if defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN)
117 ~IOThreadAutoTimer() {
118 TimeStamp
end(TimeStamp::Now());
119 uint32_t mainThread
= NS_IsMainThread() ? 1 : 0;
120 if (id
!= Telemetry::HistogramCount
) {
121 Telemetry::AccumulateTimeDelta(
122 static_cast<Telemetry::HistogramID
>(id
+ mainThread
), start
, end
);
124 // We don't report SQLite I/O on Windows because we have a comprehensive
125 // mechanism for intercepting I/O on that platform that captures a superset
126 // of the data captured here.
127 #if defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN)
128 if (IOInterposer::IsObservedOperation(op
)) {
129 const char* main_ref
= "sqlite-mainthread";
130 const char* other_ref
= "sqlite-otherthread";
132 // Create observation
133 IOInterposeObserver::Observation
ob(op
, start
, end
,
134 (mainThread
? main_ref
: other_ref
));
135 // Report observation
136 IOInterposer::Report(ob
);
138 #endif /* defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN) */
142 const TimeStamp start
;
143 const Telemetry::HistogramID id
;
144 #if defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN)
145 IOInterposeObserver::Operation op
;
149 struct telemetry_file
{
150 // Base class. Must be first
153 // histograms pertaining to this file
154 Histograms
* histograms
;
156 // quota object for this file
157 RefPtr
<QuotaObject
> quotaObject
;
159 // The chunk size for this file. See the documentation for
160 // sqlite3_file_control() and FCNTL_CHUNK_SIZE.
166 // This contains the vfs that actually does work
167 sqlite3_file pReal
[1];
170 const char* DatabasePathFromWALPath(const char* zWALName
) {
172 * Do some sketchy pointer arithmetic to find the parameter key. The WAL
173 * filename is in the middle of a big allocated block that contains:
176 * - Main Database Path
178 * - Multiple URI components consisting of:
186 * - WAL Path (zWALName)
189 * Because the main database path is preceded by a random value we have to be
190 * careful when trying to figure out when we should terminate this loop.
192 MOZ_ASSERT(zWALName
);
194 nsDependentCSubstring
dbPath(zWALName
, strlen(zWALName
));
196 // Chop off the "-wal" suffix.
197 NS_NAMED_LITERAL_CSTRING(kWALSuffix
, "-wal");
198 MOZ_ASSERT(StringEndsWith(dbPath
, kWALSuffix
));
200 dbPath
.Rebind(zWALName
, dbPath
.Length() - kWALSuffix
.Length());
201 MOZ_ASSERT(!dbPath
.IsEmpty());
203 // We want to scan to the end of the key/value URI pairs. Skip the preceding
204 // null and go to the last char of the journal path.
205 const char* cursor
= zWALName
- 2;
207 // Make sure we just skipped a null.
208 MOZ_ASSERT(!*(cursor
+ 1));
210 // Walk backwards over the journal path.
215 // There should be another null here.
217 MOZ_ASSERT(!*cursor
);
219 // Back up one more char to the last char of the previous string. It may be
220 // the database path or it may be a key/value URI pair.
225 // Verify that we just walked over the journal path. Account for the two
226 // nulls we just skipped.
227 const char* journalStart
= cursor
+ 3;
229 nsDependentCSubstring
journalPath(journalStart
, strlen(journalStart
));
231 // Chop off the "-journal" suffix.
232 NS_NAMED_LITERAL_CSTRING(kJournalSuffix
, "-journal");
233 MOZ_ASSERT(StringEndsWith(journalPath
, kJournalSuffix
));
235 journalPath
.Rebind(journalStart
,
236 journalPath
.Length() - kJournalSuffix
.Length());
237 MOZ_ASSERT(!journalPath
.IsEmpty());
239 // Make sure that the database name is a substring of the journal name.
240 MOZ_ASSERT(journalPath
== dbPath
);
244 // Now we're either at the end of the key/value URI pairs or we're at the
245 // end of the database path. Carefully walk backwards one character at a
246 // time to do this safely without running past the beginning of the database
248 const char* const dbPathStart
= dbPath
.BeginReading();
249 const char* dbPathCursor
= dbPath
.EndReading() - 1;
250 bool isDBPath
= true;
253 MOZ_ASSERT(*dbPathCursor
, "dbPathCursor should never see a null char!");
257 dbPathStart
<= dbPathCursor
&& *dbPathCursor
== *cursor
&& *cursor
;
261 // This isn't the database path so it must be a value. Scan past it and
263 for (size_t stringCount
= 0; stringCount
< 2; stringCount
++) {
264 // Scan past the string to the preceding null character.
269 // Back up one more char to the last char of preceding string.
273 // Reset and start again.
274 dbPathCursor
= dbPath
.EndReading() - 1;
280 MOZ_ASSERT(isDBPath
);
283 if (dbPathStart
== dbPathCursor
) {
284 // Found the full database path, we're all done.
285 MOZ_ASSERT(nsDependentCString(cursor
) == dbPath
);
289 // Change the cursors and go through the loop again.
294 MOZ_CRASH("Should never get here!");
297 already_AddRefed
<QuotaObject
> GetQuotaObjectFromNameAndParameters(
298 const char* zName
, const char* zURIParameterKey
) {
300 MOZ_ASSERT(zURIParameterKey
);
302 const char* directoryLockIdParam
=
303 sqlite3_uri_parameter(zURIParameterKey
, "directoryLockId");
304 if (!directoryLockIdParam
) {
309 const int64_t directoryLockId
=
310 nsDependentCString(directoryLockIdParam
).ToInteger64(&rv
);
311 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv
));
313 QuotaManager
* quotaManager
= QuotaManager::Get();
314 MOZ_ASSERT(quotaManager
);
316 return quotaManager
->GetQuotaObject(directoryLockId
,
317 NS_ConvertUTF8toUTF16(zName
));
320 void MaybeEstablishQuotaControl(const char* zName
, telemetry_file
* pFile
,
323 MOZ_ASSERT(!pFile
->quotaObject
);
325 if (!(flags
& (SQLITE_OPEN_URI
| SQLITE_OPEN_WAL
))) {
331 const char* zURIParameterKey
=
332 (flags
& SQLITE_OPEN_WAL
) ? DatabasePathFromWALPath(zName
) : zName
;
334 MOZ_ASSERT(zURIParameterKey
);
337 GetQuotaObjectFromNameAndParameters(zName
, zURIParameterKey
);
341 ** Close a telemetry_file.
343 int xClose(sqlite3_file
* pFile
) {
344 telemetry_file
* p
= (telemetry_file
*)pFile
;
346 { // Scope for IOThreadAutoTimer
347 IOThreadAutoTimer
ioTimer(IOInterposeObserver::OpClose
);
348 rc
= p
->pReal
->pMethods
->xClose(p
->pReal
);
350 if (rc
== SQLITE_OK
) {
351 delete p
->base
.pMethods
;
352 p
->base
.pMethods
= nullptr;
353 p
->quotaObject
= nullptr;
354 delete[] p
->location
;
356 p
->fileChunkSize
= 0;
363 ** Read data from a telemetry_file.
365 int xRead(sqlite3_file
* pFile
, void* zBuf
, int iAmt
, sqlite_int64 iOfst
) {
366 telemetry_file
* p
= (telemetry_file
*)pFile
;
367 IOThreadAutoTimer
ioTimer(p
->histograms
->readMS
, IOInterposeObserver::OpRead
);
369 rc
= p
->pReal
->pMethods
->xRead(p
->pReal
, zBuf
, iAmt
, iOfst
);
370 if (rc
== SQLITE_OK
&& IOActivityMonitor::IsActive()) {
371 IOActivityMonitor::Read(nsDependentCString(p
->location
), iAmt
);
373 // sqlite likes to read from empty files, this is normal, ignore it.
374 if (rc
!= SQLITE_IOERR_SHORT_READ
)
375 Telemetry::Accumulate(p
->histograms
->readB
, rc
== SQLITE_OK
? iAmt
: 0);
380 ** Return the current file-size of a telemetry_file.
382 int xFileSize(sqlite3_file
* pFile
, sqlite_int64
* pSize
) {
383 IOThreadAutoTimer
ioTimer(IOInterposeObserver::OpStat
);
384 telemetry_file
* p
= (telemetry_file
*)pFile
;
386 rc
= p
->pReal
->pMethods
->xFileSize(p
->pReal
, pSize
);
391 ** Write data to a telemetry_file.
393 int xWrite(sqlite3_file
* pFile
, const void* zBuf
, int iAmt
,
394 sqlite_int64 iOfst
) {
395 telemetry_file
* p
= (telemetry_file
*)pFile
;
396 IOThreadAutoTimer
ioTimer(p
->histograms
->writeMS
,
397 IOInterposeObserver::OpWrite
);
399 if (p
->quotaObject
) {
400 MOZ_ASSERT(INT64_MAX
- iOfst
>= iAmt
);
401 if (!p
->quotaObject
->MaybeUpdateSize(iOfst
+ iAmt
, /* aTruncate */ false)) {
405 rc
= p
->pReal
->pMethods
->xWrite(p
->pReal
, zBuf
, iAmt
, iOfst
);
406 if (rc
== SQLITE_OK
&& IOActivityMonitor::IsActive()) {
407 IOActivityMonitor::Write(nsDependentCString(p
->location
), iAmt
);
410 Telemetry::Accumulate(p
->histograms
->writeB
, rc
== SQLITE_OK
? iAmt
: 0);
411 if (p
->quotaObject
&& rc
!= SQLITE_OK
) {
413 "xWrite failed on a quota-controlled file, attempting to "
414 "update its current size...");
415 sqlite_int64 currentSize
;
416 if (xFileSize(pFile
, ¤tSize
) == SQLITE_OK
) {
417 p
->quotaObject
->MaybeUpdateSize(currentSize
, /* aTruncate */ true);
424 ** Truncate a telemetry_file.
426 int xTruncate(sqlite3_file
* pFile
, sqlite_int64 size
) {
427 IOThreadAutoTimer
ioTimer(Telemetry::MOZ_SQLITE_TRUNCATE_MS
);
428 telemetry_file
* p
= (telemetry_file
*)pFile
;
430 Telemetry::AutoTimer
<Telemetry::MOZ_SQLITE_TRUNCATE_MS
> timer
;
431 if (p
->quotaObject
) {
432 if (p
->fileChunkSize
> 0) {
433 // Round up to the smallest multiple of the chunk size that will hold all
436 ((size
+ p
->fileChunkSize
- 1) / p
->fileChunkSize
) * p
->fileChunkSize
;
438 if (!p
->quotaObject
->MaybeUpdateSize(size
, /* aTruncate */ true)) {
442 rc
= p
->pReal
->pMethods
->xTruncate(p
->pReal
, size
);
443 if (p
->quotaObject
) {
444 if (rc
== SQLITE_OK
) {
446 // Make sure xTruncate set the size exactly as we calculated above.
447 sqlite_int64 newSize
;
448 MOZ_ASSERT(xFileSize(pFile
, &newSize
) == SQLITE_OK
);
449 MOZ_ASSERT(newSize
== size
);
453 "xTruncate failed on a quota-controlled file, attempting to "
454 "update its current size...");
455 if (xFileSize(pFile
, &size
) == SQLITE_OK
) {
456 p
->quotaObject
->MaybeUpdateSize(size
, /* aTruncate */ true);
464 ** Sync a telemetry_file.
466 int xSync(sqlite3_file
* pFile
, int flags
) {
467 telemetry_file
* p
= (telemetry_file
*)pFile
;
468 IOThreadAutoTimer
ioTimer(p
->histograms
->syncMS
,
469 IOInterposeObserver::OpFSync
);
470 return p
->pReal
->pMethods
->xSync(p
->pReal
, flags
);
474 ** Lock a telemetry_file.
476 int xLock(sqlite3_file
* pFile
, int eLock
) {
477 telemetry_file
* p
= (telemetry_file
*)pFile
;
479 rc
= p
->pReal
->pMethods
->xLock(p
->pReal
, eLock
);
484 ** Unlock a telemetry_file.
486 int xUnlock(sqlite3_file
* pFile
, int eLock
) {
487 telemetry_file
* p
= (telemetry_file
*)pFile
;
489 rc
= p
->pReal
->pMethods
->xUnlock(p
->pReal
, eLock
);
494 ** Check if another file-handle holds a RESERVED lock on a telemetry_file.
496 int xCheckReservedLock(sqlite3_file
* pFile
, int* pResOut
) {
497 telemetry_file
* p
= (telemetry_file
*)pFile
;
498 int rc
= p
->pReal
->pMethods
->xCheckReservedLock(p
->pReal
, pResOut
);
503 ** File control method. For custom operations on a telemetry_file.
505 int xFileControl(sqlite3_file
* pFile
, int op
, void* pArg
) {
506 telemetry_file
* p
= (telemetry_file
*)pFile
;
508 // Hook SQLITE_FCNTL_SIZE_HINT for quota-controlled files and do the necessary
509 // work before passing to the SQLite VFS.
510 if (op
== SQLITE_FCNTL_SIZE_HINT
&& p
->quotaObject
) {
511 sqlite3_int64 hintSize
= *static_cast<sqlite3_int64
*>(pArg
);
512 sqlite3_int64 currentSize
;
513 rc
= xFileSize(pFile
, ¤tSize
);
514 if (rc
!= SQLITE_OK
) {
517 if (hintSize
> currentSize
) {
518 rc
= xTruncate(pFile
, hintSize
);
519 if (rc
!= SQLITE_OK
) {
524 rc
= p
->pReal
->pMethods
->xFileControl(p
->pReal
, op
, pArg
);
525 // Grab the file chunk size after the SQLite VFS has approved.
526 if (op
== SQLITE_FCNTL_CHUNK_SIZE
&& rc
== SQLITE_OK
) {
527 p
->fileChunkSize
= *static_cast<int*>(pArg
);
530 if (op
== SQLITE_FCNTL_SIZE_HINT
&& p
->quotaObject
&& rc
== SQLITE_OK
) {
531 sqlite3_int64 hintSize
= *static_cast<sqlite3_int64
*>(pArg
);
532 if (p
->fileChunkSize
> 0) {
533 hintSize
= ((hintSize
+ p
->fileChunkSize
- 1) / p
->fileChunkSize
) *
536 sqlite3_int64 currentSize
;
537 MOZ_ASSERT(xFileSize(pFile
, ¤tSize
) == SQLITE_OK
);
538 MOZ_ASSERT(currentSize
>= hintSize
);
545 ** Return the sector-size in bytes for a telemetry_file.
547 int xSectorSize(sqlite3_file
* pFile
) {
548 telemetry_file
* p
= (telemetry_file
*)pFile
;
550 rc
= p
->pReal
->pMethods
->xSectorSize(p
->pReal
);
555 ** Return the device characteristic flags supported by a telemetry_file.
557 int xDeviceCharacteristics(sqlite3_file
* pFile
) {
558 telemetry_file
* p
= (telemetry_file
*)pFile
;
560 rc
= p
->pReal
->pMethods
->xDeviceCharacteristics(p
->pReal
);
565 ** Shared-memory operations.
567 int xShmLock(sqlite3_file
* pFile
, int ofst
, int n
, int flags
) {
568 telemetry_file
* p
= (telemetry_file
*)pFile
;
569 return p
->pReal
->pMethods
->xShmLock(p
->pReal
, ofst
, n
, flags
);
572 int xShmMap(sqlite3_file
* pFile
, int iRegion
, int szRegion
, int isWrite
,
573 void volatile** pp
) {
574 telemetry_file
* p
= (telemetry_file
*)pFile
;
576 rc
= p
->pReal
->pMethods
->xShmMap(p
->pReal
, iRegion
, szRegion
, isWrite
, pp
);
580 void xShmBarrier(sqlite3_file
* pFile
) {
581 telemetry_file
* p
= (telemetry_file
*)pFile
;
582 p
->pReal
->pMethods
->xShmBarrier(p
->pReal
);
585 int xShmUnmap(sqlite3_file
* pFile
, int delFlag
) {
586 telemetry_file
* p
= (telemetry_file
*)pFile
;
588 rc
= p
->pReal
->pMethods
->xShmUnmap(p
->pReal
, delFlag
);
592 int xFetch(sqlite3_file
* pFile
, sqlite3_int64 iOff
, int iAmt
, void** pp
) {
593 telemetry_file
* p
= (telemetry_file
*)pFile
;
594 MOZ_ASSERT(p
->pReal
->pMethods
->iVersion
>= 3);
595 return p
->pReal
->pMethods
->xFetch(p
->pReal
, iOff
, iAmt
, pp
);
598 int xUnfetch(sqlite3_file
* pFile
, sqlite3_int64 iOff
, void* pResOut
) {
599 telemetry_file
* p
= (telemetry_file
*)pFile
;
600 MOZ_ASSERT(p
->pReal
->pMethods
->iVersion
>= 3);
601 return p
->pReal
->pMethods
->xUnfetch(p
->pReal
, iOff
, pResOut
);
604 int xOpen(sqlite3_vfs
* vfs
, const char* zName
, sqlite3_file
* pFile
, int flags
,
606 IOThreadAutoTimer
ioTimer(Telemetry::MOZ_SQLITE_OPEN_MS
,
607 IOInterposeObserver::OpCreateOrOpen
);
608 Telemetry::AutoTimer
<Telemetry::MOZ_SQLITE_OPEN_MS
> timer
;
609 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
611 telemetry_file
* p
= (telemetry_file
*)pFile
;
612 Histograms
* h
= nullptr;
613 // check if the filename is one we are probing for
614 for (size_t i
= 0; i
< sizeof(gHistograms
) / sizeof(gHistograms
[0]); i
++) {
616 // last probe is the fallback probe
618 if (!zName
) continue;
619 const char* match
= strstr(zName
, h
->name
);
620 if (!match
) continue;
621 char c
= match
[strlen(h
->name
)];
622 // include -wal/-journal too
623 if (!c
|| c
== '-') break;
627 MaybeEstablishQuotaControl(zName
, p
, flags
);
629 rc
= orig_vfs
->xOpen(orig_vfs
, zName
, p
->pReal
, flags
, pOutFlags
);
630 if (rc
!= SQLITE_OK
) return rc
;
633 p
->location
= new char[7 + strlen(zName
) + 1];
634 strcpy(p
->location
, "file://");
635 strcpy(p
->location
+ 7, zName
);
637 p
->location
= new char[8];
638 strcpy(p
->location
, "file://");
641 if (p
->pReal
->pMethods
) {
642 sqlite3_io_methods
* pNew
= new sqlite3_io_methods
;
643 const sqlite3_io_methods
* pSub
= p
->pReal
->pMethods
;
644 memset(pNew
, 0, sizeof(*pNew
));
645 // If the io_methods version is higher than the last known one, you should
646 // update this VFS adding appropriate IO methods for any methods added in
647 // the version change.
648 pNew
->iVersion
= pSub
->iVersion
;
649 MOZ_ASSERT(pNew
->iVersion
<= LAST_KNOWN_IOMETHODS_VERSION
);
650 pNew
->xClose
= xClose
;
652 pNew
->xWrite
= xWrite
;
653 pNew
->xTruncate
= xTruncate
;
655 pNew
->xFileSize
= xFileSize
;
657 pNew
->xUnlock
= xUnlock
;
658 pNew
->xCheckReservedLock
= xCheckReservedLock
;
659 pNew
->xFileControl
= xFileControl
;
660 pNew
->xSectorSize
= xSectorSize
;
661 pNew
->xDeviceCharacteristics
= xDeviceCharacteristics
;
662 if (pNew
->iVersion
>= 2) {
663 // Methods added in version 2.
664 pNew
->xShmMap
= pSub
->xShmMap
? xShmMap
: 0;
665 pNew
->xShmLock
= pSub
->xShmLock
? xShmLock
: 0;
666 pNew
->xShmBarrier
= pSub
->xShmBarrier
? xShmBarrier
: 0;
667 pNew
->xShmUnmap
= pSub
->xShmUnmap
? xShmUnmap
: 0;
669 if (pNew
->iVersion
>= 3) {
670 // Methods added in version 3.
671 // SQLite 3.7.17 calls these methods without checking for nullptr first,
672 // so we always define them. Verify that we're not going to call
674 MOZ_ASSERT(pSub
->xFetch
);
675 pNew
->xFetch
= xFetch
;
676 MOZ_ASSERT(pSub
->xUnfetch
);
677 pNew
->xUnfetch
= xUnfetch
;
679 pFile
->pMethods
= pNew
;
684 int xDelete(sqlite3_vfs
* vfs
, const char* zName
, int syncDir
) {
685 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
687 RefPtr
<QuotaObject
> quotaObject
;
689 if (StringEndsWith(nsDependentCString(zName
), NS_LITERAL_CSTRING("-wal"))) {
690 const char* zURIParameterKey
= DatabasePathFromWALPath(zName
);
691 MOZ_ASSERT(zURIParameterKey
);
693 quotaObject
= GetQuotaObjectFromNameAndParameters(zName
, zURIParameterKey
);
696 rc
= orig_vfs
->xDelete(orig_vfs
, zName
, syncDir
);
697 if (rc
== SQLITE_OK
&& quotaObject
) {
698 MOZ_ALWAYS_TRUE(quotaObject
->MaybeUpdateSize(0, /* aTruncate */ true));
704 int xAccess(sqlite3_vfs
* vfs
, const char* zName
, int flags
, int* pResOut
) {
705 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
706 return orig_vfs
->xAccess(orig_vfs
, zName
, flags
, pResOut
);
709 int xFullPathname(sqlite3_vfs
* vfs
, const char* zName
, int nOut
, char* zOut
) {
710 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
711 return orig_vfs
->xFullPathname(orig_vfs
, zName
, nOut
, zOut
);
714 void* xDlOpen(sqlite3_vfs
* vfs
, const char* zFilename
) {
715 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
716 return orig_vfs
->xDlOpen(orig_vfs
, zFilename
);
719 void xDlError(sqlite3_vfs
* vfs
, int nByte
, char* zErrMsg
) {
720 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
721 orig_vfs
->xDlError(orig_vfs
, nByte
, zErrMsg
);
724 void (*xDlSym(sqlite3_vfs
* vfs
, void* pHdle
, const char* zSym
))(void) {
725 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
726 return orig_vfs
->xDlSym(orig_vfs
, pHdle
, zSym
);
729 void xDlClose(sqlite3_vfs
* vfs
, void* pHandle
) {
730 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
731 orig_vfs
->xDlClose(orig_vfs
, pHandle
);
734 int xRandomness(sqlite3_vfs
* vfs
, int nByte
, char* zOut
) {
735 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
736 return orig_vfs
->xRandomness(orig_vfs
, nByte
, zOut
);
739 int xSleep(sqlite3_vfs
* vfs
, int microseconds
) {
740 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
741 return orig_vfs
->xSleep(orig_vfs
, microseconds
);
744 int xCurrentTime(sqlite3_vfs
* vfs
, double* prNow
) {
745 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
746 return orig_vfs
->xCurrentTime(orig_vfs
, prNow
);
749 int xGetLastError(sqlite3_vfs
* vfs
, int nBuf
, char* zBuf
) {
750 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
751 return orig_vfs
->xGetLastError(orig_vfs
, nBuf
, zBuf
);
754 int xCurrentTimeInt64(sqlite3_vfs
* vfs
, sqlite3_int64
* piNow
) {
755 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
756 return orig_vfs
->xCurrentTimeInt64(orig_vfs
, piNow
);
759 static int xSetSystemCall(sqlite3_vfs
* vfs
, const char* zName
,
760 sqlite3_syscall_ptr pFunc
) {
761 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
762 return orig_vfs
->xSetSystemCall(orig_vfs
, zName
, pFunc
);
765 static sqlite3_syscall_ptr
xGetSystemCall(sqlite3_vfs
* vfs
, const char* zName
) {
766 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
767 return orig_vfs
->xGetSystemCall(orig_vfs
, zName
);
770 static const char* xNextSystemCall(sqlite3_vfs
* vfs
, const char* zName
) {
771 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
772 return orig_vfs
->xNextSystemCall(orig_vfs
, zName
);
780 const char* GetVFSName() { return "telemetry-vfs"; }
782 sqlite3_vfs
* ConstructTelemetryVFS() {
784 # define EXPECTED_VFS "win32"
785 # define EXPECTED_VFS_EXCL "win32"
787 # define EXPECTED_VFS "unix"
788 # define EXPECTED_VFS_EXCL "unix-excl"
793 if (Preferences::GetBool(PREF_MULTI_PROCESS_ACCESS
, false)) {
794 // Use the non-exclusive VFS.
795 vfs
= sqlite3_vfs_find(nullptr);
796 expected_vfs
= vfs
->zName
&& !strcmp(vfs
->zName
, EXPECTED_VFS
);
798 vfs
= sqlite3_vfs_find(EXPECTED_VFS_EXCL
);
799 expected_vfs
= (vfs
!= nullptr);
805 sqlite3_vfs
* tvfs
= new ::sqlite3_vfs
;
806 memset(tvfs
, 0, sizeof(::sqlite3_vfs
));
807 // If the VFS version is higher than the last known one, you should update
808 // this VFS adding appropriate methods for any methods added in the version
810 tvfs
->iVersion
= vfs
->iVersion
;
811 MOZ_ASSERT(vfs
->iVersion
<= LAST_KNOWN_VFS_VERSION
);
813 sizeof(telemetry_file
) - sizeof(sqlite3_file
) + vfs
->szOsFile
;
814 tvfs
->mxPathname
= vfs
->mxPathname
;
815 tvfs
->zName
= GetVFSName();
816 tvfs
->pAppData
= vfs
;
818 tvfs
->xDelete
= xDelete
;
819 tvfs
->xAccess
= xAccess
;
820 tvfs
->xFullPathname
= xFullPathname
;
821 tvfs
->xDlOpen
= xDlOpen
;
822 tvfs
->xDlError
= xDlError
;
823 tvfs
->xDlSym
= xDlSym
;
824 tvfs
->xDlClose
= xDlClose
;
825 tvfs
->xRandomness
= xRandomness
;
826 tvfs
->xSleep
= xSleep
;
827 tvfs
->xCurrentTime
= xCurrentTime
;
828 tvfs
->xGetLastError
= xGetLastError
;
829 if (tvfs
->iVersion
>= 2) {
830 // Methods added in version 2.
831 tvfs
->xCurrentTimeInt64
= xCurrentTimeInt64
;
833 if (tvfs
->iVersion
>= 3) {
834 // Methods added in version 3.
835 tvfs
->xSetSystemCall
= xSetSystemCall
;
836 tvfs
->xGetSystemCall
= xGetSystemCall
;
837 tvfs
->xNextSystemCall
= xNextSystemCall
;
842 already_AddRefed
<QuotaObject
> GetQuotaObjectForFile(sqlite3_file
* pFile
) {
845 telemetry_file
* p
= (telemetry_file
*)pFile
;
846 RefPtr
<QuotaObject
> result
= p
->quotaObject
;
847 return result
.forget();
850 } // namespace storage
851 } // namespace mozilla