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