xref: /sqlite-3.40.0/src/test_osinst.c (revision de60fc2d)
1 /*
2 ** 2008 April 10
3 **
4 ** The author disclaims copyright to this source code.  In place of
5 ** a legal notice, here is a blessing:
6 **
7 **    May you do good and not evil.
8 **    May you find forgiveness for yourself and forgive others.
9 **    May you share freely, never taking more than you give.
10 **
11 ******************************************************************************
12 **
13 ** This file contains the implementation of an SQLite vfs wrapper that
14 ** adds instrumentation to all vfs and file methods. C and Tcl interfaces
15 ** are provided to control the instrumentation.
16 */
17 
18 /*
19 ** This module contains code for a wrapper VFS that causes a log of
20 ** most VFS calls to be written into a nominated file on disk. The log
21 ** is stored in a compressed binary format to reduce the amount of IO
22 ** overhead introduced into the application by logging.
23 **
24 ** All calls on sqlite3_file objects except xFileControl() are logged.
25 ** Additionally, calls to the xAccess(), xOpen(), and xDelete()
26 ** methods are logged. The other sqlite3_vfs object methods (xDlXXX,
27 ** xRandomness, xSleep, xCurrentTime, xGetLastError and xCurrentTimeInt64)
28 ** are not logged.
29 **
30 ** The binary log files are read using a virtual table implementation
31 ** also contained in this file.
32 **
33 ** CREATING LOG FILES:
34 **
35 **       int sqlite3_vfslog_new(
36 **         const char *zVfs,          // Name of new VFS
37 **         const char *zParentVfs,    // Name of parent VFS (or NULL)
38 **         const char *zLog           // Name of log file to write to
39 **       );
40 **
41 **       int sqlite3_vfslog_finalize(const char *zVfs);
42 **
43 ** ANNOTATING LOG FILES:
44 **
45 **   To write an arbitrary message into a log file:
46 **
47 **       int sqlite3_vfslog_annotate(const char *zVfs, const char *zMsg);
48 **
49 ** READING LOG FILES:
50 **
51 **   Log files are read using the "vfslog" virtual table implementation
52 **   in this file. To register the virtual table with SQLite, use:
53 **
54 **       int sqlite3_vfslog_register(sqlite3 *db);
55 **
56 **   Then, if the log file is named "vfs.log", the following SQL command:
57 **
58 **       CREATE VIRTUAL TABLE v USING vfslog('vfs.log');
59 **
60 **   creates a virtual table with 6 columns, as follows:
61 **
62 **       CREATE TABLE v(
63 **         event    TEXT,             // "xOpen", "xRead" etc.
64 **         file     TEXT,             // Name of file this call applies to
65 **         clicks   INTEGER,          // Time spent in call
66 **         rc       INTEGER,          // Return value
67 **         size     INTEGER,          // Bytes read or written
68 **         offset   INTEGER           // File offset read or written
69 **       );
70 */
71 
72 #include "sqlite3.h"
73 #include <string.h>
74 #include <assert.h>
75 
76 
77 /*
78 ** Maximum pathname length supported by the vfslog backend.
79 */
80 #define INST_MAX_PATHNAME 512
81 
82 #define OS_ACCESS            1
83 #define OS_CHECKRESERVEDLOCK 2
84 #define OS_CLOSE             3
85 #define OS_CURRENTTIME       4
86 #define OS_DELETE            5
87 #define OS_DEVCHAR           6
88 #define OS_FILECONTROL       7
89 #define OS_FILESIZE          8
90 #define OS_FULLPATHNAME      9
91 #define OS_LOCK              11
92 #define OS_OPEN              12
93 #define OS_RANDOMNESS        13
94 #define OS_READ              14
95 #define OS_SECTORSIZE        15
96 #define OS_SLEEP             16
97 #define OS_SYNC              17
98 #define OS_TRUNCATE          18
99 #define OS_UNLOCK            19
100 #define OS_WRITE             20
101 #define OS_SHMUNMAP          22
102 #define OS_SHMMAP            23
103 #define OS_SHMLOCK           25
104 #define OS_SHMBARRIER        26
105 #define OS_ANNOTATE          28
106 
107 #define OS_NUMEVENTS         29
108 
109 #define VFSLOG_BUFFERSIZE 8192
110 
111 typedef struct VfslogVfs VfslogVfs;
112 typedef struct VfslogFile VfslogFile;
113 
114 struct VfslogVfs {
115   sqlite3_vfs base;               /* VFS methods */
116   sqlite3_vfs *pVfs;              /* Parent VFS */
117   int iNextFileId;                /* Next file id */
118   sqlite3_file *pLog;             /* Log file handle */
119   sqlite3_int64 iOffset;          /* Log file offset of start of write buffer */
120   int nBuf;                       /* Number of valid bytes in aBuf[] */
121   char aBuf[VFSLOG_BUFFERSIZE];   /* Write buffer */
122 };
123 
124 struct VfslogFile {
125   sqlite3_file base;              /* IO methods */
126   sqlite3_file *pReal;            /* Underlying file handle */
127   sqlite3_vfs *pVfslog;           /* Associated VsflogVfs object */
128   int iFileId;                    /* File id number */
129 };
130 
131 #define REALVFS(p) (((VfslogVfs *)(p))->pVfs)
132 
133 
134 
135 /*
136 ** Method declarations for vfslog_file.
137 */
138 static int vfslogClose(sqlite3_file*);
139 static int vfslogRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
140 static int vfslogWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
141 static int vfslogTruncate(sqlite3_file*, sqlite3_int64 size);
142 static int vfslogSync(sqlite3_file*, int flags);
143 static int vfslogFileSize(sqlite3_file*, sqlite3_int64 *pSize);
144 static int vfslogLock(sqlite3_file*, int);
145 static int vfslogUnlock(sqlite3_file*, int);
146 static int vfslogCheckReservedLock(sqlite3_file*, int *pResOut);
147 static int vfslogFileControl(sqlite3_file*, int op, void *pArg);
148 static int vfslogSectorSize(sqlite3_file*);
149 static int vfslogDeviceCharacteristics(sqlite3_file*);
150 
151 static int vfslogShmLock(sqlite3_file *pFile, int ofst, int n, int flags);
152 static int vfslogShmMap(sqlite3_file *pFile,int,int,int,volatile void **);
153 static void vfslogShmBarrier(sqlite3_file*);
154 static int vfslogShmUnmap(sqlite3_file *pFile, int deleteFlag);
155 
156 /*
157 ** Method declarations for vfslog_vfs.
158 */
159 static int vfslogOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
160 static int vfslogDelete(sqlite3_vfs*, const char *zName, int syncDir);
161 static int vfslogAccess(sqlite3_vfs*, const char *zName, int flags, int *);
162 static int vfslogFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
163 static void *vfslogDlOpen(sqlite3_vfs*, const char *zFilename);
164 static void vfslogDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
165 static void (*vfslogDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
166 static void vfslogDlClose(sqlite3_vfs*, void*);
167 static int vfslogRandomness(sqlite3_vfs*, int nByte, char *zOut);
168 static int vfslogSleep(sqlite3_vfs*, int microseconds);
169 static int vfslogCurrentTime(sqlite3_vfs*, double*);
170 
171 static int vfslogGetLastError(sqlite3_vfs*, int, char *);
172 static int vfslogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
173 
174 static sqlite3_vfs vfslog_vfs = {
175   1,                              /* iVersion */
176   sizeof(VfslogFile),             /* szOsFile */
177   INST_MAX_PATHNAME,              /* mxPathname */
178   0,                              /* pNext */
179   0,                              /* zName */
180   0,                              /* pAppData */
181   vfslogOpen,                     /* xOpen */
182   vfslogDelete,                   /* xDelete */
183   vfslogAccess,                   /* xAccess */
184   vfslogFullPathname,             /* xFullPathname */
185   vfslogDlOpen,                   /* xDlOpen */
186   vfslogDlError,                  /* xDlError */
187   vfslogDlSym,                    /* xDlSym */
188   vfslogDlClose,                  /* xDlClose */
189   vfslogRandomness,               /* xRandomness */
190   vfslogSleep,                    /* xSleep */
191   vfslogCurrentTime,              /* xCurrentTime */
192   vfslogGetLastError,             /* xGetLastError */
193   vfslogCurrentTimeInt64          /* xCurrentTime */
194 };
195 
196 static sqlite3_io_methods vfslog_io_methods = {
197   2,                              /* iVersion */
198   vfslogClose,                    /* xClose */
199   vfslogRead,                     /* xRead */
200   vfslogWrite,                    /* xWrite */
201   vfslogTruncate,                 /* xTruncate */
202   vfslogSync,                     /* xSync */
203   vfslogFileSize,                 /* xFileSize */
204   vfslogLock,                     /* xLock */
205   vfslogUnlock,                   /* xUnlock */
206   vfslogCheckReservedLock,        /* xCheckReservedLock */
207   vfslogFileControl,              /* xFileControl */
208   vfslogSectorSize,               /* xSectorSize */
209   vfslogDeviceCharacteristics,    /* xDeviceCharacteristics */
210   vfslogShmMap,                   /* xShmMap */
211   vfslogShmLock,                  /* xShmLock */
212   vfslogShmBarrier,               /* xShmBarrier */
213   vfslogShmUnmap                  /* xShmUnmap */
214 };
215 
216 #if SQLITE_OS_UNIX && !defined(NO_GETTOD)
217 #include <sys/time.h>
218 static sqlite3_uint64 vfslog_time(){
219   struct timeval sTime;
220   gettimeofday(&sTime, 0);
221   return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000;
222 }
223 #elif SQLITE_OS_WIN
224 #include <windows.h>
225 #include <time.h>
226 static sqlite3_uint64 vfslog_time(){
227   FILETIME ft;
228   sqlite3_uint64 u64time = 0;
229 
230   GetSystemTimeAsFileTime(&ft);
231 
232   u64time |= ft.dwHighDateTime;
233   u64time <<= 32;
234   u64time |= ft.dwLowDateTime;
235 
236   /* ft is 100-nanosecond intervals, we want microseconds */
237   return u64time /(sqlite3_uint64)10;
238 }
239 #else
240 static sqlite3_uint64 vfslog_time(){
241   return 0;
242 }
243 #endif
244 
245 static void vfslog_call(sqlite3_vfs *, int, int, int, int, int, int);
246 static void vfslog_string(sqlite3_vfs *, const char *);
247 
248 /*
249 ** Close an vfslog-file.
250 */
251 static int vfslogClose(sqlite3_file *pFile){
252   sqlite3_uint64 t;
253   int rc = SQLITE_OK;
254   VfslogFile *p = (VfslogFile *)pFile;
255 
256   t = vfslog_time();
257   if( p->pReal->pMethods ){
258     rc = p->pReal->pMethods->xClose(p->pReal);
259   }
260   t = vfslog_time() - t;
261   vfslog_call(p->pVfslog, OS_CLOSE, p->iFileId, t, rc, 0, 0);
262   return rc;
263 }
264 
265 /*
266 ** Read data from an vfslog-file.
267 */
268 static int vfslogRead(
269   sqlite3_file *pFile,
270   void *zBuf,
271   int iAmt,
272   sqlite_int64 iOfst
273 ){
274   int rc;
275   sqlite3_uint64 t;
276   VfslogFile *p = (VfslogFile *)pFile;
277   t = vfslog_time();
278   rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
279   t = vfslog_time() - t;
280   vfslog_call(p->pVfslog, OS_READ, p->iFileId, t, rc, iAmt, (int)iOfst);
281   return rc;
282 }
283 
284 /*
285 ** Write data to an vfslog-file.
286 */
287 static int vfslogWrite(
288   sqlite3_file *pFile,
289   const void *z,
290   int iAmt,
291   sqlite_int64 iOfst
292 ){
293   int rc;
294   sqlite3_uint64 t;
295   VfslogFile *p = (VfslogFile *)pFile;
296   t = vfslog_time();
297   rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst);
298   t = vfslog_time() - t;
299   vfslog_call(p->pVfslog, OS_WRITE, p->iFileId, t, rc, iAmt, (int)iOfst);
300   return rc;
301 }
302 
303 /*
304 ** Truncate an vfslog-file.
305 */
306 static int vfslogTruncate(sqlite3_file *pFile, sqlite_int64 size){
307   int rc;
308   sqlite3_uint64 t;
309   VfslogFile *p = (VfslogFile *)pFile;
310   t = vfslog_time();
311   rc = p->pReal->pMethods->xTruncate(p->pReal, size);
312   t = vfslog_time() - t;
313   vfslog_call(p->pVfslog, OS_TRUNCATE, p->iFileId, t, rc, 0, (int)size);
314   return rc;
315 }
316 
317 /*
318 ** Sync an vfslog-file.
319 */
320 static int vfslogSync(sqlite3_file *pFile, int flags){
321   int rc;
322   sqlite3_uint64 t;
323   VfslogFile *p = (VfslogFile *)pFile;
324   t = vfslog_time();
325   rc = p->pReal->pMethods->xSync(p->pReal, flags);
326   t = vfslog_time() - t;
327   vfslog_call(p->pVfslog, OS_SYNC, p->iFileId, t, rc, flags, 0);
328   return rc;
329 }
330 
331 /*
332 ** Return the current file-size of an vfslog-file.
333 */
334 static int vfslogFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
335   int rc;
336   sqlite3_uint64 t;
337   VfslogFile *p = (VfslogFile *)pFile;
338   t = vfslog_time();
339   rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
340   t = vfslog_time() - t;
341   vfslog_call(p->pVfslog, OS_FILESIZE, p->iFileId, t, rc, 0, (int)*pSize);
342   return rc;
343 }
344 
345 /*
346 ** Lock an vfslog-file.
347 */
348 static int vfslogLock(sqlite3_file *pFile, int eLock){
349   int rc;
350   sqlite3_uint64 t;
351   VfslogFile *p = (VfslogFile *)pFile;
352   t = vfslog_time();
353   rc = p->pReal->pMethods->xLock(p->pReal, eLock);
354   t = vfslog_time() - t;
355   vfslog_call(p->pVfslog, OS_LOCK, p->iFileId, t, rc, eLock, 0);
356   return rc;
357 }
358 
359 /*
360 ** Unlock an vfslog-file.
361 */
362 static int vfslogUnlock(sqlite3_file *pFile, int eLock){
363   int rc;
364   sqlite3_uint64 t;
365   VfslogFile *p = (VfslogFile *)pFile;
366   t = vfslog_time();
367   rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
368   t = vfslog_time() - t;
369   vfslog_call(p->pVfslog, OS_UNLOCK, p->iFileId, t, rc, eLock, 0);
370   return rc;
371 }
372 
373 /*
374 ** Check if another file-handle holds a RESERVED lock on an vfslog-file.
375 */
376 static int vfslogCheckReservedLock(sqlite3_file *pFile, int *pResOut){
377   int rc;
378   sqlite3_uint64 t;
379   VfslogFile *p = (VfslogFile *)pFile;
380   t = vfslog_time();
381   rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
382   t = vfslog_time() - t;
383   vfslog_call(p->pVfslog, OS_CHECKRESERVEDLOCK, p->iFileId, t, rc, *pResOut, 0);
384   return rc;
385 }
386 
387 /*
388 ** File control method. For custom operations on an vfslog-file.
389 */
390 static int vfslogFileControl(sqlite3_file *pFile, int op, void *pArg){
391   VfslogFile *p = (VfslogFile *)pFile;
392   int rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
393   if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
394     *(char**)pArg = sqlite3_mprintf("vfslog/%z", *(char**)pArg);
395   }
396   return rc;
397 }
398 
399 /*
400 ** Return the sector-size in bytes for an vfslog-file.
401 */
402 static int vfslogSectorSize(sqlite3_file *pFile){
403   int rc;
404   sqlite3_uint64 t;
405   VfslogFile *p = (VfslogFile *)pFile;
406   t = vfslog_time();
407   rc = p->pReal->pMethods->xSectorSize(p->pReal);
408   t = vfslog_time() - t;
409   vfslog_call(p->pVfslog, OS_SECTORSIZE, p->iFileId, t, rc, 0, 0);
410   return rc;
411 }
412 
413 /*
414 ** Return the device characteristic flags supported by an vfslog-file.
415 */
416 static int vfslogDeviceCharacteristics(sqlite3_file *pFile){
417   int rc;
418   sqlite3_uint64 t;
419   VfslogFile *p = (VfslogFile *)pFile;
420   t = vfslog_time();
421   rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
422   t = vfslog_time() - t;
423   vfslog_call(p->pVfslog, OS_DEVCHAR, p->iFileId, t, rc, 0, 0);
424   return rc;
425 }
426 
427 static int vfslogShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
428   int rc;
429   sqlite3_uint64 t;
430   VfslogFile *p = (VfslogFile *)pFile;
431   t = vfslog_time();
432   rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
433   t = vfslog_time() - t;
434   vfslog_call(p->pVfslog, OS_SHMLOCK, p->iFileId, t, rc, 0, 0);
435   return rc;
436 }
437 static int vfslogShmMap(
438   sqlite3_file *pFile,
439   int iRegion,
440   int szRegion,
441   int isWrite,
442   volatile void **pp
443 ){
444   int rc;
445   sqlite3_uint64 t;
446   VfslogFile *p = (VfslogFile *)pFile;
447   t = vfslog_time();
448   rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
449   t = vfslog_time() - t;
450   vfslog_call(p->pVfslog, OS_SHMMAP, p->iFileId, t, rc, 0, 0);
451   return rc;
452 }
453 static void vfslogShmBarrier(sqlite3_file *pFile){
454   sqlite3_uint64 t;
455   VfslogFile *p = (VfslogFile *)pFile;
456   t = vfslog_time();
457   p->pReal->pMethods->xShmBarrier(p->pReal);
458   t = vfslog_time() - t;
459   vfslog_call(p->pVfslog, OS_SHMBARRIER, p->iFileId, t, SQLITE_OK, 0, 0);
460 }
461 static int vfslogShmUnmap(sqlite3_file *pFile, int deleteFlag){
462   int rc;
463   sqlite3_uint64 t;
464   VfslogFile *p = (VfslogFile *)pFile;
465   t = vfslog_time();
466   rc = p->pReal->pMethods->xShmUnmap(p->pReal, deleteFlag);
467   t = vfslog_time() - t;
468   vfslog_call(p->pVfslog, OS_SHMUNMAP, p->iFileId, t, rc, 0, 0);
469   return rc;
470 }
471 
472 
473 /*
474 ** Open an vfslog file handle.
475 */
476 static int vfslogOpen(
477   sqlite3_vfs *pVfs,
478   const char *zName,
479   sqlite3_file *pFile,
480   int flags,
481   int *pOutFlags
482 ){
483   int rc;
484   sqlite3_uint64 t;
485   VfslogFile *p = (VfslogFile *)pFile;
486   VfslogVfs *pLog = (VfslogVfs *)pVfs;
487 
488   pFile->pMethods = &vfslog_io_methods;
489   p->pReal = (sqlite3_file *)&p[1];
490   p->pVfslog = pVfs;
491   p->iFileId = ++pLog->iNextFileId;
492 
493   t = vfslog_time();
494   rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags);
495   t = vfslog_time() - t;
496 
497   vfslog_call(pVfs, OS_OPEN, p->iFileId, t, rc, 0, 0);
498   vfslog_string(pVfs, zName);
499   return rc;
500 }
501 
502 /*
503 ** Delete the file located at zPath. If the dirSync argument is true,
504 ** ensure the file-system modifications are synced to disk before
505 ** returning.
506 */
507 static int vfslogDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
508   int rc;
509   sqlite3_uint64 t;
510   t = vfslog_time();
511   rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync);
512   t = vfslog_time() - t;
513   vfslog_call(pVfs, OS_DELETE, 0, t, rc, dirSync, 0);
514   vfslog_string(pVfs, zPath);
515   return rc;
516 }
517 
518 /*
519 ** Test for access permissions. Return true if the requested permission
520 ** is available, or false otherwise.
521 */
522 static int vfslogAccess(
523   sqlite3_vfs *pVfs,
524   const char *zPath,
525   int flags,
526   int *pResOut
527 ){
528   int rc;
529   sqlite3_uint64 t;
530   t = vfslog_time();
531   rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut);
532   t = vfslog_time() - t;
533   vfslog_call(pVfs, OS_ACCESS, 0, t, rc, flags, *pResOut);
534   vfslog_string(pVfs, zPath);
535   return rc;
536 }
537 
538 /*
539 ** Populate buffer zOut with the full canonical pathname corresponding
540 ** to the pathname in zPath. zOut is guaranteed to point to a buffer
541 ** of at least (INST_MAX_PATHNAME+1) bytes.
542 */
543 static int vfslogFullPathname(
544   sqlite3_vfs *pVfs,
545   const char *zPath,
546   int nOut,
547   char *zOut
548 ){
549   return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
550 }
551 
552 /*
553 ** Open the dynamic library located at zPath and return a handle.
554 */
555 static void *vfslogDlOpen(sqlite3_vfs *pVfs, const char *zPath){
556   return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath);
557 }
558 
559 /*
560 ** Populate the buffer zErrMsg (size nByte bytes) with a human readable
561 ** utf-8 string describing the most recent error encountered associated
562 ** with dynamic libraries.
563 */
564 static void vfslogDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
565   REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg);
566 }
567 
568 /*
569 ** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
570 */
571 static void (*vfslogDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
572   return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym);
573 }
574 
575 /*
576 ** Close the dynamic library handle pHandle.
577 */
578 static void vfslogDlClose(sqlite3_vfs *pVfs, void *pHandle){
579   REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle);
580 }
581 
582 /*
583 ** Populate the buffer pointed to by zBufOut with nByte bytes of
584 ** random data.
585 */
586 static int vfslogRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
587   return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
588 }
589 
590 /*
591 ** Sleep for nMicro microseconds. Return the number of microseconds
592 ** actually slept.
593 */
594 static int vfslogSleep(sqlite3_vfs *pVfs, int nMicro){
595   return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro);
596 }
597 
598 /*
599 ** Return the current time as a Julian Day number in *pTimeOut.
600 */
601 static int vfslogCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
602   return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut);
603 }
604 
605 static int vfslogGetLastError(sqlite3_vfs *pVfs, int a, char *b){
606   return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b);
607 }
608 static int vfslogCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
609   return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p);
610 }
611 
612 static void vfslog_flush(VfslogVfs *p){
613 #ifdef SQLITE_TEST
614   extern int sqlite3_io_error_pending;
615   extern int sqlite3_io_error_persist;
616   extern int sqlite3_diskfull_pending;
617 
618   int pending = sqlite3_io_error_pending;
619   int persist = sqlite3_io_error_persist;
620   int diskfull = sqlite3_diskfull_pending;
621 
622   sqlite3_io_error_pending = 0;
623   sqlite3_io_error_persist = 0;
624   sqlite3_diskfull_pending = 0;
625 #endif
626 
627   if( p->nBuf ){
628     p->pLog->pMethods->xWrite(p->pLog, p->aBuf, p->nBuf, p->iOffset);
629     p->iOffset += p->nBuf;
630     p->nBuf = 0;
631   }
632 
633 #ifdef SQLITE_TEST
634   sqlite3_io_error_pending = pending;
635   sqlite3_io_error_persist = persist;
636   sqlite3_diskfull_pending = diskfull;
637 #endif
638 }
639 
640 static void put32bits(unsigned char *p, unsigned int v){
641   p[0] = v>>24;
642   p[1] = v>>16;
643   p[2] = v>>8;
644   p[3] = v;
645 }
646 
647 static void vfslog_call(
648   sqlite3_vfs *pVfs,
649   int eEvent,
650   int iFileid,
651   int nClick,
652   int return_code,
653   int size,
654   int offset
655 ){
656   VfslogVfs *p = (VfslogVfs *)pVfs;
657   unsigned char *zRec;
658   if( (24+p->nBuf)>sizeof(p->aBuf) ){
659     vfslog_flush(p);
660   }
661   zRec = (unsigned char *)&p->aBuf[p->nBuf];
662   put32bits(&zRec[0], eEvent);
663   put32bits(&zRec[4], iFileid);
664   put32bits(&zRec[8], nClick);
665   put32bits(&zRec[12], return_code);
666   put32bits(&zRec[16], size);
667   put32bits(&zRec[20], offset);
668   p->nBuf += 24;
669 }
670 
671 static void vfslog_string(sqlite3_vfs *pVfs, const char *zStr){
672   VfslogVfs *p = (VfslogVfs *)pVfs;
673   unsigned char *zRec;
674   int nStr = zStr ? strlen(zStr) : 0;
675   if( (4+nStr+p->nBuf)>sizeof(p->aBuf) ){
676     vfslog_flush(p);
677   }
678   zRec = (unsigned char *)&p->aBuf[p->nBuf];
679   put32bits(&zRec[0], nStr);
680   if( zStr ){
681     memcpy(&zRec[4], zStr, nStr);
682   }
683   p->nBuf += (4 + nStr);
684 }
685 
686 static void vfslog_finalize(VfslogVfs *p){
687   if( p->pLog->pMethods ){
688     vfslog_flush(p);
689     p->pLog->pMethods->xClose(p->pLog);
690   }
691   sqlite3_free(p);
692 }
693 
694 int sqlite3_vfslog_finalize(const char *zVfs){
695   sqlite3_vfs *pVfs;
696   pVfs = sqlite3_vfs_find(zVfs);
697   if( !pVfs || pVfs->xOpen!=vfslogOpen ){
698     return SQLITE_ERROR;
699   }
700   sqlite3_vfs_unregister(pVfs);
701   vfslog_finalize((VfslogVfs *)pVfs);
702   return SQLITE_OK;
703 }
704 
705 int sqlite3_vfslog_new(
706   const char *zVfs,               /* New VFS name */
707   const char *zParentVfs,         /* Parent VFS name (or NULL) */
708   const char *zLog                /* Log file name */
709 ){
710   VfslogVfs *p;
711   sqlite3_vfs *pParent;
712   int nByte;
713   int flags;
714   int rc;
715   char *zFile;
716   int nVfs;
717 
718   pParent = sqlite3_vfs_find(zParentVfs);
719   if( !pParent ){
720     return SQLITE_ERROR;
721   }
722 
723   nVfs = strlen(zVfs);
724   nByte = sizeof(VfslogVfs) + pParent->szOsFile + nVfs+1+pParent->mxPathname+1;
725   p = (VfslogVfs *)sqlite3_malloc(nByte);
726   memset(p, 0, nByte);
727 
728   p->pVfs = pParent;
729   p->pLog = (sqlite3_file *)&p[1];
730   memcpy(&p->base, &vfslog_vfs, sizeof(sqlite3_vfs));
731   p->base.zName = &((char *)p->pLog)[pParent->szOsFile];
732   p->base.szOsFile += pParent->szOsFile;
733   memcpy((char *)p->base.zName, zVfs, nVfs);
734 
735   zFile = (char *)&p->base.zName[nVfs+1];
736   pParent->xFullPathname(pParent, zLog, pParent->mxPathname, zFile);
737 
738   flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MASTER_JOURNAL;
739   pParent->xDelete(pParent, zFile, 0);
740   rc = pParent->xOpen(pParent, zFile, p->pLog, flags, &flags);
741   if( rc==SQLITE_OK ){
742     memcpy(p->aBuf, "sqlite_ostrace1.....", 20);
743     p->iOffset = 0;
744     p->nBuf = 20;
745     rc = sqlite3_vfs_register((sqlite3_vfs *)p, 1);
746   }
747   if( rc ){
748     vfslog_finalize(p);
749   }
750   return rc;
751 }
752 
753 int sqlite3_vfslog_annotate(const char *zVfs, const char *zMsg){
754   sqlite3_vfs *pVfs;
755   pVfs = sqlite3_vfs_find(zVfs);
756   if( !pVfs || pVfs->xOpen!=vfslogOpen ){
757     return SQLITE_ERROR;
758   }
759   vfslog_call(pVfs, OS_ANNOTATE, 0, 0, 0, 0, 0);
760   vfslog_string(pVfs, zMsg);
761   return SQLITE_OK;
762 }
763 
764 static const char *vfslog_eventname(int eEvent){
765   const char *zEvent = 0;
766 
767   switch( eEvent ){
768     case OS_CLOSE:             zEvent = "xClose"; break;
769     case OS_READ:              zEvent = "xRead"; break;
770     case OS_WRITE:             zEvent = "xWrite"; break;
771     case OS_TRUNCATE:          zEvent = "xTruncate"; break;
772     case OS_SYNC:              zEvent = "xSync"; break;
773     case OS_FILESIZE:          zEvent = "xFilesize"; break;
774     case OS_LOCK:              zEvent = "xLock"; break;
775     case OS_UNLOCK:            zEvent = "xUnlock"; break;
776     case OS_CHECKRESERVEDLOCK: zEvent = "xCheckResLock"; break;
777     case OS_FILECONTROL:       zEvent = "xFileControl"; break;
778     case OS_SECTORSIZE:        zEvent = "xSectorSize"; break;
779     case OS_DEVCHAR:           zEvent = "xDeviceChar"; break;
780     case OS_OPEN:              zEvent = "xOpen"; break;
781     case OS_DELETE:            zEvent = "xDelete"; break;
782     case OS_ACCESS:            zEvent = "xAccess"; break;
783     case OS_FULLPATHNAME:      zEvent = "xFullPathname"; break;
784     case OS_RANDOMNESS:        zEvent = "xRandomness"; break;
785     case OS_SLEEP:             zEvent = "xSleep"; break;
786     case OS_CURRENTTIME:       zEvent = "xCurrentTime"; break;
787 
788     case OS_SHMUNMAP:          zEvent = "xShmUnmap"; break;
789     case OS_SHMLOCK:           zEvent = "xShmLock"; break;
790     case OS_SHMBARRIER:        zEvent = "xShmBarrier"; break;
791     case OS_SHMMAP:            zEvent = "xShmMap"; break;
792 
793     case OS_ANNOTATE:          zEvent = "annotation"; break;
794   }
795 
796   return zEvent;
797 }
798 
799 typedef struct VfslogVtab VfslogVtab;
800 typedef struct VfslogCsr VfslogCsr;
801 
802 /*
803 ** Virtual table type for the vfslog reader module.
804 */
805 struct VfslogVtab {
806   sqlite3_vtab base;              /* Base class */
807   sqlite3_file *pFd;              /* File descriptor open on vfslog file */
808   sqlite3_int64 nByte;            /* Size of file in bytes */
809   char *zFile;                    /* File name for pFd */
810 };
811 
812 /*
813 ** Virtual table cursor type for the vfslog reader module.
814 */
815 struct VfslogCsr {
816   sqlite3_vtab_cursor base;       /* Base class */
817   sqlite3_int64 iRowid;           /* Current rowid. */
818   sqlite3_int64 iOffset;          /* Offset of next record in file */
819   char *zTransient;               /* Transient 'file' string */
820   int nFile;                      /* Size of array azFile[] */
821   char **azFile;                  /* File strings */
822   unsigned char aBuf[1024];       /* Current vfs log entry (read from file) */
823 };
824 
825 static unsigned int get32bits(unsigned char *p){
826   return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3];
827 }
828 
829 /*
830 ** The argument must point to a buffer containing a nul-terminated string.
831 ** If the string begins with an SQL quote character it is overwritten by
832 ** the dequoted version. Otherwise the buffer is left unmodified.
833 */
834 static void dequote(char *z){
835   char quote;                     /* Quote character (if any ) */
836   quote = z[0];
837   if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){
838     int iIn = 1;                  /* Index of next byte to read from input */
839     int iOut = 0;                 /* Index of next byte to write to output */
840     if( quote=='[' ) quote = ']';
841     while( z[iIn] ){
842       if( z[iIn]==quote ){
843         if( z[iIn+1]!=quote ) break;
844         z[iOut++] = quote;
845         iIn += 2;
846       }else{
847         z[iOut++] = z[iIn++];
848       }
849     }
850     z[iOut] = '\0';
851   }
852 }
853 
854 #ifndef SQLITE_OMIT_VIRTUALTABLE
855 /*
856 ** Connect to or create a vfslog virtual table.
857 */
858 static int vlogConnect(
859   sqlite3 *db,
860   void *pAux,
861   int argc, const char *const*argv,
862   sqlite3_vtab **ppVtab,
863   char **pzErr
864 ){
865   sqlite3_vfs *pVfs;              /* VFS used to read log file */
866   int flags;                      /* flags passed to pVfs->xOpen() */
867   VfslogVtab *p;
868   int rc;
869   int nByte;
870   char *zFile;
871 
872   *ppVtab = 0;
873   pVfs = sqlite3_vfs_find(0);
874   nByte = sizeof(VfslogVtab) + pVfs->szOsFile + pVfs->mxPathname;
875   p = sqlite3_malloc(nByte);
876   if( p==0 ) return SQLITE_NOMEM;
877   memset(p, 0, nByte);
878 
879   p->pFd = (sqlite3_file *)&p[1];
880   p->zFile = &((char *)p->pFd)[pVfs->szOsFile];
881 
882   zFile = sqlite3_mprintf("%s", argv[3]);
883   if( !zFile ){
884     sqlite3_free(p);
885     return SQLITE_NOMEM;
886   }
887   dequote(zFile);
888   pVfs->xFullPathname(pVfs, zFile, pVfs->mxPathname, p->zFile);
889   sqlite3_free(zFile);
890 
891   flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MASTER_JOURNAL;
892   rc = pVfs->xOpen(pVfs, p->zFile, p->pFd, flags, &flags);
893 
894   if( rc==SQLITE_OK ){
895     p->pFd->pMethods->xFileSize(p->pFd, &p->nByte);
896     sqlite3_declare_vtab(db,
897         "CREATE TABLE xxx(event, file, click, rc, size, offset)"
898     );
899     *ppVtab = &p->base;
900   }else{
901     sqlite3_free(p);
902   }
903 
904   return rc;
905 }
906 
907 /*
908 ** There is no "best-index". This virtual table always does a linear
909 ** scan of the binary VFS log file.
910 */
911 static int vlogBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
912   pIdxInfo->estimatedCost = 10.0;
913   return SQLITE_OK;
914 }
915 
916 /*
917 ** Disconnect from or destroy a vfslog virtual table.
918 */
919 static int vlogDisconnect(sqlite3_vtab *pVtab){
920   VfslogVtab *p = (VfslogVtab *)pVtab;
921   if( p->pFd->pMethods ){
922     p->pFd->pMethods->xClose(p->pFd);
923     p->pFd->pMethods = 0;
924   }
925   sqlite3_free(p);
926   return SQLITE_OK;
927 }
928 
929 /*
930 ** Open a new vfslog cursor.
931 */
932 static int vlogOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
933   VfslogCsr *pCsr;                /* Newly allocated cursor object */
934 
935   pCsr = sqlite3_malloc(sizeof(VfslogCsr));
936   if( !pCsr ) return SQLITE_NOMEM;
937   memset(pCsr, 0, sizeof(VfslogCsr));
938   *ppCursor = &pCsr->base;
939   return SQLITE_OK;
940 }
941 
942 /*
943 ** Close a vfslog cursor.
944 */
945 static int vlogClose(sqlite3_vtab_cursor *pCursor){
946   VfslogCsr *p = (VfslogCsr *)pCursor;
947   int i;
948   for(i=0; i<p->nFile; i++){
949     sqlite3_free(p->azFile[i]);
950   }
951   sqlite3_free(p->azFile);
952   sqlite3_free(p->zTransient);
953   sqlite3_free(p);
954   return SQLITE_OK;
955 }
956 
957 /*
958 ** Move a vfslog cursor to the next entry in the file.
959 */
960 static int vlogNext(sqlite3_vtab_cursor *pCursor){
961   VfslogCsr *pCsr = (VfslogCsr *)pCursor;
962   VfslogVtab *p = (VfslogVtab *)pCursor->pVtab;
963   int rc = SQLITE_OK;
964   int nRead;
965 
966   sqlite3_free(pCsr->zTransient);
967   pCsr->zTransient = 0;
968 
969   nRead = 24;
970   if( pCsr->iOffset+nRead<=p->nByte ){
971     int eEvent;
972     rc = p->pFd->pMethods->xRead(p->pFd, pCsr->aBuf, nRead, pCsr->iOffset);
973 
974     eEvent = get32bits(pCsr->aBuf);
975     if( (rc==SQLITE_OK)
976      && (eEvent==OS_OPEN || eEvent==OS_DELETE || eEvent==OS_ACCESS)
977     ){
978       char buf[4];
979       rc = p->pFd->pMethods->xRead(p->pFd, buf, 4, pCsr->iOffset+nRead);
980       nRead += 4;
981       if( rc==SQLITE_OK ){
982         int nStr = get32bits((unsigned char *)buf);
983         char *zStr = sqlite3_malloc(nStr+1);
984         rc = p->pFd->pMethods->xRead(p->pFd, zStr, nStr, pCsr->iOffset+nRead);
985         zStr[nStr] = '\0';
986         nRead += nStr;
987 
988         if( eEvent==OS_OPEN ){
989           int iFileid = get32bits(&pCsr->aBuf[4]);
990           if( iFileid>=pCsr->nFile ){
991             int nNew = sizeof(pCsr->azFile[0])*(iFileid+1);
992             pCsr->azFile = (char **)sqlite3_realloc(pCsr->azFile, nNew);
993             nNew -= sizeof(pCsr->azFile[0])*pCsr->nFile;
994             memset(&pCsr->azFile[pCsr->nFile], 0, nNew);
995             pCsr->nFile = iFileid+1;
996           }
997           sqlite3_free(pCsr->azFile[iFileid]);
998           pCsr->azFile[iFileid] = zStr;
999         }else{
1000           pCsr->zTransient = zStr;
1001         }
1002       }
1003     }
1004   }
1005 
1006   pCsr->iRowid += 1;
1007   pCsr->iOffset += nRead;
1008   return rc;
1009 }
1010 
1011 static int vlogEof(sqlite3_vtab_cursor *pCursor){
1012   VfslogCsr *pCsr = (VfslogCsr *)pCursor;
1013   VfslogVtab *p = (VfslogVtab *)pCursor->pVtab;
1014   return (pCsr->iOffset>=p->nByte);
1015 }
1016 
1017 static int vlogFilter(
1018   sqlite3_vtab_cursor *pCursor,
1019   int idxNum, const char *idxStr,
1020   int argc, sqlite3_value **argv
1021 ){
1022   VfslogCsr *pCsr = (VfslogCsr *)pCursor;
1023   pCsr->iRowid = 0;
1024   pCsr->iOffset = 20;
1025   return vlogNext(pCursor);
1026 }
1027 
1028 static int vlogColumn(
1029   sqlite3_vtab_cursor *pCursor,
1030   sqlite3_context *ctx,
1031   int i
1032 ){
1033   unsigned int val;
1034   VfslogCsr *pCsr = (VfslogCsr *)pCursor;
1035 
1036   assert( i<7 );
1037   val = get32bits(&pCsr->aBuf[4*i]);
1038 
1039   switch( i ){
1040     case 0: {
1041       sqlite3_result_text(ctx, vfslog_eventname(val), -1, SQLITE_STATIC);
1042       break;
1043     }
1044     case 1: {
1045       char *zStr = pCsr->zTransient;
1046       if( val!=0 && val<pCsr->nFile ){
1047         zStr = pCsr->azFile[val];
1048       }
1049       sqlite3_result_text(ctx, zStr, -1, SQLITE_TRANSIENT);
1050       break;
1051     }
1052     default:
1053       sqlite3_result_int(ctx, val);
1054       break;
1055   }
1056 
1057   return SQLITE_OK;
1058 }
1059 
1060 static int vlogRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
1061   VfslogCsr *pCsr = (VfslogCsr *)pCursor;
1062   *pRowid = pCsr->iRowid;
1063   return SQLITE_OK;
1064 }
1065 
1066 int sqlite3_vfslog_register(sqlite3 *db){
1067   static sqlite3_module vfslog_module = {
1068     0,                            /* iVersion */
1069     vlogConnect,                /* xCreate */
1070     vlogConnect,                /* xConnect */
1071     vlogBestIndex,              /* xBestIndex */
1072     vlogDisconnect,             /* xDisconnect */
1073     vlogDisconnect,             /* xDestroy */
1074     vlogOpen,                   /* xOpen - open a cursor */
1075     vlogClose,                  /* xClose - close a cursor */
1076     vlogFilter,                 /* xFilter - configure scan constraints */
1077     vlogNext,                   /* xNext - advance a cursor */
1078     vlogEof,                    /* xEof - check for end of scan */
1079     vlogColumn,                 /* xColumn - read data */
1080     vlogRowid,                  /* xRowid - read data */
1081     0,                            /* xUpdate */
1082     0,                            /* xBegin */
1083     0,                            /* xSync */
1084     0,                            /* xCommit */
1085     0,                            /* xRollback */
1086     0,                            /* xFindMethod */
1087     0,                            /* xRename */
1088   };
1089 
1090   sqlite3_create_module(db, "vfslog", &vfslog_module, 0);
1091   return SQLITE_OK;
1092 }
1093 #endif /* SQLITE_OMIT_VIRTUALTABLE */
1094 
1095 /**************************************************************************
1096 ***************************************************************************
1097 ** Tcl interface starts here.
1098 */
1099 
1100 #if defined(SQLITE_TEST) || defined(TCLSH)
1101 
1102 #include <tcl.h>
1103 
1104 static int test_vfslog(
1105   void *clientData,
1106   Tcl_Interp *interp,
1107   int objc,
1108   Tcl_Obj *CONST objv[]
1109 ){
1110   struct SqliteDb { sqlite3 *db; };
1111   sqlite3 *db;
1112   Tcl_CmdInfo cmdInfo;
1113   int rc = SQLITE_ERROR;
1114 
1115   static const char *strs[] = { "annotate", "finalize", "new",  "register", 0 };
1116   enum VL_enum { VL_ANNOTATE, VL_FINALIZE, VL_NEW, VL_REGISTER };
1117   int iSub;
1118 
1119   if( objc<2 ){
1120     Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
1121     return TCL_ERROR;
1122   }
1123   if( Tcl_GetIndexFromObj(interp, objv[1], strs, "sub-command", 0, &iSub) ){
1124     return TCL_ERROR;
1125   }
1126 
1127   switch( (enum VL_enum)iSub ){
1128     case VL_ANNOTATE: {
1129       int rc;
1130       char *zVfs;
1131       char *zMsg;
1132       if( objc!=4 ){
1133         Tcl_WrongNumArgs(interp, 3, objv, "VFS");
1134         return TCL_ERROR;
1135       }
1136       zVfs = Tcl_GetString(objv[2]);
1137       zMsg = Tcl_GetString(objv[3]);
1138       rc = sqlite3_vfslog_annotate(zVfs, zMsg);
1139       if( rc!=SQLITE_OK ){
1140         Tcl_AppendResult(interp, "failed", 0);
1141         return TCL_ERROR;
1142       }
1143       break;
1144     }
1145     case VL_FINALIZE: {
1146       int rc;
1147       char *zVfs;
1148       if( objc!=3 ){
1149         Tcl_WrongNumArgs(interp, 2, objv, "VFS");
1150         return TCL_ERROR;
1151       }
1152       zVfs = Tcl_GetString(objv[2]);
1153       rc = sqlite3_vfslog_finalize(zVfs);
1154       if( rc!=SQLITE_OK ){
1155         Tcl_AppendResult(interp, "failed", 0);
1156         return TCL_ERROR;
1157       }
1158       break;
1159     };
1160 
1161     case VL_NEW: {
1162       int rc;
1163       char *zVfs;
1164       char *zParent;
1165       char *zLog;
1166       if( objc!=5 ){
1167         Tcl_WrongNumArgs(interp, 2, objv, "VFS PARENT LOGFILE");
1168         return TCL_ERROR;
1169       }
1170       zVfs = Tcl_GetString(objv[2]);
1171       zParent = Tcl_GetString(objv[3]);
1172       zLog = Tcl_GetString(objv[4]);
1173       if( *zParent=='\0' ) zParent = 0;
1174       rc = sqlite3_vfslog_new(zVfs, zParent, zLog);
1175       if( rc!=SQLITE_OK ){
1176         Tcl_AppendResult(interp, "failed", 0);
1177         return TCL_ERROR;
1178       }
1179       break;
1180     };
1181 
1182     case VL_REGISTER: {
1183       char *zDb;
1184       if( objc!=3 ){
1185         Tcl_WrongNumArgs(interp, 2, objv, "DB");
1186         return TCL_ERROR;
1187       }
1188 #ifdef SQLITE_OMIT_VIRTUALTABLE
1189       Tcl_AppendResult(interp, "vfslog not available because of "
1190                                "SQLITE_OMIT_VIRTUALTABLE", (void*)0);
1191       return TCL_ERROR;
1192 #else
1193       zDb = Tcl_GetString(objv[2]);
1194       if( Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){
1195         db = ((struct SqliteDb*)cmdInfo.objClientData)->db;
1196         rc = sqlite3_vfslog_register(db);
1197       }
1198       if( rc!=SQLITE_OK ){
1199         Tcl_AppendResult(interp, "bad sqlite3 handle: ", zDb, (void*)0);
1200         return TCL_ERROR;
1201       }
1202       break;
1203 #endif
1204     }
1205   }
1206 
1207   return TCL_OK;
1208 }
1209 
1210 int SqlitetestOsinst_Init(Tcl_Interp *interp){
1211   Tcl_CreateObjCommand(interp, "vfslog", test_vfslog, 0, 0);
1212   return TCL_OK;
1213 }
1214 
1215 #endif /* SQLITE_TEST */
1216