xref: /sqlite-3.40.0/src/test_osinst.c (revision af75c869)
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 #ifdef SQLITE_ENABLE_INSTVFS
19 /*
20 ** C interface:
21 **
22 **   sqlite3_instvfs_create()
23 **   sqlite3_instvfs_destroy()
24 **   sqlite3_instvfs_configure()
25 **
26 **   sqlite3_instvfs_reset()
27 **   sqlite3_instvfs_get()
28 **
29 **   sqlite3_instvfs_binarylog
30 **   sqlite3_instvfs_binarylog_marker
31 **
32 ** Tcl interface (omitted if SQLITE_TEST is not set):
33 **
34 **   sqlite3_instvfs create NAME ?PARENT?
35 **
36 **       Create and register new vfs called $NAME, which is a wrapper around
37 **       the existing vfs $PARENT. If the PARENT argument is omitted, the
38 **       new vfs is a wrapper around the current default vfs.
39 **
40 **   sqlite3_instvfs destroy NAME
41 **
42 **       Deregister and destroy the vfs named $NAME, which must have been
43 **       created by an earlier invocation of [sqlite3_instvfs create].
44 **
45 **   sqlite3_instvfs configure NAME SCRIPT
46 **
47 **       Configure the callback script for the vfs $NAME, which much have
48 **       been created by an earlier invocation of [sqlite3_instvfs create].
49 **       After a callback script has been configured, it is invoked each
50 **       time a vfs or file method is called by SQLite. Before invoking
51 **       the callback script, five arguments are appended to it:
52 **
53 **         * The name of the invoked method - i.e. "xRead".
54 **
55 **         * The time consumed by the method call as measured by
56 **           sqlite3Hwtime() (an integer value)
57 **
58 **         * A string value with a different meaning for different calls.
59 **           For file methods, the name of the file being operated on. For
60 **           other methods it is the filename argument, if any.
61 **
62 **         * A 32-bit integer value with a call-specific meaning.
63 **
64 **         * A 64-bit integer value. For xRead() and xWrite() calls this
65 **           is the file offset being written to or read from. Unused by
66 **           all other calls.
67 **
68 **   sqlite3_instvfs reset NAME
69 **
70 **       Zero the internal event counters associated with vfs $NAME,
71 **       which must have been created by an earlier invocation of
72 **       [sqlite3_instvfs create].
73 **
74 **   sqlite3_instvfs report NAME
75 **
76 **       Return the values of the internal event counters associated
77 **       with vfs $NAME. The report format is a list with one element
78 **       for each method call (xWrite, xRead etc.). Each element is
79 **       itself a list with three elements:
80 **
81 **         * The name of the method call - i.e. "xWrite",
82 **         * The total number of calls to the method (an integer).
83 **         * The aggregate time consumed by all calls to the method as
84 **           measured by sqlite3Hwtime() (an integer).
85 */
86 
87 #include "sqlite3.h"
88 #include <string.h>
89 #include <assert.h>
90 
91 /*
92 ** Maximum pathname length supported by the inst backend.
93 */
94 #define INST_MAX_PATHNAME 512
95 
96 
97 /* File methods */
98 /* Vfs methods */
99 #define OS_ACCESS            1
100 #define OS_CHECKRESERVEDLOCK 2
101 #define OS_CLOSE             3
102 #define OS_CURRENTTIME       4
103 #define OS_DELETE            5
104 #define OS_DEVCHAR           6
105 #define OS_FILECONTROL       7
106 #define OS_FILESIZE          8
107 #define OS_FULLPATHNAME      9
108 #define OS_LOCK              11
109 #define OS_OPEN              12
110 #define OS_RANDOMNESS        13
111 #define OS_READ              14
112 #define OS_SECTORSIZE        15
113 #define OS_SLEEP             16
114 #define OS_SYNC              17
115 #define OS_TRUNCATE          18
116 #define OS_UNLOCK            19
117 #define OS_WRITE             20
118 
119 #define OS_NUMEVENTS         21
120 
121 #define BINARYLOG_STRING     30
122 #define BINARYLOG_MARKER     31
123 
124 #define BINARYLOG_PREPARE_V2 64
125 #define BINARYLOG_STEP       65
126 #define BINARYLOG_FINALIZE   66
127 
128 struct InstVfs {
129   sqlite3_vfs base;
130   sqlite3_vfs *pVfs;
131 
132   void *pClient;
133   void (*xDel)(void *);
134   void (*xCall)(void *, int, int, sqlite3_int64, int, const char *, int, int, sqlite3_int64);
135 
136   /* Counters */
137   sqlite3_int64 aTime[OS_NUMEVENTS];
138   int aCount[OS_NUMEVENTS];
139 
140   int iNextFileId;
141 };
142 typedef struct InstVfs InstVfs;
143 
144 #define REALVFS(p) (((InstVfs *)(p))->pVfs)
145 
146 typedef struct inst_file inst_file;
147 struct inst_file {
148   sqlite3_file base;
149   sqlite3_file *pReal;
150   InstVfs *pInstVfs;
151   const char *zName;
152   int iFileId;               /* File id number */
153   int flags;
154 };
155 
156 /*
157 ** Method declarations for inst_file.
158 */
159 static int instClose(sqlite3_file*);
160 static int instRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
161 static int instWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
162 static int instTruncate(sqlite3_file*, sqlite3_int64 size);
163 static int instSync(sqlite3_file*, int flags);
164 static int instFileSize(sqlite3_file*, sqlite3_int64 *pSize);
165 static int instLock(sqlite3_file*, int);
166 static int instUnlock(sqlite3_file*, int);
167 static int instCheckReservedLock(sqlite3_file*, int *pResOut);
168 static int instFileControl(sqlite3_file*, int op, void *pArg);
169 static int instSectorSize(sqlite3_file*);
170 static int instDeviceCharacteristics(sqlite3_file*);
171 
172 /*
173 ** Method declarations for inst_vfs.
174 */
175 static int instOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
176 static int instDelete(sqlite3_vfs*, const char *zName, int syncDir);
177 static int instAccess(sqlite3_vfs*, const char *zName, int flags, int *);
178 static int instFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
179 static void *instDlOpen(sqlite3_vfs*, const char *zFilename);
180 static void instDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
181 static void (*instDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
182 static void instDlClose(sqlite3_vfs*, void*);
183 static int instRandomness(sqlite3_vfs*, int nByte, char *zOut);
184 static int instSleep(sqlite3_vfs*, int microseconds);
185 static int instCurrentTime(sqlite3_vfs*, double*);
186 
187 static void binarylog_blob(sqlite3_vfs *, const char *, int, int);
188 
189 static sqlite3_vfs inst_vfs = {
190   1,                      /* iVersion */
191   sizeof(inst_file),      /* szOsFile */
192   INST_MAX_PATHNAME,      /* mxPathname */
193   0,                      /* pNext */
194   0,                      /* zName */
195   0,                      /* pAppData */
196   instOpen,               /* xOpen */
197   instDelete,             /* xDelete */
198   instAccess,             /* xAccess */
199   instFullPathname,       /* xFullPathname */
200   instDlOpen,             /* xDlOpen */
201   instDlError,            /* xDlError */
202   instDlSym,              /* xDlSym */
203   instDlClose,            /* xDlClose */
204   instRandomness,         /* xRandomness */
205   instSleep,              /* xSleep */
206   instCurrentTime,        /* xCurrentTime */
207 };
208 
209 static sqlite3_io_methods inst_io_methods = {
210   1,                            /* iVersion */
211   instClose,                      /* xClose */
212   instRead,                       /* xRead */
213   instWrite,                      /* xWrite */
214   instTruncate,                   /* xTruncate */
215   instSync,                       /* xSync */
216   instFileSize,                   /* xFileSize */
217   instLock,                       /* xLock */
218   instUnlock,                     /* xUnlock */
219   instCheckReservedLock,          /* xCheckReservedLock */
220   instFileControl,                /* xFileControl */
221   instSectorSize,                 /* xSectorSize */
222   instDeviceCharacteristics       /* xDeviceCharacteristics */
223 };
224 
225 /*
226 ** hwtime.h contains inline assembler code for implementing
227 ** high-performance timing routines.
228 */
229 #include "hwtime.h"
230 
231 #define OS_TIME_IO(eEvent, A, B, Call) {     \
232   inst_file *p = (inst_file *)pFile;         \
233   InstVfs *pInstVfs = p->pInstVfs;           \
234   int rc;                                    \
235   sqlite_uint64 t = sqlite3Hwtime();         \
236   rc = Call;                                 \
237   t = sqlite3Hwtime() - t;                   \
238   pInstVfs->aTime[eEvent] += t;              \
239   pInstVfs->aCount[eEvent] += 1;             \
240   if( pInstVfs->xCall ){                     \
241     pInstVfs->xCall(                         \
242       pInstVfs->pClient,eEvent,p->iFileId,t,rc,p->zName,p->flags,A,B  \
243     );                                       \
244   }                                          \
245   return rc;                                 \
246 }
247 
248 #define OS_TIME_VFS(eEvent, Z, flags, A, B, Call) {      \
249   InstVfs *pInstVfs = (InstVfs *)pVfs;   \
250   int rc;                                \
251   sqlite_uint64 t = sqlite3Hwtime();     \
252   rc = Call;                             \
253   t = sqlite3Hwtime() - t;               \
254   pInstVfs->aTime[eEvent] += t;          \
255   pInstVfs->aCount[eEvent] += 1;         \
256   if( pInstVfs->xCall ){                 \
257     pInstVfs->xCall(pInstVfs->pClient,eEvent,0, t, rc, Z, flags, A, B); \
258   }                                      \
259   return rc;                             \
260 }
261 
262 /*
263 ** Close an inst-file.
264 */
265 static int instClose(sqlite3_file *pFile){
266   OS_TIME_IO(OS_CLOSE, 0, 0,
267     (p->pReal->pMethods ? p->pReal->pMethods->xClose(p->pReal) : SQLITE_OK)
268   );
269 }
270 
271 /*
272 ** Read data from an inst-file.
273 */
274 static int instRead(
275   sqlite3_file *pFile,
276   void *zBuf,
277   int iAmt,
278   sqlite_int64 iOfst
279 ){
280   sqlite3_vfs *pVfs = (sqlite3_vfs *)(((inst_file *)pFile)->pInstVfs);
281   OS_TIME_IO(OS_READ, iAmt, (binarylog_blob(pVfs, zBuf, iAmt, 1), iOfst),
282       p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst)
283   );
284 }
285 
286 /*
287 ** Write data to an inst-file.
288 */
289 static int instWrite(
290   sqlite3_file *pFile,
291   const void *z,
292   int iAmt,
293   sqlite_int64 iOfst
294 ){
295   sqlite3_vfs *pVfs = (sqlite3_vfs *)(((inst_file *)pFile)->pInstVfs);
296   binarylog_blob(pVfs, z, iAmt, 1);
297   OS_TIME_IO(OS_WRITE, iAmt, iOfst,
298       p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst)
299   );
300 }
301 
302 /*
303 ** Truncate an inst-file.
304 */
305 static int instTruncate(sqlite3_file *pFile, sqlite_int64 size){
306   OS_TIME_IO(OS_TRUNCATE, 0, (int)size,
307     p->pReal->pMethods->xTruncate(p->pReal, size)
308   );
309 }
310 
311 /*
312 ** Sync an inst-file.
313 */
314 static int instSync(sqlite3_file *pFile, int flags){
315   OS_TIME_IO(OS_SYNC, flags, 0, p->pReal->pMethods->xSync(p->pReal, flags));
316 }
317 
318 /*
319 ** Return the current file-size of an inst-file.
320 */
321 static int instFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
322   OS_TIME_IO(OS_FILESIZE, (int)(*pSize), 0,
323     p->pReal->pMethods->xFileSize(p->pReal, pSize)
324   );
325 }
326 
327 /*
328 ** Lock an inst-file.
329 */
330 static int instLock(sqlite3_file *pFile, int eLock){
331   OS_TIME_IO(OS_LOCK, eLock, 0, p->pReal->pMethods->xLock(p->pReal, eLock));
332 }
333 
334 /*
335 ** Unlock an inst-file.
336 */
337 static int instUnlock(sqlite3_file *pFile, int eLock){
338   OS_TIME_IO(OS_UNLOCK, eLock, 0, p->pReal->pMethods->xUnlock(p->pReal, eLock));
339 }
340 
341 /*
342 ** Check if another file-handle holds a RESERVED lock on an inst-file.
343 */
344 static int instCheckReservedLock(sqlite3_file *pFile, int *pResOut){
345   OS_TIME_IO(OS_CHECKRESERVEDLOCK, 0, 0,
346       p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut)
347   );
348 }
349 
350 /*
351 ** File control method. For custom operations on an inst-file.
352 */
353 static int instFileControl(sqlite3_file *pFile, int op, void *pArg){
354   OS_TIME_IO(OS_FILECONTROL, 0, 0, p->pReal->pMethods->xFileControl(p->pReal, op, pArg));
355 }
356 
357 /*
358 ** Return the sector-size in bytes for an inst-file.
359 */
360 static int instSectorSize(sqlite3_file *pFile){
361   OS_TIME_IO(OS_SECTORSIZE, 0, 0, p->pReal->pMethods->xSectorSize(p->pReal));
362 }
363 
364 /*
365 ** Return the device characteristic flags supported by an inst-file.
366 */
367 static int instDeviceCharacteristics(sqlite3_file *pFile){
368   OS_TIME_IO(OS_DEVCHAR, 0, 0, p->pReal->pMethods->xDeviceCharacteristics(p->pReal));
369 }
370 
371 /*
372 ** Open an inst file handle.
373 */
374 static int instOpen(
375   sqlite3_vfs *pVfs,
376   const char *zName,
377   sqlite3_file *pFile,
378   int flags,
379   int *pOutFlags
380 ){
381   inst_file *p = (inst_file *)pFile;
382   pFile->pMethods = &inst_io_methods;
383   p->pReal = (sqlite3_file *)&p[1];
384   p->pInstVfs = (InstVfs *)pVfs;
385   p->zName = zName;
386   p->flags = flags;
387   p->iFileId = ++p->pInstVfs->iNextFileId;
388 
389   binarylog_blob(pVfs, zName, -1, 0);
390   OS_TIME_VFS(OS_OPEN, zName, flags, p->iFileId, 0,
391     REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags)
392   );
393 }
394 
395 /*
396 ** Delete the file located at zPath. If the dirSync argument is true,
397 ** ensure the file-system modifications are synced to disk before
398 ** returning.
399 */
400 static int instDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
401   binarylog_blob(pVfs, zPath, -1, 0);
402   OS_TIME_VFS(OS_DELETE, zPath, 0, dirSync, 0,
403     REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync)
404   );
405 }
406 
407 /*
408 ** Test for access permissions. Return true if the requested permission
409 ** is available, or false otherwise.
410 */
411 static int instAccess(
412   sqlite3_vfs *pVfs,
413   const char *zPath,
414   int flags,
415   int *pResOut
416 ){
417   binarylog_blob(pVfs, zPath, -1, 0);
418   OS_TIME_VFS(OS_ACCESS, zPath, 0, flags, *pResOut,
419     REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut)
420   );
421 }
422 
423 /*
424 ** Populate buffer zOut with the full canonical pathname corresponding
425 ** to the pathname in zPath. zOut is guaranteed to point to a buffer
426 ** of at least (INST_MAX_PATHNAME+1) bytes.
427 */
428 static int instFullPathname(
429   sqlite3_vfs *pVfs,
430   const char *zPath,
431   int nOut,
432   char *zOut
433 ){
434   OS_TIME_VFS( OS_FULLPATHNAME, zPath, 0, 0, 0,
435     REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
436   );
437 }
438 
439 /*
440 ** Open the dynamic library located at zPath and return a handle.
441 */
442 static void *instDlOpen(sqlite3_vfs *pVfs, const char *zPath){
443   return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath);
444 }
445 
446 /*
447 ** Populate the buffer zErrMsg (size nByte bytes) with a human readable
448 ** utf-8 string describing the most recent error encountered associated
449 ** with dynamic libraries.
450 */
451 static void instDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
452   REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg);
453 }
454 
455 /*
456 ** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
457 */
458 static void (*instDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
459   return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym);
460 }
461 
462 /*
463 ** Close the dynamic library handle pHandle.
464 */
465 static void instDlClose(sqlite3_vfs *pVfs, void *pHandle){
466   REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle);
467 }
468 
469 /*
470 ** Populate the buffer pointed to by zBufOut with nByte bytes of
471 ** random data.
472 */
473 static int instRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
474   OS_TIME_VFS( OS_RANDOMNESS, 0, 0, nByte, 0,
475     REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
476   );
477 }
478 
479 /*
480 ** Sleep for nMicro microseconds. Return the number of microseconds
481 ** actually slept.
482 */
483 static int instSleep(sqlite3_vfs *pVfs, int nMicro){
484   OS_TIME_VFS( OS_SLEEP, 0, 0, nMicro, 0,
485     REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro)
486   );
487 }
488 
489 /*
490 ** Return the current time as a Julian Day number in *pTimeOut.
491 */
492 static int instCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
493   OS_TIME_VFS( OS_CURRENTTIME, 0, 0, 0, 0,
494     REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut)
495   );
496 }
497 
498 sqlite3_vfs *sqlite3_instvfs_create(const char *zName, const char *zParent){
499   int nByte;
500   InstVfs *p;
501   sqlite3_vfs *pParent;
502 
503   pParent = sqlite3_vfs_find(zParent);
504   if( !pParent ){
505     return 0;
506   }
507 
508   nByte = strlen(zName) + 1 + sizeof(InstVfs);
509   p = (InstVfs *)sqlite3_malloc(nByte);
510   if( p ){
511     char *zCopy = (char *)&p[1];
512     memset(p, 0, nByte);
513     memcpy(p, &inst_vfs, sizeof(sqlite3_vfs));
514     p->pVfs = pParent;
515     memcpy(zCopy, zName, strlen(zName));
516     p->base.zName = (const char *)zCopy;
517     p->base.szOsFile += pParent->szOsFile;
518     sqlite3_vfs_register((sqlite3_vfs *)p, 0);
519   }
520 
521   return (sqlite3_vfs *)p;
522 }
523 
524 void sqlite3_instvfs_configure(
525   sqlite3_vfs *pVfs,
526   void (*xCall)(
527       void*,
528       int,                           /* File id */
529       int,                           /* Event code */
530       sqlite3_int64,
531       int,                           /* Return code */
532       const char*,                   /* File name */
533       int,
534       int,
535       sqlite3_int64
536   ),
537   void *pClient,
538   void (*xDel)(void *)
539 ){
540   InstVfs *p = (InstVfs *)pVfs;
541   assert( pVfs->xOpen==instOpen );
542   if( p->xDel ){
543     p->xDel(p->pClient);
544   }
545   p->xCall = xCall;
546   p->xDel = xDel;
547   p->pClient = pClient;
548 }
549 
550 void sqlite3_instvfs_destroy(sqlite3_vfs *pVfs){
551   if( pVfs ){
552     sqlite3_vfs_unregister(pVfs);
553     sqlite3_instvfs_configure(pVfs, 0, 0, 0);
554     sqlite3_free(pVfs);
555   }
556 }
557 
558 void sqlite3_instvfs_reset(sqlite3_vfs *pVfs){
559   InstVfs *p = (InstVfs *)pVfs;
560   assert( pVfs->xOpen==instOpen );
561   memset(p->aTime, 0, sizeof(sqlite3_int64)*OS_NUMEVENTS);
562   memset(p->aCount, 0, sizeof(int)*OS_NUMEVENTS);
563 }
564 
565 const char *sqlite3_instvfs_name(int eEvent){
566   const char *zEvent = 0;
567 
568   switch( eEvent ){
569     case OS_CLOSE:             zEvent = "xClose"; break;
570     case OS_READ:              zEvent = "xRead"; break;
571     case OS_WRITE:             zEvent = "xWrite"; break;
572     case OS_TRUNCATE:          zEvent = "xTruncate"; break;
573     case OS_SYNC:              zEvent = "xSync"; break;
574     case OS_FILESIZE:          zEvent = "xFilesize"; break;
575     case OS_LOCK:              zEvent = "xLock"; break;
576     case OS_UNLOCK:            zEvent = "xUnlock"; break;
577     case OS_CHECKRESERVEDLOCK: zEvent = "xCheckReservedLock"; break;
578     case OS_FILECONTROL:       zEvent = "xFileControl"; break;
579     case OS_SECTORSIZE:        zEvent = "xSectorSize"; break;
580     case OS_DEVCHAR:           zEvent = "xDeviceCharacteristics"; break;
581     case OS_OPEN:              zEvent = "xOpen"; break;
582     case OS_DELETE:            zEvent = "xDelete"; break;
583     case OS_ACCESS:            zEvent = "xAccess"; break;
584     case OS_FULLPATHNAME:      zEvent = "xFullPathname"; break;
585     case OS_RANDOMNESS:        zEvent = "xRandomness"; break;
586     case OS_SLEEP:             zEvent = "xSleep"; break;
587     case OS_CURRENTTIME:       zEvent = "xCurrentTime"; break;
588   }
589 
590   return zEvent;
591 }
592 
593 void sqlite3_instvfs_get(
594   sqlite3_vfs *pVfs,
595   int eEvent,
596   const char **pzEvent,
597   sqlite3_int64 *pnClick,
598   int *pnCall
599 ){
600   InstVfs *p = (InstVfs *)pVfs;
601   assert( pVfs->xOpen==instOpen );
602   if( eEvent<1 || eEvent>=OS_NUMEVENTS ){
603     *pzEvent = 0;
604     *pnClick = 0;
605     *pnCall = 0;
606     return;
607   }
608 
609   *pzEvent = sqlite3_instvfs_name(eEvent);
610   *pnClick = p->aTime[eEvent];
611   *pnCall = p->aCount[eEvent];
612 }
613 
614 #define BINARYLOG_BUFFERSIZE 8192
615 
616 struct InstVfsBinaryLog {
617   int nBuf;
618   char *zBuf;
619   sqlite3_int64 iOffset;
620   int log_data;
621   sqlite3_file *pOut;
622   char *zOut;                       /* Log file name */
623 };
624 typedef struct InstVfsBinaryLog InstVfsBinaryLog;
625 
626 static void put32bits(unsigned char *p, unsigned int v){
627   p[0] = v>>24;
628   p[1] = v>>16;
629   p[2] = v>>8;
630   p[3] = v;
631 }
632 
633 static void binarylog_flush(InstVfsBinaryLog *pLog){
634   sqlite3_file *pFile = pLog->pOut;
635 
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   pFile->pMethods->xWrite(pFile, pLog->zBuf, pLog->nBuf, pLog->iOffset);
651   pLog->iOffset += pLog->nBuf;
652   pLog->nBuf = 0;
653 
654 #ifdef SQLITE_TEST
655   sqlite3_io_error_pending = pending;
656   sqlite3_io_error_persist = persist;
657   sqlite3_diskfull_pending = diskfull;
658 #endif
659 }
660 
661 static void binarylog_xcall(
662   void *p,
663   int eEvent,
664   int iFileId,
665   sqlite3_int64 nClick,
666   int return_code,
667   const char *zName,
668   int flags,
669   int nByte,
670   sqlite3_int64 iOffset
671 ){
672   InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)p;
673   unsigned char *zRec;
674   if( (28+pLog->nBuf)>BINARYLOG_BUFFERSIZE ){
675     binarylog_flush(pLog);
676   }
677   zRec = (unsigned char *)&pLog->zBuf[pLog->nBuf];
678   put32bits(&zRec[0], eEvent);
679   put32bits(&zRec[4], (int)iFileId);
680   put32bits(&zRec[8], (int)nClick);
681   put32bits(&zRec[12], return_code);
682   put32bits(&zRec[16], flags);
683   put32bits(&zRec[20], nByte);
684   put32bits(&zRec[24], (int)iOffset);
685   pLog->nBuf += 28;
686 }
687 
688 static void binarylog_xdel(void *p){
689   /* Close the log file and free the memory allocated for the
690   ** InstVfsBinaryLog structure.
691   */
692   InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)p;
693   sqlite3_file *pFile = pLog->pOut;
694   if( pLog->nBuf ){
695     binarylog_flush(pLog);
696   }
697   pFile->pMethods->xClose(pFile);
698   sqlite3_free(pLog->pOut);
699   sqlite3_free(pLog->zBuf);
700   sqlite3_free(pLog);
701 }
702 
703 static void binarylog_blob(
704   sqlite3_vfs *pVfs,
705   const char *zBlob,
706   int nBlob,
707   int isBinary
708 ){
709   InstVfsBinaryLog *pLog;
710   InstVfs *pInstVfs = (InstVfs *)pVfs;
711 
712   if( pVfs->xOpen!=instOpen || pInstVfs->xCall!=binarylog_xcall ){
713     return;
714   }
715   pLog = (InstVfsBinaryLog *)pInstVfs->pClient;
716   if( zBlob && (!isBinary || pLog->log_data) ){
717     unsigned char *zRec;
718     int nWrite;
719 
720     if( nBlob<0 ){
721       nBlob = strlen(zBlob);
722     }
723     nWrite = nBlob + 28;
724 
725     if( (nWrite+pLog->nBuf)>BINARYLOG_BUFFERSIZE ){
726       binarylog_flush(pLog);
727     }
728 
729     zRec = (unsigned char *)&pLog->zBuf[pLog->nBuf];
730     memset(zRec, 0, nWrite);
731     put32bits(&zRec[0], BINARYLOG_STRING);
732     put32bits(&zRec[4], (int)nBlob);
733     put32bits(&zRec[8], (int)isBinary);
734     memcpy(&zRec[28], zBlob, nBlob);
735     pLog->nBuf += nWrite;
736   }
737 }
738 
739 void sqlite3_instvfs_binarylog_call(
740   sqlite3_vfs *pVfs,
741   int eEvent,
742   sqlite3_int64 nClick,
743   int return_code,
744   const char *zString
745 ){
746   InstVfs *pInstVfs = (InstVfs *)pVfs;
747   InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)pInstVfs->pClient;
748 
749   if( zString ){
750     binarylog_blob(pVfs, zString, -1, 0);
751   }
752   binarylog_xcall(pLog, eEvent, 0, nClick, return_code, 0, 0, 0, 0);
753 }
754 
755 void sqlite3_instvfs_binarylog_marker(
756   sqlite3_vfs *pVfs,
757   const char *zMarker
758 ){
759   InstVfs *pInstVfs = (InstVfs *)pVfs;
760   InstVfsBinaryLog *pLog = (InstVfsBinaryLog *)pInstVfs->pClient;
761   binarylog_blob(pVfs, zMarker, -1, 0);
762   binarylog_xcall(pLog, BINARYLOG_MARKER, 0, 0, 0, 0, 0, 0, 0);
763 }
764 
765 sqlite3_vfs *sqlite3_instvfs_binarylog(
766   const char *zVfs,
767   const char *zParentVfs,
768   const char *zLog,
769   int log_data
770 ){
771   InstVfsBinaryLog *p;
772   sqlite3_vfs *pVfs;
773   sqlite3_vfs *pParent;
774   int nByte;
775   int flags;
776   int rc;
777 
778   pParent = sqlite3_vfs_find(zParentVfs);
779   if( !pParent ){
780     return 0;
781   }
782 
783   nByte = sizeof(InstVfsBinaryLog) + pParent->mxPathname+1;
784   p = (InstVfsBinaryLog *)sqlite3_malloc(nByte);
785   memset(p, 0, nByte);
786   p->zBuf = sqlite3_malloc(BINARYLOG_BUFFERSIZE);
787   p->zOut = (char *)&p[1];
788   p->pOut = (sqlite3_file *)sqlite3_malloc(pParent->szOsFile);
789   p->log_data = log_data;
790   pParent->xFullPathname(pParent, zLog, pParent->mxPathname, p->zOut);
791   flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MASTER_JOURNAL;
792   pParent->xDelete(pParent, p->zOut, 0);
793   rc = pParent->xOpen(pParent, p->zOut, p->pOut, flags, &flags);
794   if( rc==SQLITE_OK ){
795     memcpy(p->zBuf, "sqlite_ostrace1.....", 20);
796     p->iOffset = 0;
797     p->nBuf = 20;
798   }
799   if( rc ){
800     binarylog_xdel(p);
801     return 0;
802   }
803 
804   pVfs = sqlite3_instvfs_create(zVfs, zParentVfs);
805   if( pVfs ){
806     sqlite3_instvfs_configure(pVfs, binarylog_xcall, p, binarylog_xdel);
807   }
808 
809   return pVfs;
810 }
811 #endif /* SQLITE_ENABLE_INSTVFS */
812 
813 /**************************************************************************
814 ***************************************************************************
815 ** Tcl interface starts here.
816 */
817 #if SQLITE_TEST
818 
819 #include <tcl.h>
820 
821 #ifdef SQLITE_ENABLE_INSTVFS
822 struct InstVfsCall {
823   Tcl_Interp *interp;
824   Tcl_Obj *pScript;
825 };
826 typedef struct InstVfsCall InstVfsCall;
827 
828 static void test_instvfs_xcall(
829   void *p,
830   int eEvent,
831   int iFileId,
832   sqlite3_int64 nClick,
833   int return_code,
834   const char *zName,
835   int flags,
836   int nByte,
837   sqlite3_int64 iOffset
838 ){
839   int rc;
840   InstVfsCall *pCall = (InstVfsCall *)p;
841   Tcl_Obj *pObj = Tcl_DuplicateObj( pCall->pScript);
842   const char *zEvent = sqlite3_instvfs_name(eEvent);
843 
844   Tcl_IncrRefCount(pObj);
845   Tcl_ListObjAppendElement(0, pObj, Tcl_NewStringObj(zEvent, -1));
846   Tcl_ListObjAppendElement(0, pObj, Tcl_NewWideIntObj(nClick));
847   Tcl_ListObjAppendElement(0, pObj, Tcl_NewStringObj(zName, -1));
848   Tcl_ListObjAppendElement(0, pObj, Tcl_NewIntObj(nByte));
849   Tcl_ListObjAppendElement(0, pObj, Tcl_NewWideIntObj(iOffset));
850 
851   rc = Tcl_EvalObjEx(pCall->interp, pObj, TCL_EVAL_GLOBAL|TCL_EVAL_DIRECT);
852   if( rc ){
853     Tcl_BackgroundError(pCall->interp);
854   }
855   Tcl_DecrRefCount(pObj);
856 }
857 
858 static void test_instvfs_xdel(void *p){
859   InstVfsCall *pCall = (InstVfsCall *)p;
860   Tcl_DecrRefCount(pCall->pScript);
861   sqlite3_free(pCall);
862 }
863 
864 static int test_sqlite3_instvfs(
865   void * clientData,
866   Tcl_Interp *interp,
867   int objc,
868   Tcl_Obj *CONST objv[]
869 ){
870   static const char *IV_strs[] =
871                { "create",  "destroy",  "reset",  "report", "configure", "binarylog", "marker", 0 };
872   enum IV_enum { IV_CREATE, IV_DESTROY, IV_RESET, IV_REPORT, IV_CONFIGURE, IV_BINARYLOG, IV_MARKER };
873   int iSub;
874 
875   if( objc<2 ){
876     Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
877   }
878   if( Tcl_GetIndexFromObj(interp, objv[1], IV_strs, "sub-command", 0, &iSub) ){
879     return TCL_ERROR;
880   }
881 
882   switch( (enum IV_enum)iSub ){
883     case IV_CREATE: {
884       char *zParent = 0;
885       sqlite3_vfs *p;
886       int isDefault = 0;
887       if( objc>2 && 0==strcmp("-default", Tcl_GetString(objv[2])) ){
888         isDefault = 1;
889       }
890       if( (objc-isDefault)!=4 && (objc-isDefault)!=3 ){
891         Tcl_WrongNumArgs(interp, 2, objv, "?-default? NAME ?PARENT-VFS?");
892         return TCL_ERROR;
893       }
894       if( objc==(4+isDefault) ){
895         zParent = Tcl_GetString(objv[3+isDefault]);
896       }
897       p = sqlite3_instvfs_create(Tcl_GetString(objv[2+isDefault]), zParent);
898       if( !p ){
899         Tcl_AppendResult(interp, "error creating vfs ", 0);
900         return TCL_ERROR;
901       }
902       if( isDefault ){
903         sqlite3_vfs_register(p, 1);
904       }
905       Tcl_SetObjResult(interp, objv[2]);
906       break;
907     }
908     case IV_BINARYLOG: {
909       char *zName = 0;
910       char *zLog = 0;
911       char *zParent = 0;
912       sqlite3_vfs *p;
913       int isDefault = 0;
914       int isLogdata = 0;
915       int argbase = 2;
916 
917       for(argbase=2; argbase<(objc-2); argbase++){
918         if( 0==strcmp("-default", Tcl_GetString(objv[argbase])) ){
919           isDefault = 1;
920         }
921         else if( 0==strcmp("-parent", Tcl_GetString(objv[argbase])) ){
922           argbase++;
923           zParent = Tcl_GetString(objv[argbase]);
924         }
925         else if( 0==strcmp("-logdata", Tcl_GetString(objv[argbase])) ){
926           isLogdata = 1;
927         }else{
928           break;
929         }
930       }
931 
932       if( (objc-argbase)!=2 ){
933         Tcl_WrongNumArgs(
934             interp, 2, objv, "?-default? ?-parent VFS? ?-logdata? NAME LOGFILE"
935         );
936         return TCL_ERROR;
937       }
938       zName = Tcl_GetString(objv[argbase]);
939       zLog = Tcl_GetString(objv[argbase+1]);
940       p = sqlite3_instvfs_binarylog(zName, zParent, zLog, isLogdata);
941       if( !p ){
942         Tcl_AppendResult(interp, "error creating vfs ", 0);
943         return TCL_ERROR;
944       }
945       if( isDefault ){
946         sqlite3_vfs_register(p, 1);
947       }
948       Tcl_SetObjResult(interp, objv[2]);
949       break;
950     }
951 
952     case IV_MARKER: {
953       sqlite3_vfs *p;
954       if( objc!=4 ){
955         Tcl_WrongNumArgs(interp, 2, objv, "VFS MARKER");
956         return TCL_ERROR;
957       }
958       p = sqlite3_vfs_find(Tcl_GetString(objv[2]));
959       if( !p || p->xOpen!=instOpen ){
960         Tcl_AppendResult(interp, "no such vfs: ", Tcl_GetString(objv[2]), 0);
961         return TCL_ERROR;
962       }
963       sqlite3_instvfs_binarylog_marker(p, Tcl_GetString(objv[3]));
964       Tcl_ResetResult(interp);
965       break;
966     }
967 
968     case IV_CONFIGURE: {
969       InstVfsCall *pCall;
970 
971       sqlite3_vfs *p;
972       if( objc!=4 ){
973         Tcl_WrongNumArgs(interp, 2, objv, "NAME SCRIPT");
974         return TCL_ERROR;
975       }
976       p = sqlite3_vfs_find(Tcl_GetString(objv[2]));
977       if( !p || p->xOpen!=instOpen ){
978         Tcl_AppendResult(interp, "no such vfs: ", Tcl_GetString(objv[2]), 0);
979         return TCL_ERROR;
980       }
981 
982       if( strlen(Tcl_GetString(objv[3])) ){
983         pCall = (InstVfsCall *)sqlite3_malloc(sizeof(InstVfsCall));
984         pCall->interp = interp;
985         pCall->pScript = Tcl_DuplicateObj(objv[3]);
986         Tcl_IncrRefCount(pCall->pScript);
987         sqlite3_instvfs_configure(p,
988             test_instvfs_xcall, (void *)pCall, test_instvfs_xdel
989         );
990       }else{
991         sqlite3_instvfs_configure(p, 0, 0, 0);
992       }
993       break;
994     }
995 
996     case IV_REPORT:
997     case IV_DESTROY:
998     case IV_RESET: {
999       sqlite3_vfs *p;
1000       if( objc!=3 ){
1001         Tcl_WrongNumArgs(interp, 2, objv, "NAME");
1002         return TCL_ERROR;
1003       }
1004       p = sqlite3_vfs_find(Tcl_GetString(objv[2]));
1005       if( !p || p->xOpen!=instOpen ){
1006         Tcl_AppendResult(interp, "no such vfs: ", Tcl_GetString(objv[2]), 0);
1007         return TCL_ERROR;
1008       }
1009 
1010       if( ((enum IV_enum)iSub)==IV_DESTROY ){
1011         sqlite3_instvfs_destroy(p);
1012       }
1013       if( ((enum IV_enum)iSub)==IV_RESET ){
1014         sqlite3_instvfs_reset(p);
1015       }
1016       if( ((enum IV_enum)iSub)==IV_REPORT ){
1017         int ii;
1018         Tcl_Obj *pRet = Tcl_NewObj();
1019 
1020         const char *zName = (char *)1;
1021         sqlite3_int64 nClick;
1022         int nCall;
1023         for(ii=1; zName; ii++){
1024           sqlite3_instvfs_get(p, ii, &zName, &nClick, &nCall);
1025           if( zName ){
1026             Tcl_Obj *pElem = Tcl_NewObj();
1027             Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zName, -1));
1028             Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(nCall));
1029             Tcl_ListObjAppendElement(0, pElem, Tcl_NewWideIntObj(nClick));
1030             Tcl_ListObjAppendElement(0, pRet, pElem);
1031           }
1032         }
1033 
1034         Tcl_SetObjResult(interp, pRet);
1035       }
1036 
1037       break;
1038     }
1039   }
1040 
1041   return TCL_OK;
1042 }
1043 #endif /* SQLITE_ENABLE_INSTVFS */
1044 
1045 /* Alternative implementation of sqlite3_instvfs when the real
1046 ** implementation is unavailable.
1047 */
1048 #ifndef SQLITE_ENABLE_INSTVFS
1049 static int test_sqlite3_instvfs(
1050   void * clientData,
1051   Tcl_Interp *interp,
1052   int objc,
1053   Tcl_Obj *CONST objv[]
1054 ){
1055   Tcl_AppendResult(interp,
1056      "not compiled with -DSQLITE_ENABLE_INSTVFS; sqlite3_instvfs is "
1057      "unavailable", (char*)0);
1058   return TCL_ERROR;
1059 }
1060 #endif /* !defined(SQLITE_ENABLE_INSTVFS) */
1061 
1062 int SqlitetestOsinst_Init(Tcl_Interp *interp){
1063   Tcl_CreateObjCommand(interp, "sqlite3_instvfs", test_sqlite3_instvfs, 0, 0);
1064   return TCL_OK;
1065 }
1066 
1067 #endif /* SQLITE_TEST */
1068