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"
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
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
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"
43 using namespace mozilla
;
44 using namespace mozilla::dom::quota
;
45 using namespace mozilla::net
;
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) \
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
{
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()),
93 #if defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN)
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)
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) */
141 const TimeStamp start
;
142 const Telemetry::HistogramID id
;
143 #if defined(MOZ_GECKO_PROFILER) && !defined(XP_WIN)
144 IOInterposeObserver::Operation op
;
148 struct telemetry_file
{
149 // Base class. Must be first
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.
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:
175 * - Main Database Path
177 * - Multiple URI components consisting of:
185 * - WAL Path (zWALName)
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.
214 // There should be another null here.
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.
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
);
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
247 const char *const dbPathStart
= dbPath
.BeginReading();
248 const char *dbPathCursor
= dbPath
.EndReading() - 1;
249 bool isDBPath
= true;
252 MOZ_ASSERT(*dbPathCursor
, "dbPathCursor should never see a null char!");
256 dbPathStart
<= dbPathCursor
&& *dbPathCursor
== *cursor
&& *cursor
;
260 // This isn't the database path so it must be a value. Scan past it and
262 for (size_t stringCount
= 0; stringCount
< 2; stringCount
++) {
263 // Scan past the string to the preceding null character.
268 // Back up one more char to the last char of preceding string.
272 // Reset and start again.
273 dbPathCursor
= dbPath
.EndReading() - 1;
279 MOZ_ASSERT(isDBPath
);
282 if (dbPathStart
== dbPathCursor
) {
283 // Found the full database path, we're all done.
284 MOZ_ASSERT(nsDependentCString(cursor
) == dbPath
);
288 // Change the cursors and go through the loop again.
293 MOZ_CRASH("Should never get here!");
296 already_AddRefed
<QuotaObject
> GetQuotaObjectFromNameAndParameters(
297 const char *zName
, const char *zURIParameterKey
) {
299 MOZ_ASSERT(zURIParameterKey
);
301 const char *persistenceType
=
302 sqlite3_uri_parameter(zURIParameterKey
, "persistenceType");
303 if (!persistenceType
) {
307 const char *group
= sqlite3_uri_parameter(zURIParameterKey
, "group");
309 NS_WARNING("SQLite URI had 'persistenceType' but not 'group'?!");
313 const char *origin
= sqlite3_uri_parameter(zURIParameterKey
, "origin");
316 "SQLite URI had 'persistenceType' and 'group' but not "
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
,
333 MOZ_ASSERT(!pFile
->quotaObject
);
335 if (!(flags
& (SQLITE_OPEN_URI
| SQLITE_OPEN_WAL
))) {
341 const char *zURIParameterKey
=
342 (flags
& SQLITE_OPEN_WAL
) ? DatabasePathFromWALPath(zName
) : zName
;
344 MOZ_ASSERT(zURIParameterKey
);
347 GetQuotaObjectFromNameAndParameters(zName
, zURIParameterKey
);
351 ** Close a telemetry_file.
353 int xClose(sqlite3_file
*pFile
) {
354 telemetry_file
*p
= (telemetry_file
*)pFile
;
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
;
366 p
->fileChunkSize
= 0;
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
);
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);
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
;
396 rc
= p
->pReal
->pMethods
->xFileSize(p
->pReal
, pSize
);
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
);
409 if (p
->quotaObject
) {
410 MOZ_ASSERT(INT64_MAX
- iOfst
>= iAmt
);
411 if (!p
->quotaObject
->MaybeUpdateSize(iOfst
+ iAmt
, /* aTruncate */ false)) {
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
) {
423 "xWrite failed on a quota-controlled file, attempting to "
424 "update its current size...");
425 sqlite_int64 currentSize
;
426 if (xFileSize(pFile
, ¤tSize
) == SQLITE_OK
) {
427 p
->quotaObject
->MaybeUpdateSize(currentSize
, /* aTruncate */ true);
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
;
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
446 ((size
+ p
->fileChunkSize
- 1) / p
->fileChunkSize
) * p
->fileChunkSize
;
448 if (!p
->quotaObject
->MaybeUpdateSize(size
, /* aTruncate */ true)) {
452 rc
= p
->pReal
->pMethods
->xTruncate(p
->pReal
, size
);
453 if (p
->quotaObject
) {
454 if (rc
== SQLITE_OK
) {
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
);
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);
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
;
489 rc
= p
->pReal
->pMethods
->xLock(p
->pReal
, eLock
);
494 ** Unlock a telemetry_file.
496 int xUnlock(sqlite3_file
*pFile
, int eLock
) {
497 telemetry_file
*p
= (telemetry_file
*)pFile
;
499 rc
= p
->pReal
->pMethods
->xUnlock(p
->pReal
, eLock
);
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
);
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
;
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
, ¤tSize
);
524 if (rc
!= SQLITE_OK
) {
527 if (hintSize
> currentSize
) {
528 rc
= xTruncate(pFile
, hintSize
);
529 if (rc
!= SQLITE_OK
) {
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
);
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
) *
546 sqlite3_int64 currentSize
;
547 MOZ_ASSERT(xFileSize(pFile
, ¤tSize
) == SQLITE_OK
);
548 MOZ_ASSERT(currentSize
>= hintSize
);
555 ** Return the sector-size in bytes for a telemetry_file.
557 int xSectorSize(sqlite3_file
*pFile
) {
558 telemetry_file
*p
= (telemetry_file
*)pFile
;
560 rc
= p
->pReal
->pMethods
->xSectorSize(p
->pReal
);
565 ** Return the device characteristic flags supported by a telemetry_file.
567 int xDeviceCharacteristics(sqlite3_file
*pFile
) {
568 telemetry_file
*p
= (telemetry_file
*)pFile
;
570 rc
= p
->pReal
->pMethods
->xDeviceCharacteristics(p
->pReal
);
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
;
586 rc
= p
->pReal
->pMethods
->xShmMap(p
->pReal
, iRegion
, szRegion
, isWrite
, pp
);
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
;
598 rc
= p
->pReal
->pMethods
->xShmUnmap(p
->pReal
, delFlag
);
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
,
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
);
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
++) {
626 // last probe is the fallback probe
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;
637 MaybeEstablishQuotaControl(zName
, p
, flags
);
639 rc
= orig_vfs
->xOpen(orig_vfs
, zName
, p
->pReal
, flags
, pOutFlags
);
640 if (rc
!= SQLITE_OK
) return rc
;
643 p
->location
= new char[7 + strlen(zName
) + 1];
644 strcpy(p
->location
, "file://");
645 strcpy(p
->location
+ 7, zName
);
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
;
662 pNew
->xWrite
= xWrite
;
663 pNew
->xTruncate
= xTruncate
;
665 pNew
->xFileSize
= xFileSize
;
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
684 MOZ_ASSERT(pSub
->xFetch
);
685 pNew
->xFetch
= xFetch
;
686 MOZ_ASSERT(pSub
->xUnfetch
);
687 pNew
->xUnfetch
= xUnfetch
;
689 pFile
->pMethods
= pNew
;
694 int xDelete(sqlite3_vfs
*vfs
, const char *zName
, int syncDir
) {
695 sqlite3_vfs
*orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
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));
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
);
790 const char *GetVFSName() { return "telemetry-vfs"; }
792 sqlite3_vfs
*ConstructTelemetryVFS() {
794 #define EXPECTED_VFS "win32"
795 #define EXPECTED_VFS_EXCL "win32"
797 #define EXPECTED_VFS "unix"
798 #define EXPECTED_VFS_EXCL "unix-excl"
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
);
808 vfs
= sqlite3_vfs_find(EXPECTED_VFS_EXCL
);
809 expected_vfs
= (vfs
!= 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
820 tvfs
->iVersion
= vfs
->iVersion
;
821 MOZ_ASSERT(vfs
->iVersion
<= LAST_KNOWN_VFS_VERSION
);
823 sizeof(telemetry_file
) - sizeof(sqlite3_file
) + vfs
->szOsFile
;
824 tvfs
->mxPathname
= vfs
->mxPathname
;
825 tvfs
->zName
= GetVFSName();
826 tvfs
->pAppData
= vfs
;
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
;
852 already_AddRefed
<QuotaObject
> GetQuotaObjectForFile(sqlite3_file
*pFile
) {
855 telemetry_file
*p
= (telemetry_file
*)pFile
;
856 RefPtr
<QuotaObject
> result
= p
->quotaObject
;
857 return result
.forget();
860 } // namespace storage
861 } // namespace mozilla