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