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