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"
10 #include "nsThreadUtils.h"
11 #include "mozilla/net/IOActivityMonitor.h"
12 #include "mozilla/IOInterposer.h"
14 // The last VFS version for which this file has been updated.
15 #define LAST_KNOWN_VFS_VERSION 3
17 // The last io_methods version for which this file has been updated.
18 #define LAST_KNOWN_IOMETHODS_VERSION 3
22 using namespace mozilla
;
23 using namespace mozilla::net
;
27 const Telemetry::HistogramID readB
;
28 const Telemetry::HistogramID writeB
;
29 const Telemetry::HistogramID readMS
;
30 const Telemetry::HistogramID writeMS
;
31 const Telemetry::HistogramID syncMS
;
34 #define SQLITE_TELEMETRY(FILENAME, HGRAM) \
36 FILENAME, Telemetry::MOZ_SQLITE_##HGRAM##_READ_B, \
37 Telemetry::MOZ_SQLITE_##HGRAM##_WRITE_B, \
38 Telemetry::MOZ_SQLITE_##HGRAM##_READ_MS, \
39 Telemetry::MOZ_SQLITE_##HGRAM##_WRITE_MS, \
40 Telemetry::MOZ_SQLITE_##HGRAM##_SYNC_MS \
43 Histograms gHistograms
[] = {SQLITE_TELEMETRY("places.sqlite", PLACES
),
44 SQLITE_TELEMETRY("cookies.sqlite", COOKIES
),
45 SQLITE_TELEMETRY("webappsstore.sqlite", WEBAPPS
),
46 SQLITE_TELEMETRY(nullptr, OTHER
)};
47 #undef SQLITE_TELEMETRY
49 /** RAII class for measuring how long io takes on/off main thread
51 class IOThreadAutoTimer
{
54 * IOThreadAutoTimer measures time spent in IO. Additionally it
55 * automatically determines whether IO is happening on the main
56 * thread and picks an appropriate histogram.
58 * @param id takes a telemetry histogram id. The id+1 must be an
59 * equivalent histogram for the main thread. Eg, MOZ_SQLITE_OPEN_MS
60 * is followed by MOZ_SQLITE_OPEN_MAIN_THREAD_MS.
62 * @param aOp optionally takes an IO operation to report through the
63 * IOInterposer. Filename will be reported as NULL, and reference will be
64 * either "sqlite-mainthread" or "sqlite-otherthread".
66 explicit IOThreadAutoTimer(
67 Telemetry::HistogramID aId
,
68 IOInterposeObserver::Operation aOp
= IOInterposeObserver::OpNone
)
69 : start(TimeStamp::Now()),
79 * This constructor is for when we want to report an operation to
80 * IOInterposer but do not require a telemetry probe.
82 * @param aOp IO Operation to report through the IOInterposer.
84 explicit IOThreadAutoTimer(IOInterposeObserver::Operation aOp
)
85 : start(TimeStamp::Now()),
86 id(Telemetry::HistogramCount
)
94 ~IOThreadAutoTimer() {
95 TimeStamp
end(TimeStamp::Now());
96 uint32_t mainThread
= NS_IsMainThread() ? 1 : 0;
97 if (id
!= Telemetry::HistogramCount
) {
98 Telemetry::AccumulateTimeDelta(
99 static_cast<Telemetry::HistogramID
>(id
+ mainThread
), start
, end
);
101 // We don't report SQLite I/O on Windows because we have a comprehensive
102 // mechanism for intercepting I/O on that platform that captures a superset
103 // of the data captured here.
105 if (IOInterposer::IsObservedOperation(op
)) {
106 const char* main_ref
= "sqlite-mainthread";
107 const char* other_ref
= "sqlite-otherthread";
109 // Create observation
110 IOInterposeObserver::Observation
ob(op
, start
, end
,
111 (mainThread
? main_ref
: other_ref
));
112 // Report observation
113 IOInterposer::Report(ob
);
115 #endif /* !defined(XP_WIN) */
119 const TimeStamp start
;
120 const Telemetry::HistogramID id
;
122 IOInterposeObserver::Operation op
;
126 struct telemetry_file
{
127 // Base class. Must be first
130 // histograms pertaining to this file
131 Histograms
* histograms
;
136 // This contains the vfs that actually does work
137 sqlite3_file pReal
[1];
141 ** Close a telemetry_file.
143 int xClose(sqlite3_file
* pFile
) {
144 telemetry_file
* p
= (telemetry_file
*)pFile
;
146 { // Scope for IOThreadAutoTimer
147 IOThreadAutoTimer
ioTimer(IOInterposeObserver::OpClose
);
148 rc
= p
->pReal
->pMethods
->xClose(p
->pReal
);
150 if (rc
== SQLITE_OK
) {
151 delete p
->base
.pMethods
;
152 p
->base
.pMethods
= nullptr;
153 delete[] p
->location
;
159 ** Read data from a telemetry_file.
161 int xRead(sqlite3_file
* pFile
, void* zBuf
, int iAmt
, sqlite_int64 iOfst
) {
162 telemetry_file
* p
= (telemetry_file
*)pFile
;
163 IOThreadAutoTimer
ioTimer(p
->histograms
->readMS
, IOInterposeObserver::OpRead
);
165 rc
= p
->pReal
->pMethods
->xRead(p
->pReal
, zBuf
, iAmt
, iOfst
);
166 if (rc
== SQLITE_OK
&& IOActivityMonitor::IsActive()) {
167 IOActivityMonitor::Read(nsDependentCString(p
->location
), iAmt
);
169 // sqlite likes to read from empty files, this is normal, ignore it.
170 if (rc
!= SQLITE_IOERR_SHORT_READ
)
171 Telemetry::Accumulate(p
->histograms
->readB
, rc
== SQLITE_OK
? iAmt
: 0);
176 ** Return the current file-size of a telemetry_file.
178 int xFileSize(sqlite3_file
* pFile
, sqlite_int64
* pSize
) {
179 IOThreadAutoTimer
ioTimer(IOInterposeObserver::OpStat
);
180 telemetry_file
* p
= (telemetry_file
*)pFile
;
182 rc
= p
->pReal
->pMethods
->xFileSize(p
->pReal
, pSize
);
187 ** Write data to a telemetry_file.
189 int xWrite(sqlite3_file
* pFile
, const void* zBuf
, int iAmt
,
190 sqlite_int64 iOfst
) {
191 telemetry_file
* p
= (telemetry_file
*)pFile
;
192 IOThreadAutoTimer
ioTimer(p
->histograms
->writeMS
,
193 IOInterposeObserver::OpWrite
);
195 rc
= p
->pReal
->pMethods
->xWrite(p
->pReal
, zBuf
, iAmt
, iOfst
);
196 if (rc
== SQLITE_OK
&& IOActivityMonitor::IsActive()) {
197 IOActivityMonitor::Write(nsDependentCString(p
->location
), iAmt
);
199 Telemetry::Accumulate(p
->histograms
->writeB
, rc
== SQLITE_OK
? iAmt
: 0);
204 ** Truncate a telemetry_file.
206 int xTruncate(sqlite3_file
* pFile
, sqlite_int64 size
) {
207 IOThreadAutoTimer
ioTimer(Telemetry::MOZ_SQLITE_TRUNCATE_MS
);
208 telemetry_file
* p
= (telemetry_file
*)pFile
;
210 Telemetry::AutoTimer
<Telemetry::MOZ_SQLITE_TRUNCATE_MS
> timer
;
211 rc
= p
->pReal
->pMethods
->xTruncate(p
->pReal
, size
);
216 ** Sync a telemetry_file.
218 int xSync(sqlite3_file
* pFile
, int flags
) {
219 telemetry_file
* p
= (telemetry_file
*)pFile
;
220 IOThreadAutoTimer
ioTimer(p
->histograms
->syncMS
,
221 IOInterposeObserver::OpFSync
);
222 return p
->pReal
->pMethods
->xSync(p
->pReal
, flags
);
226 ** Lock a telemetry_file.
228 int xLock(sqlite3_file
* pFile
, int eLock
) {
229 telemetry_file
* p
= (telemetry_file
*)pFile
;
231 rc
= p
->pReal
->pMethods
->xLock(p
->pReal
, eLock
);
236 ** Unlock a telemetry_file.
238 int xUnlock(sqlite3_file
* pFile
, int eLock
) {
239 telemetry_file
* p
= (telemetry_file
*)pFile
;
241 rc
= p
->pReal
->pMethods
->xUnlock(p
->pReal
, eLock
);
246 ** Check if another file-handle holds a RESERVED lock on a telemetry_file.
248 int xCheckReservedLock(sqlite3_file
* pFile
, int* pResOut
) {
249 telemetry_file
* p
= (telemetry_file
*)pFile
;
250 int rc
= p
->pReal
->pMethods
->xCheckReservedLock(p
->pReal
, pResOut
);
255 ** File control method. For custom operations on a telemetry_file.
257 int xFileControl(sqlite3_file
* pFile
, int op
, void* pArg
) {
258 telemetry_file
* p
= (telemetry_file
*)pFile
;
259 int rc
= p
->pReal
->pMethods
->xFileControl(p
->pReal
, op
, pArg
);
264 ** Return the sector-size in bytes for a telemetry_file.
266 int xSectorSize(sqlite3_file
* pFile
) {
267 telemetry_file
* p
= (telemetry_file
*)pFile
;
269 rc
= p
->pReal
->pMethods
->xSectorSize(p
->pReal
);
274 ** Return the device characteristic flags supported by a telemetry_file.
276 int xDeviceCharacteristics(sqlite3_file
* pFile
) {
277 telemetry_file
* p
= (telemetry_file
*)pFile
;
279 rc
= p
->pReal
->pMethods
->xDeviceCharacteristics(p
->pReal
);
284 ** Shared-memory operations.
286 int xShmLock(sqlite3_file
* pFile
, int ofst
, int n
, int flags
) {
287 telemetry_file
* p
= (telemetry_file
*)pFile
;
288 return p
->pReal
->pMethods
->xShmLock(p
->pReal
, ofst
, n
, flags
);
291 int xShmMap(sqlite3_file
* pFile
, int iRegion
, int szRegion
, int isWrite
,
292 void volatile** pp
) {
293 telemetry_file
* p
= (telemetry_file
*)pFile
;
295 rc
= p
->pReal
->pMethods
->xShmMap(p
->pReal
, iRegion
, szRegion
, isWrite
, pp
);
299 void xShmBarrier(sqlite3_file
* pFile
) {
300 telemetry_file
* p
= (telemetry_file
*)pFile
;
301 p
->pReal
->pMethods
->xShmBarrier(p
->pReal
);
304 int xShmUnmap(sqlite3_file
* pFile
, int delFlag
) {
305 telemetry_file
* p
= (telemetry_file
*)pFile
;
307 rc
= p
->pReal
->pMethods
->xShmUnmap(p
->pReal
, delFlag
);
311 int xFetch(sqlite3_file
* pFile
, sqlite3_int64 iOff
, int iAmt
, void** pp
) {
312 telemetry_file
* p
= (telemetry_file
*)pFile
;
313 MOZ_ASSERT(p
->pReal
->pMethods
->iVersion
>= 3);
314 return p
->pReal
->pMethods
->xFetch(p
->pReal
, iOff
, iAmt
, pp
);
317 int xUnfetch(sqlite3_file
* pFile
, sqlite3_int64 iOff
, void* pResOut
) {
318 telemetry_file
* p
= (telemetry_file
*)pFile
;
319 MOZ_ASSERT(p
->pReal
->pMethods
->iVersion
>= 3);
320 return p
->pReal
->pMethods
->xUnfetch(p
->pReal
, iOff
, pResOut
);
323 int xOpen(sqlite3_vfs
* vfs
, const char* zName
, sqlite3_file
* pFile
, int flags
,
325 IOThreadAutoTimer
ioTimer(Telemetry::MOZ_SQLITE_OPEN_MS
,
326 IOInterposeObserver::OpCreateOrOpen
);
327 Telemetry::AutoTimer
<Telemetry::MOZ_SQLITE_OPEN_MS
> timer
;
328 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
330 telemetry_file
* p
= (telemetry_file
*)pFile
;
331 Histograms
* h
= nullptr;
332 // check if the filename is one we are probing for
333 for (size_t i
= 0; i
< sizeof(gHistograms
) / sizeof(gHistograms
[0]); i
++) {
335 // last probe is the fallback probe
337 if (!zName
) continue;
338 const char* match
= strstr(zName
, h
->name
);
339 if (!match
) continue;
340 char c
= match
[strlen(h
->name
)];
341 // include -wal/-journal too
342 if (!c
|| c
== '-') break;
346 rc
= orig_vfs
->xOpen(orig_vfs
, zName
, p
->pReal
, flags
, pOutFlags
);
347 if (rc
!= SQLITE_OK
) return rc
;
350 p
->location
= new char[7 + strlen(zName
) + 1];
351 strcpy(p
->location
, "file://");
352 strcpy(p
->location
+ 7, zName
);
354 p
->location
= new char[8];
355 strcpy(p
->location
, "file://");
358 if (p
->pReal
->pMethods
) {
359 sqlite3_io_methods
* pNew
= new sqlite3_io_methods
;
360 const sqlite3_io_methods
* pSub
= p
->pReal
->pMethods
;
361 memset(pNew
, 0, sizeof(*pNew
));
362 // If the io_methods version is higher than the last known one, you should
363 // update this VFS adding appropriate IO methods for any methods added in
364 // the version change.
365 pNew
->iVersion
= pSub
->iVersion
;
366 MOZ_ASSERT(pNew
->iVersion
<= LAST_KNOWN_IOMETHODS_VERSION
);
367 pNew
->xClose
= xClose
;
369 pNew
->xWrite
= xWrite
;
370 pNew
->xTruncate
= xTruncate
;
372 pNew
->xFileSize
= xFileSize
;
374 pNew
->xUnlock
= xUnlock
;
375 pNew
->xCheckReservedLock
= xCheckReservedLock
;
376 pNew
->xFileControl
= xFileControl
;
377 pNew
->xSectorSize
= xSectorSize
;
378 pNew
->xDeviceCharacteristics
= xDeviceCharacteristics
;
379 if (pNew
->iVersion
>= 2) {
380 // Methods added in version 2.
381 pNew
->xShmMap
= pSub
->xShmMap
? xShmMap
: 0;
382 pNew
->xShmLock
= pSub
->xShmLock
? xShmLock
: 0;
383 pNew
->xShmBarrier
= pSub
->xShmBarrier
? xShmBarrier
: 0;
384 pNew
->xShmUnmap
= pSub
->xShmUnmap
? xShmUnmap
: 0;
386 if (pNew
->iVersion
>= 3) {
387 // Methods added in version 3.
388 // SQLite 3.7.17 calls these methods without checking for nullptr first,
389 // so we always define them. Verify that we're not going to call
391 MOZ_ASSERT(pSub
->xFetch
);
392 pNew
->xFetch
= xFetch
;
393 MOZ_ASSERT(pSub
->xUnfetch
);
394 pNew
->xUnfetch
= xUnfetch
;
396 pFile
->pMethods
= pNew
;
401 int xDelete(sqlite3_vfs
* vfs
, const char* zName
, int syncDir
) {
402 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
403 return orig_vfs
->xDelete(orig_vfs
, zName
, syncDir
);
406 int xAccess(sqlite3_vfs
* vfs
, const char* zName
, int flags
, int* pResOut
) {
407 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
408 return orig_vfs
->xAccess(orig_vfs
, zName
, flags
, pResOut
);
411 int xFullPathname(sqlite3_vfs
* vfs
, const char* zName
, int nOut
, char* zOut
) {
412 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
413 return orig_vfs
->xFullPathname(orig_vfs
, zName
, nOut
, zOut
);
416 void* xDlOpen(sqlite3_vfs
* vfs
, const char* zFilename
) {
417 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
418 return orig_vfs
->xDlOpen(orig_vfs
, zFilename
);
421 void xDlError(sqlite3_vfs
* vfs
, int nByte
, char* zErrMsg
) {
422 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
423 orig_vfs
->xDlError(orig_vfs
, nByte
, zErrMsg
);
426 void (*xDlSym(sqlite3_vfs
* vfs
, void* pHdle
, const char* zSym
))(void) {
427 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
428 return orig_vfs
->xDlSym(orig_vfs
, pHdle
, zSym
);
431 void xDlClose(sqlite3_vfs
* vfs
, void* pHandle
) {
432 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
433 orig_vfs
->xDlClose(orig_vfs
, pHandle
);
436 int xRandomness(sqlite3_vfs
* vfs
, int nByte
, char* zOut
) {
437 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
438 return orig_vfs
->xRandomness(orig_vfs
, nByte
, zOut
);
441 int xSleep(sqlite3_vfs
* vfs
, int microseconds
) {
442 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
443 return orig_vfs
->xSleep(orig_vfs
, microseconds
);
446 int xCurrentTime(sqlite3_vfs
* vfs
, double* prNow
) {
447 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
448 return orig_vfs
->xCurrentTime(orig_vfs
, prNow
);
451 int xGetLastError(sqlite3_vfs
* vfs
, int nBuf
, char* zBuf
) {
452 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
453 return orig_vfs
->xGetLastError(orig_vfs
, nBuf
, zBuf
);
456 int xCurrentTimeInt64(sqlite3_vfs
* vfs
, sqlite3_int64
* piNow
) {
457 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
458 return orig_vfs
->xCurrentTimeInt64(orig_vfs
, piNow
);
461 static int xSetSystemCall(sqlite3_vfs
* vfs
, const char* zName
,
462 sqlite3_syscall_ptr pFunc
) {
463 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
464 return orig_vfs
->xSetSystemCall(orig_vfs
, zName
, pFunc
);
467 static sqlite3_syscall_ptr
xGetSystemCall(sqlite3_vfs
* vfs
, const char* zName
) {
468 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
469 return orig_vfs
->xGetSystemCall(orig_vfs
, zName
);
472 static const char* xNextSystemCall(sqlite3_vfs
* vfs
, const char* zName
) {
473 sqlite3_vfs
* orig_vfs
= static_cast<sqlite3_vfs
*>(vfs
->pAppData
);
474 return orig_vfs
->xNextSystemCall(orig_vfs
, zName
);
482 const char* GetTelemetryVFSName(bool exclusive
) {
483 return exclusive
? "telemetry-vfs-excl" : "telemetry-vfs";
486 UniquePtr
<sqlite3_vfs
> ConstructTelemetryVFS(bool exclusive
) {
488 # define EXPECTED_VFS "win32"
489 # define EXPECTED_VFS_EXCL "win32"
491 # define EXPECTED_VFS "unix"
492 # define EXPECTED_VFS_EXCL "unix-excl"
498 // Use the non-exclusive VFS.
499 vfs
= sqlite3_vfs_find(nullptr);
500 expected_vfs
= vfs
->zName
&& !strcmp(vfs
->zName
, EXPECTED_VFS
);
502 vfs
= sqlite3_vfs_find(EXPECTED_VFS_EXCL
);
503 expected_vfs
= (vfs
!= nullptr);
509 auto tvfs
= MakeUnique
<::sqlite3_vfs
>();
510 memset(tvfs
.get(), 0, sizeof(::sqlite3_vfs
));
511 // If the VFS version is higher than the last known one, you should update
512 // this VFS adding appropriate methods for any methods added in the version
514 tvfs
->iVersion
= vfs
->iVersion
;
515 MOZ_ASSERT(vfs
->iVersion
<= LAST_KNOWN_VFS_VERSION
);
517 sizeof(telemetry_file
) - sizeof(sqlite3_file
) + vfs
->szOsFile
;
518 tvfs
->mxPathname
= vfs
->mxPathname
;
519 tvfs
->zName
= GetTelemetryVFSName(exclusive
);
520 tvfs
->pAppData
= vfs
;
522 tvfs
->xDelete
= xDelete
;
523 tvfs
->xAccess
= xAccess
;
524 tvfs
->xFullPathname
= xFullPathname
;
525 tvfs
->xDlOpen
= xDlOpen
;
526 tvfs
->xDlError
= xDlError
;
527 tvfs
->xDlSym
= xDlSym
;
528 tvfs
->xDlClose
= xDlClose
;
529 tvfs
->xRandomness
= xRandomness
;
530 tvfs
->xSleep
= xSleep
;
531 tvfs
->xCurrentTime
= xCurrentTime
;
532 tvfs
->xGetLastError
= xGetLastError
;
533 if (tvfs
->iVersion
>= 2) {
534 // Methods added in version 2.
535 tvfs
->xCurrentTimeInt64
= xCurrentTimeInt64
;
537 if (tvfs
->iVersion
>= 3) {
538 // Methods added in version 3.
539 tvfs
->xSetSystemCall
= xSetSystemCall
;
540 tvfs
->xGetSystemCall
= xGetSystemCall
;
541 tvfs
->xNextSystemCall
= xNextSystemCall
;
546 } // namespace storage
547 } // namespace mozilla