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