Bug 1762395 [wpt PR 33447] - Update idlharness.js tests for new File System standard...
[gecko.git] / storage / TelemetryVFS.cpp
blob65d794e0dc3b49d6eab406daa02118ed671e86cd
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 "sqlite3.h"
10 #include "nsThreadUtils.h"
11 #include "mozilla/dom/quota/PersistenceType.h"
12 #include "mozilla/dom/quota/QuotaManager.h"
13 #include "mozilla/dom/quota/QuotaObject.h"
14 #include "mozilla/net/IOActivityMonitor.h"
15 #include "mozilla/IOInterposer.h"
16 #include "nsEscape.h"
17 #include "mozilla/StaticPrefs_storage.h"
19 #ifdef XP_WIN
20 # include "mozilla/StaticPrefs_dom.h"
21 #endif
23 // The last VFS version for which this file has been updated.
24 #define LAST_KNOWN_VFS_VERSION 3
26 // The last io_methods version for which this file has been updated.
27 #define LAST_KNOWN_IOMETHODS_VERSION 3
29 namespace {
31 using namespace mozilla;
32 using namespace mozilla::dom::quota;
33 using namespace mozilla::net;
35 struct Histograms {
36 const char* name;
37 const Telemetry::HistogramID readB;
38 const Telemetry::HistogramID writeB;
39 const Telemetry::HistogramID readMS;
40 const Telemetry::HistogramID writeMS;
41 const Telemetry::HistogramID syncMS;
44 #define SQLITE_TELEMETRY(FILENAME, HGRAM) \
45 { \
46 FILENAME, Telemetry::MOZ_SQLITE_##HGRAM##_READ_B, \
47 Telemetry::MOZ_SQLITE_##HGRAM##_WRITE_B, \
48 Telemetry::MOZ_SQLITE_##HGRAM##_READ_MS, \
49 Telemetry::MOZ_SQLITE_##HGRAM##_WRITE_MS, \
50 Telemetry::MOZ_SQLITE_##HGRAM##_SYNC_MS \
53 Histograms gHistograms[] = {SQLITE_TELEMETRY("places.sqlite", PLACES),
54 SQLITE_TELEMETRY("cookies.sqlite", COOKIES),
55 SQLITE_TELEMETRY("webappsstore.sqlite", WEBAPPS),
56 SQLITE_TELEMETRY(nullptr, OTHER)};
57 #undef SQLITE_TELEMETRY
59 /** RAII class for measuring how long io takes on/off main thread
61 class IOThreadAutoTimer {
62 public:
63 /**
64 * IOThreadAutoTimer measures time spent in IO. Additionally it
65 * automatically determines whether IO is happening on the main
66 * thread and picks an appropriate histogram.
68 * @param id takes a telemetry histogram id. The id+1 must be an
69 * equivalent histogram for the main thread. Eg, MOZ_SQLITE_OPEN_MS
70 * is followed by MOZ_SQLITE_OPEN_MAIN_THREAD_MS.
72 * @param aOp optionally takes an IO operation to report through the
73 * IOInterposer. Filename will be reported as NULL, and reference will be
74 * either "sqlite-mainthread" or "sqlite-otherthread".
76 explicit IOThreadAutoTimer(
77 Telemetry::HistogramID aId,
78 IOInterposeObserver::Operation aOp = IOInterposeObserver::OpNone)
79 : start(TimeStamp::Now()),
80 id(aId)
81 #if !defined(XP_WIN)
83 op(aOp)
84 #endif
88 /**
89 * This constructor is for when we want to report an operation to
90 * IOInterposer but do not require a telemetry probe.
92 * @param aOp IO Operation to report through the IOInterposer.
94 explicit IOThreadAutoTimer(IOInterposeObserver::Operation aOp)
95 : start(TimeStamp::Now()),
96 id(Telemetry::HistogramCount)
97 #if !defined(XP_WIN)
99 op(aOp)
100 #endif
104 ~IOThreadAutoTimer() {
105 TimeStamp end(TimeStamp::Now());
106 uint32_t mainThread = NS_IsMainThread() ? 1 : 0;
107 if (id != Telemetry::HistogramCount) {
108 Telemetry::AccumulateTimeDelta(
109 static_cast<Telemetry::HistogramID>(id + mainThread), start, end);
111 // We don't report SQLite I/O on Windows because we have a comprehensive
112 // mechanism for intercepting I/O on that platform that captures a superset
113 // of the data captured here.
114 #if !defined(XP_WIN)
115 if (IOInterposer::IsObservedOperation(op)) {
116 const char* main_ref = "sqlite-mainthread";
117 const char* other_ref = "sqlite-otherthread";
119 // Create observation
120 IOInterposeObserver::Observation ob(op, start, end,
121 (mainThread ? main_ref : other_ref));
122 // Report observation
123 IOInterposer::Report(ob);
125 #endif /* !defined(XP_WIN) */
128 private:
129 const TimeStamp start;
130 const Telemetry::HistogramID id;
131 #if !defined(XP_WIN)
132 IOInterposeObserver::Operation op;
133 #endif
136 struct telemetry_file {
137 // Base class. Must be first
138 sqlite3_file base;
140 // histograms pertaining to this file
141 Histograms* histograms;
143 // quota object for this file
144 RefPtr<QuotaObject> quotaObject;
146 // The chunk size for this file. See the documentation for
147 // sqlite3_file_control() and FCNTL_CHUNK_SIZE.
148 int fileChunkSize;
150 // The filename
151 char* location;
153 // This contains the vfs that actually does work
154 sqlite3_file pReal[1];
157 already_AddRefed<QuotaObject> GetQuotaObjectFromName(const char* zName) {
158 MOZ_ASSERT(zName);
160 const char* directoryLockIdParam =
161 sqlite3_uri_parameter(zName, "directoryLockId");
162 if (!directoryLockIdParam) {
163 return nullptr;
166 nsresult rv;
167 const int64_t directoryLockId =
168 nsDependentCString(directoryLockIdParam).ToInteger64(&rv);
169 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
171 QuotaManager* quotaManager = QuotaManager::Get();
172 MOZ_ASSERT(quotaManager);
174 return quotaManager->GetQuotaObject(directoryLockId,
175 NS_ConvertUTF8toUTF16(zName));
178 void MaybeEstablishQuotaControl(const char* zName, telemetry_file* pFile,
179 int flags) {
180 MOZ_ASSERT(pFile);
181 MOZ_ASSERT(!pFile->quotaObject);
183 if (!(flags & (SQLITE_OPEN_URI | SQLITE_OPEN_WAL))) {
184 return;
186 pFile->quotaObject = GetQuotaObjectFromName(zName);
190 ** Close a telemetry_file.
192 int xClose(sqlite3_file* pFile) {
193 telemetry_file* p = (telemetry_file*)pFile;
194 int rc;
195 { // Scope for IOThreadAutoTimer
196 IOThreadAutoTimer ioTimer(IOInterposeObserver::OpClose);
197 rc = p->pReal->pMethods->xClose(p->pReal);
199 if (rc == SQLITE_OK) {
200 delete p->base.pMethods;
201 p->base.pMethods = nullptr;
202 p->quotaObject = nullptr;
203 delete[] p->location;
204 #ifdef DEBUG
205 p->fileChunkSize = 0;
206 #endif
208 return rc;
212 ** Read data from a telemetry_file.
214 int xRead(sqlite3_file* pFile, void* zBuf, int iAmt, sqlite_int64 iOfst) {
215 telemetry_file* p = (telemetry_file*)pFile;
216 IOThreadAutoTimer ioTimer(p->histograms->readMS, IOInterposeObserver::OpRead);
217 int rc;
218 rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
219 if (rc == SQLITE_OK && IOActivityMonitor::IsActive()) {
220 IOActivityMonitor::Read(nsDependentCString(p->location), iAmt);
222 // sqlite likes to read from empty files, this is normal, ignore it.
223 if (rc != SQLITE_IOERR_SHORT_READ)
224 Telemetry::Accumulate(p->histograms->readB, rc == SQLITE_OK ? iAmt : 0);
225 return rc;
229 ** Return the current file-size of a telemetry_file.
231 int xFileSize(sqlite3_file* pFile, sqlite_int64* pSize) {
232 IOThreadAutoTimer ioTimer(IOInterposeObserver::OpStat);
233 telemetry_file* p = (telemetry_file*)pFile;
234 int rc;
235 rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
236 return rc;
240 ** Write data to a telemetry_file.
242 int xWrite(sqlite3_file* pFile, const void* zBuf, int iAmt,
243 sqlite_int64 iOfst) {
244 telemetry_file* p = (telemetry_file*)pFile;
245 IOThreadAutoTimer ioTimer(p->histograms->writeMS,
246 IOInterposeObserver::OpWrite);
247 int rc;
248 if (p->quotaObject) {
249 MOZ_ASSERT(INT64_MAX - iOfst >= iAmt);
250 if (!p->quotaObject->MaybeUpdateSize(iOfst + iAmt, /* aTruncate */ false)) {
251 return SQLITE_FULL;
254 rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
255 if (rc == SQLITE_OK && IOActivityMonitor::IsActive()) {
256 IOActivityMonitor::Write(nsDependentCString(p->location), iAmt);
259 Telemetry::Accumulate(p->histograms->writeB, rc == SQLITE_OK ? iAmt : 0);
260 if (p->quotaObject && rc != SQLITE_OK) {
261 NS_WARNING(
262 "xWrite failed on a quota-controlled file, attempting to "
263 "update its current size...");
264 sqlite_int64 currentSize;
265 if (xFileSize(pFile, &currentSize) == SQLITE_OK) {
266 DebugOnly<bool> res =
267 p->quotaObject->MaybeUpdateSize(currentSize, /* aTruncate */ true);
268 MOZ_ASSERT(res);
271 return rc;
275 ** Truncate a telemetry_file.
277 int xTruncate(sqlite3_file* pFile, sqlite_int64 size) {
278 IOThreadAutoTimer ioTimer(Telemetry::MOZ_SQLITE_TRUNCATE_MS);
279 telemetry_file* p = (telemetry_file*)pFile;
280 int rc;
281 Telemetry::AutoTimer<Telemetry::MOZ_SQLITE_TRUNCATE_MS> timer;
282 if (p->quotaObject) {
283 if (p->fileChunkSize > 0) {
284 // Round up to the smallest multiple of the chunk size that will hold all
285 // the data.
286 size =
287 ((size + p->fileChunkSize - 1) / p->fileChunkSize) * p->fileChunkSize;
289 if (!p->quotaObject->MaybeUpdateSize(size, /* aTruncate */ true)) {
290 return SQLITE_FULL;
293 rc = p->pReal->pMethods->xTruncate(p->pReal, size);
294 if (p->quotaObject) {
295 if (rc == SQLITE_OK) {
296 #ifdef DEBUG
297 // Make sure xTruncate set the size exactly as we calculated above.
298 sqlite_int64 newSize;
299 MOZ_ASSERT(xFileSize(pFile, &newSize) == SQLITE_OK);
300 MOZ_ASSERT(newSize == size);
301 #endif
302 } else {
303 NS_WARNING(
304 "xTruncate failed on a quota-controlled file, attempting to "
305 "update its current size...");
306 if (xFileSize(pFile, &size) == SQLITE_OK) {
307 DebugOnly<bool> res =
308 p->quotaObject->MaybeUpdateSize(size, /* aTruncate */ true);
309 MOZ_ASSERT(res);
313 return rc;
317 ** Sync a telemetry_file.
319 int xSync(sqlite3_file* pFile, int flags) {
320 telemetry_file* p = (telemetry_file*)pFile;
321 IOThreadAutoTimer ioTimer(p->histograms->syncMS,
322 IOInterposeObserver::OpFSync);
323 return p->pReal->pMethods->xSync(p->pReal, flags);
327 ** Lock a telemetry_file.
329 int xLock(sqlite3_file* pFile, int eLock) {
330 telemetry_file* p = (telemetry_file*)pFile;
331 int rc;
332 rc = p->pReal->pMethods->xLock(p->pReal, eLock);
333 return rc;
337 ** Unlock a telemetry_file.
339 int xUnlock(sqlite3_file* pFile, int eLock) {
340 telemetry_file* p = (telemetry_file*)pFile;
341 int rc;
342 rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
343 return rc;
347 ** Check if another file-handle holds a RESERVED lock on a telemetry_file.
349 int xCheckReservedLock(sqlite3_file* pFile, int* pResOut) {
350 telemetry_file* p = (telemetry_file*)pFile;
351 int rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
352 return rc;
356 ** File control method. For custom operations on a telemetry_file.
358 int xFileControl(sqlite3_file* pFile, int op, void* pArg) {
359 telemetry_file* p = (telemetry_file*)pFile;
360 int rc;
361 // Hook SQLITE_FCNTL_SIZE_HINT for quota-controlled files and do the necessary
362 // work before passing to the SQLite VFS.
363 if (op == SQLITE_FCNTL_SIZE_HINT && p->quotaObject) {
364 sqlite3_int64 hintSize = *static_cast<sqlite3_int64*>(pArg);
365 sqlite3_int64 currentSize;
366 rc = xFileSize(pFile, &currentSize);
367 if (rc != SQLITE_OK) {
368 return rc;
370 if (hintSize > currentSize) {
371 rc = xTruncate(pFile, hintSize);
372 if (rc != SQLITE_OK) {
373 return rc;
377 rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
378 // Grab the file chunk size after the SQLite VFS has approved.
379 if (op == SQLITE_FCNTL_CHUNK_SIZE && rc == SQLITE_OK) {
380 p->fileChunkSize = *static_cast<int*>(pArg);
382 #ifdef DEBUG
383 if (op == SQLITE_FCNTL_SIZE_HINT && p->quotaObject && rc == SQLITE_OK) {
384 sqlite3_int64 hintSize = *static_cast<sqlite3_int64*>(pArg);
385 if (p->fileChunkSize > 0) {
386 hintSize = ((hintSize + p->fileChunkSize - 1) / p->fileChunkSize) *
387 p->fileChunkSize;
389 sqlite3_int64 currentSize;
390 MOZ_ASSERT(xFileSize(pFile, &currentSize) == SQLITE_OK);
391 MOZ_ASSERT(currentSize >= hintSize);
393 #endif
394 return rc;
398 ** Return the sector-size in bytes for a telemetry_file.
400 int xSectorSize(sqlite3_file* pFile) {
401 telemetry_file* p = (telemetry_file*)pFile;
402 int rc;
403 rc = p->pReal->pMethods->xSectorSize(p->pReal);
404 return rc;
408 ** Return the device characteristic flags supported by a telemetry_file.
410 int xDeviceCharacteristics(sqlite3_file* pFile) {
411 telemetry_file* p = (telemetry_file*)pFile;
412 int rc;
413 rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
414 return rc;
418 ** Shared-memory operations.
420 int xShmLock(sqlite3_file* pFile, int ofst, int n, int flags) {
421 telemetry_file* p = (telemetry_file*)pFile;
422 return p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
425 int xShmMap(sqlite3_file* pFile, int iRegion, int szRegion, int isWrite,
426 void volatile** pp) {
427 telemetry_file* p = (telemetry_file*)pFile;
428 int rc;
429 rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
430 return rc;
433 void xShmBarrier(sqlite3_file* pFile) {
434 telemetry_file* p = (telemetry_file*)pFile;
435 p->pReal->pMethods->xShmBarrier(p->pReal);
438 int xShmUnmap(sqlite3_file* pFile, int delFlag) {
439 telemetry_file* p = (telemetry_file*)pFile;
440 int rc;
441 rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
442 return rc;
445 int xFetch(sqlite3_file* pFile, sqlite3_int64 iOff, int iAmt, void** pp) {
446 telemetry_file* p = (telemetry_file*)pFile;
447 MOZ_ASSERT(p->pReal->pMethods->iVersion >= 3);
448 return p->pReal->pMethods->xFetch(p->pReal, iOff, iAmt, pp);
451 int xUnfetch(sqlite3_file* pFile, sqlite3_int64 iOff, void* pResOut) {
452 telemetry_file* p = (telemetry_file*)pFile;
453 MOZ_ASSERT(p->pReal->pMethods->iVersion >= 3);
454 return p->pReal->pMethods->xUnfetch(p->pReal, iOff, pResOut);
457 int xOpen(sqlite3_vfs* vfs, const char* zName, sqlite3_file* pFile, int flags,
458 int* pOutFlags) {
459 IOThreadAutoTimer ioTimer(Telemetry::MOZ_SQLITE_OPEN_MS,
460 IOInterposeObserver::OpCreateOrOpen);
461 Telemetry::AutoTimer<Telemetry::MOZ_SQLITE_OPEN_MS> timer;
462 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
463 int rc;
464 telemetry_file* p = (telemetry_file*)pFile;
465 Histograms* h = nullptr;
466 // check if the filename is one we are probing for
467 for (size_t i = 0; i < sizeof(gHistograms) / sizeof(gHistograms[0]); i++) {
468 h = &gHistograms[i];
469 // last probe is the fallback probe
470 if (!h->name) break;
471 if (!zName) continue;
472 const char* match = strstr(zName, h->name);
473 if (!match) continue;
474 char c = match[strlen(h->name)];
475 // include -wal/-journal too
476 if (!c || c == '-') break;
478 p->histograms = h;
480 MaybeEstablishQuotaControl(zName, p, flags);
482 rc = orig_vfs->xOpen(orig_vfs, zName, p->pReal, flags, pOutFlags);
483 if (rc != SQLITE_OK) return rc;
485 if (zName) {
486 p->location = new char[7 + strlen(zName) + 1];
487 strcpy(p->location, "file://");
488 strcpy(p->location + 7, zName);
489 } else {
490 p->location = new char[8];
491 strcpy(p->location, "file://");
494 if (p->pReal->pMethods) {
495 sqlite3_io_methods* pNew = new sqlite3_io_methods;
496 const sqlite3_io_methods* pSub = p->pReal->pMethods;
497 memset(pNew, 0, sizeof(*pNew));
498 // If the io_methods version is higher than the last known one, you should
499 // update this VFS adding appropriate IO methods for any methods added in
500 // the version change.
501 pNew->iVersion = pSub->iVersion;
502 MOZ_ASSERT(pNew->iVersion <= LAST_KNOWN_IOMETHODS_VERSION);
503 pNew->xClose = xClose;
504 pNew->xRead = xRead;
505 pNew->xWrite = xWrite;
506 pNew->xTruncate = xTruncate;
507 pNew->xSync = xSync;
508 pNew->xFileSize = xFileSize;
509 pNew->xLock = xLock;
510 pNew->xUnlock = xUnlock;
511 pNew->xCheckReservedLock = xCheckReservedLock;
512 pNew->xFileControl = xFileControl;
513 pNew->xSectorSize = xSectorSize;
514 pNew->xDeviceCharacteristics = xDeviceCharacteristics;
515 if (pNew->iVersion >= 2) {
516 // Methods added in version 2.
517 pNew->xShmMap = pSub->xShmMap ? xShmMap : 0;
518 pNew->xShmLock = pSub->xShmLock ? xShmLock : 0;
519 pNew->xShmBarrier = pSub->xShmBarrier ? xShmBarrier : 0;
520 pNew->xShmUnmap = pSub->xShmUnmap ? xShmUnmap : 0;
522 if (pNew->iVersion >= 3) {
523 // Methods added in version 3.
524 // SQLite 3.7.17 calls these methods without checking for nullptr first,
525 // so we always define them. Verify that we're not going to call
526 // nullptrs, though.
527 MOZ_ASSERT(pSub->xFetch);
528 pNew->xFetch = xFetch;
529 MOZ_ASSERT(pSub->xUnfetch);
530 pNew->xUnfetch = xUnfetch;
532 pFile->pMethods = pNew;
534 return rc;
537 int xDelete(sqlite3_vfs* vfs, const char* zName, int syncDir) {
538 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
539 int rc;
540 RefPtr<QuotaObject> quotaObject;
542 if (StringEndsWith(nsDependentCString(zName), "-wal"_ns)) {
543 quotaObject = GetQuotaObjectFromName(zName);
546 rc = orig_vfs->xDelete(orig_vfs, zName, syncDir);
547 if (rc == SQLITE_OK && quotaObject) {
548 MOZ_ALWAYS_TRUE(quotaObject->MaybeUpdateSize(0, /* aTruncate */ true));
551 return rc;
554 int xAccess(sqlite3_vfs* vfs, const char* zName, int flags, int* pResOut) {
555 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
556 return orig_vfs->xAccess(orig_vfs, zName, flags, pResOut);
559 int xFullPathname(sqlite3_vfs* vfs, const char* zName, int nOut, char* zOut) {
560 #if defined(XP_WIN)
561 // SQLite uses GetFullPathnameW which also normailizes file path. If a file
562 // component ends with a dot, it would be removed. However, it's not desired.
564 // And that would result SQLite uses wrong database and quotaObject.
565 // Note that we are safe to avoid the GetFullPathnameW call for \\?\ prefixed
566 // paths.
567 // And note that this hack will be removed once the issue is fixed directly in
568 // SQLite.
570 // zName that starts with "//?/" is the case when a file URI was passed and
571 // zName that starts with "\\?\" is the case when a normal path was passed
572 // (not file URI).
573 if (StaticPrefs::dom_quotaManager_overrideXFullPathname() &&
574 ((zName[0] == '/' && zName[1] == '/' && zName[2] == '?' &&
575 zName[3] == '/') ||
576 (zName[0] == '\\' && zName[1] == '\\' && zName[2] == '?' &&
577 zName[3] == '\\'))) {
578 MOZ_ASSERT(nOut >= vfs->mxPathname);
579 MOZ_ASSERT(nOut > strlen(zName));
581 size_t index = 0;
582 while (zName[index] != '\0') {
583 if (zName[index] == '/') {
584 zOut[index] = '\\';
585 } else {
586 zOut[index] = zName[index];
589 index++;
591 zOut[index] = '\0';
593 return SQLITE_OK;
595 #endif
597 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
598 return orig_vfs->xFullPathname(orig_vfs, zName, nOut, zOut);
601 void* xDlOpen(sqlite3_vfs* vfs, const char* zFilename) {
602 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
603 return orig_vfs->xDlOpen(orig_vfs, zFilename);
606 void xDlError(sqlite3_vfs* vfs, int nByte, char* zErrMsg) {
607 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
608 orig_vfs->xDlError(orig_vfs, nByte, zErrMsg);
611 void (*xDlSym(sqlite3_vfs* vfs, void* pHdle, const char* zSym))(void) {
612 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
613 return orig_vfs->xDlSym(orig_vfs, pHdle, zSym);
616 void xDlClose(sqlite3_vfs* vfs, void* pHandle) {
617 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
618 orig_vfs->xDlClose(orig_vfs, pHandle);
621 int xRandomness(sqlite3_vfs* vfs, int nByte, char* zOut) {
622 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
623 return orig_vfs->xRandomness(orig_vfs, nByte, zOut);
626 int xSleep(sqlite3_vfs* vfs, int microseconds) {
627 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
628 return orig_vfs->xSleep(orig_vfs, microseconds);
631 int xCurrentTime(sqlite3_vfs* vfs, double* prNow) {
632 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
633 return orig_vfs->xCurrentTime(orig_vfs, prNow);
636 int xGetLastError(sqlite3_vfs* vfs, int nBuf, char* zBuf) {
637 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
638 return orig_vfs->xGetLastError(orig_vfs, nBuf, zBuf);
641 int xCurrentTimeInt64(sqlite3_vfs* vfs, sqlite3_int64* piNow) {
642 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
643 return orig_vfs->xCurrentTimeInt64(orig_vfs, piNow);
646 static int xSetSystemCall(sqlite3_vfs* vfs, const char* zName,
647 sqlite3_syscall_ptr pFunc) {
648 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
649 return orig_vfs->xSetSystemCall(orig_vfs, zName, pFunc);
652 static sqlite3_syscall_ptr xGetSystemCall(sqlite3_vfs* vfs, const char* zName) {
653 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
654 return orig_vfs->xGetSystemCall(orig_vfs, zName);
657 static const char* xNextSystemCall(sqlite3_vfs* vfs, const char* zName) {
658 sqlite3_vfs* orig_vfs = static_cast<sqlite3_vfs*>(vfs->pAppData);
659 return orig_vfs->xNextSystemCall(orig_vfs, zName);
662 } // namespace
664 namespace mozilla {
665 namespace storage {
667 const char* GetTelemetryVFSName(bool exclusive) {
668 return exclusive ? "telemetry-vfs-excl" : "telemetry-vfs";
671 UniquePtr<sqlite3_vfs> ConstructTelemetryVFS(bool exclusive) {
672 #if defined(XP_WIN)
673 # define EXPECTED_VFS "win32"
674 # define EXPECTED_VFS_EXCL "win32"
675 #else
676 # define EXPECTED_VFS "unix"
677 # define EXPECTED_VFS_EXCL "unix-excl"
678 #endif
680 bool expected_vfs;
681 sqlite3_vfs* vfs;
682 if (!exclusive) {
683 // Use the non-exclusive VFS.
684 vfs = sqlite3_vfs_find(nullptr);
685 expected_vfs = vfs->zName && !strcmp(vfs->zName, EXPECTED_VFS);
686 } else {
687 vfs = sqlite3_vfs_find(EXPECTED_VFS_EXCL);
688 expected_vfs = (vfs != nullptr);
690 if (!expected_vfs) {
691 return nullptr;
694 auto tvfs = MakeUnique<::sqlite3_vfs>();
695 memset(tvfs.get(), 0, sizeof(::sqlite3_vfs));
696 // If the VFS version is higher than the last known one, you should update
697 // this VFS adding appropriate methods for any methods added in the version
698 // change.
699 tvfs->iVersion = vfs->iVersion;
700 MOZ_ASSERT(vfs->iVersion <= LAST_KNOWN_VFS_VERSION);
701 tvfs->szOsFile =
702 sizeof(telemetry_file) - sizeof(sqlite3_file) + vfs->szOsFile;
703 tvfs->mxPathname = vfs->mxPathname;
704 tvfs->zName = GetTelemetryVFSName(exclusive);
705 tvfs->pAppData = vfs;
706 tvfs->xOpen = xOpen;
707 tvfs->xDelete = xDelete;
708 tvfs->xAccess = xAccess;
709 tvfs->xFullPathname = xFullPathname;
710 tvfs->xDlOpen = xDlOpen;
711 tvfs->xDlError = xDlError;
712 tvfs->xDlSym = xDlSym;
713 tvfs->xDlClose = xDlClose;
714 tvfs->xRandomness = xRandomness;
715 tvfs->xSleep = xSleep;
716 tvfs->xCurrentTime = xCurrentTime;
717 tvfs->xGetLastError = xGetLastError;
718 if (tvfs->iVersion >= 2) {
719 // Methods added in version 2.
720 tvfs->xCurrentTimeInt64 = xCurrentTimeInt64;
722 if (tvfs->iVersion >= 3) {
723 // Methods added in version 3.
724 tvfs->xSetSystemCall = xSetSystemCall;
725 tvfs->xGetSystemCall = xGetSystemCall;
726 tvfs->xNextSystemCall = xNextSystemCall;
728 return tvfs;
731 already_AddRefed<QuotaObject> GetQuotaObjectForFile(sqlite3_file* pFile) {
732 MOZ_ASSERT(pFile);
734 telemetry_file* p = (telemetry_file*)pFile;
735 RefPtr<QuotaObject> result = p->quotaObject;
736 return result.forget();
739 } // namespace storage
740 } // namespace mozilla