Merge pull request #37 from developernotes/prerelease
[sqlcipher.git] / src / test_osinst.c
blob531433313ee36c07eb21c02f3115cb7a37486c79
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.
11 ******************************************************************************
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.
19 ** This module contains code for a wrapper VFS that causes a log of
20 ** most VFS calls to be written into a nominated file on disk. The log
21 ** is stored in a compressed binary format to reduce the amount of IO
22 ** overhead introduced into the application by logging.
24 ** All calls on sqlite3_file objects except xFileControl() are logged.
25 ** Additionally, calls to the xAccess(), xOpen(), and xDelete()
26 ** methods are logged. The other sqlite3_vfs object methods (xDlXXX,
27 ** xRandomness, xSleep, xCurrentTime, xGetLastError and xCurrentTimeInt64)
28 ** are not logged.
30 ** The binary log files are read using a virtual table implementation
31 ** also contained in this file.
33 ** CREATING LOG FILES:
35 ** int sqlite3_vfslog_new(
36 ** const char *zVfs, // Name of new VFS
37 ** const char *zParentVfs, // Name of parent VFS (or NULL)
38 ** const char *zLog // Name of log file to write to
39 ** );
41 ** int sqlite3_vfslog_finalize(const char *zVfs);
43 ** ANNOTATING LOG FILES:
45 ** To write an arbitrary message into a log file:
47 ** int sqlite3_vfslog_annotate(const char *zVfs, const char *zMsg);
49 ** READING LOG FILES:
51 ** Log files are read using the "vfslog" virtual table implementation
52 ** in this file. To register the virtual table with SQLite, use:
54 ** int sqlite3_vfslog_register(sqlite3 *db);
56 ** Then, if the log file is named "vfs.log", the following SQL command:
58 ** CREATE VIRTUAL TABLE v USING vfslog('vfs.log');
60 ** creates a virtual table with 6 columns, as follows:
62 ** CREATE TABLE v(
63 ** event TEXT, // "xOpen", "xRead" etc.
64 ** file TEXT, // Name of file this call applies to
65 ** clicks INTEGER, // Time spent in call
66 ** rc INTEGER, // Return value
67 ** size INTEGER, // Bytes read or written
68 ** offset INTEGER // File offset read or written
69 ** );
72 #include "sqlite3.h"
73 #include <string.h>
74 #include <assert.h>
78 ** Maximum pathname length supported by the vfslog backend.
80 #define INST_MAX_PATHNAME 512
82 #define OS_ACCESS 1
83 #define OS_CHECKRESERVEDLOCK 2
84 #define OS_CLOSE 3
85 #define OS_CURRENTTIME 4
86 #define OS_DELETE 5
87 #define OS_DEVCHAR 6
88 #define OS_FILECONTROL 7
89 #define OS_FILESIZE 8
90 #define OS_FULLPATHNAME 9
91 #define OS_LOCK 11
92 #define OS_OPEN 12
93 #define OS_RANDOMNESS 13
94 #define OS_READ 14
95 #define OS_SECTORSIZE 15
96 #define OS_SLEEP 16
97 #define OS_SYNC 17
98 #define OS_TRUNCATE 18
99 #define OS_UNLOCK 19
100 #define OS_WRITE 20
101 #define OS_SHMUNMAP 22
102 #define OS_SHMMAP 23
103 #define OS_SHMLOCK 25
104 #define OS_SHMBARRIER 26
105 #define OS_ANNOTATE 28
107 #define OS_NUMEVENTS 29
109 #define VFSLOG_BUFFERSIZE 8192
111 typedef struct VfslogVfs VfslogVfs;
112 typedef struct VfslogFile VfslogFile;
114 struct VfslogVfs {
115 sqlite3_vfs base; /* VFS methods */
116 sqlite3_vfs *pVfs; /* Parent VFS */
117 int iNextFileId; /* Next file id */
118 sqlite3_file *pLog; /* Log file handle */
119 sqlite3_int64 iOffset; /* Log file offset of start of write buffer */
120 int nBuf; /* Number of valid bytes in aBuf[] */
121 char aBuf[VFSLOG_BUFFERSIZE]; /* Write buffer */
124 struct VfslogFile {
125 sqlite3_file base; /* IO methods */
126 sqlite3_file *pReal; /* Underlying file handle */
127 sqlite3_vfs *pVfslog; /* Associated VsflogVfs object */
128 int iFileId; /* File id number */
131 #define REALVFS(p) (((VfslogVfs *)(p))->pVfs)
136 ** Method declarations for vfslog_file.
138 static int vfslogClose(sqlite3_file*);
139 static int vfslogRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
140 static int vfslogWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
141 static int vfslogTruncate(sqlite3_file*, sqlite3_int64 size);
142 static int vfslogSync(sqlite3_file*, int flags);
143 static int vfslogFileSize(sqlite3_file*, sqlite3_int64 *pSize);
144 static int vfslogLock(sqlite3_file*, int);
145 static int vfslogUnlock(sqlite3_file*, int);
146 static int vfslogCheckReservedLock(sqlite3_file*, int *pResOut);
147 static int vfslogFileControl(sqlite3_file*, int op, void *pArg);
148 static int vfslogSectorSize(sqlite3_file*);
149 static int vfslogDeviceCharacteristics(sqlite3_file*);
151 static int vfslogShmLock(sqlite3_file *pFile, int ofst, int n, int flags);
152 static int vfslogShmMap(sqlite3_file *pFile,int,int,int,volatile void **);
153 static void vfslogShmBarrier(sqlite3_file*);
154 static int vfslogShmUnmap(sqlite3_file *pFile, int deleteFlag);
157 ** Method declarations for vfslog_vfs.
159 static int vfslogOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
160 static int vfslogDelete(sqlite3_vfs*, const char *zName, int syncDir);
161 static int vfslogAccess(sqlite3_vfs*, const char *zName, int flags, int *);
162 static int vfslogFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
163 static void *vfslogDlOpen(sqlite3_vfs*, const char *zFilename);
164 static void vfslogDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
165 static void (*vfslogDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
166 static void vfslogDlClose(sqlite3_vfs*, void*);
167 static int vfslogRandomness(sqlite3_vfs*, int nByte, char *zOut);
168 static int vfslogSleep(sqlite3_vfs*, int microseconds);
169 static int vfslogCurrentTime(sqlite3_vfs*, double*);
171 static int vfslogGetLastError(sqlite3_vfs*, int, char *);
172 static int vfslogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
174 static sqlite3_vfs vfslog_vfs = {
175 1, /* iVersion */
176 sizeof(VfslogFile), /* szOsFile */
177 INST_MAX_PATHNAME, /* mxPathname */
178 0, /* pNext */
179 0, /* zName */
180 0, /* pAppData */
181 vfslogOpen, /* xOpen */
182 vfslogDelete, /* xDelete */
183 vfslogAccess, /* xAccess */
184 vfslogFullPathname, /* xFullPathname */
185 vfslogDlOpen, /* xDlOpen */
186 vfslogDlError, /* xDlError */
187 vfslogDlSym, /* xDlSym */
188 vfslogDlClose, /* xDlClose */
189 vfslogRandomness, /* xRandomness */
190 vfslogSleep, /* xSleep */
191 vfslogCurrentTime, /* xCurrentTime */
192 vfslogGetLastError, /* xGetLastError */
193 vfslogCurrentTimeInt64 /* xCurrentTime */
196 static sqlite3_io_methods vfslog_io_methods = {
197 2, /* iVersion */
198 vfslogClose, /* xClose */
199 vfslogRead, /* xRead */
200 vfslogWrite, /* xWrite */
201 vfslogTruncate, /* xTruncate */
202 vfslogSync, /* xSync */
203 vfslogFileSize, /* xFileSize */
204 vfslogLock, /* xLock */
205 vfslogUnlock, /* xUnlock */
206 vfslogCheckReservedLock, /* xCheckReservedLock */
207 vfslogFileControl, /* xFileControl */
208 vfslogSectorSize, /* xSectorSize */
209 vfslogDeviceCharacteristics, /* xDeviceCharacteristics */
210 vfslogShmMap, /* xShmMap */
211 vfslogShmLock, /* xShmLock */
212 vfslogShmBarrier, /* xShmBarrier */
213 vfslogShmUnmap /* xShmUnmap */
216 #if SQLITE_OS_UNIX && !defined(NO_GETTOD)
217 #include <sys/time.h>
218 static sqlite3_uint64 vfslog_time(){
219 struct timeval sTime;
220 gettimeofday(&sTime, 0);
221 return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000;
223 #elif SQLITE_OS_WIN
224 #include <windows.h>
225 #include <time.h>
226 static sqlite3_uint64 vfslog_time(){
227 FILETIME ft;
228 sqlite3_uint64 u64time = 0;
230 GetSystemTimeAsFileTime(&ft);
232 u64time |= ft.dwHighDateTime;
233 u64time <<= 32;
234 u64time |= ft.dwLowDateTime;
236 /* ft is 100-nanosecond intervals, we want microseconds */
237 return u64time /(sqlite3_uint64)10;
239 #else
240 static sqlite3_uint64 vfslog_time(){
241 return 0;
243 #endif
245 static void vfslog_call(sqlite3_vfs *, int, int, sqlite3_int64, int, int, int);
246 static void vfslog_string(sqlite3_vfs *, const char *);
249 ** Close an vfslog-file.
251 static int vfslogClose(sqlite3_file *pFile){
252 sqlite3_uint64 t;
253 int rc = SQLITE_OK;
254 VfslogFile *p = (VfslogFile *)pFile;
256 t = vfslog_time();
257 if( p->pReal->pMethods ){
258 rc = p->pReal->pMethods->xClose(p->pReal);
260 t = vfslog_time() - t;
261 vfslog_call(p->pVfslog, OS_CLOSE, p->iFileId, t, rc, 0, 0);
262 return rc;
266 ** Read data from an vfslog-file.
268 static int vfslogRead(
269 sqlite3_file *pFile,
270 void *zBuf,
271 int iAmt,
272 sqlite_int64 iOfst
274 int rc;
275 sqlite3_uint64 t;
276 VfslogFile *p = (VfslogFile *)pFile;
277 t = vfslog_time();
278 rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
279 t = vfslog_time() - t;
280 vfslog_call(p->pVfslog, OS_READ, p->iFileId, t, rc, iAmt, (int)iOfst);
281 return rc;
285 ** Write data to an vfslog-file.
287 static int vfslogWrite(
288 sqlite3_file *pFile,
289 const void *z,
290 int iAmt,
291 sqlite_int64 iOfst
293 int rc;
294 sqlite3_uint64 t;
295 VfslogFile *p = (VfslogFile *)pFile;
296 t = vfslog_time();
297 rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst);
298 t = vfslog_time() - t;
299 vfslog_call(p->pVfslog, OS_WRITE, p->iFileId, t, rc, iAmt, (int)iOfst);
300 return rc;
304 ** Truncate an vfslog-file.
306 static int vfslogTruncate(sqlite3_file *pFile, sqlite_int64 size){
307 int rc;
308 sqlite3_uint64 t;
309 VfslogFile *p = (VfslogFile *)pFile;
310 t = vfslog_time();
311 rc = p->pReal->pMethods->xTruncate(p->pReal, size);
312 t = vfslog_time() - t;
313 vfslog_call(p->pVfslog, OS_TRUNCATE, p->iFileId, t, rc, 0, (int)size);
314 return rc;
318 ** Sync an vfslog-file.
320 static int vfslogSync(sqlite3_file *pFile, int flags){
321 int rc;
322 sqlite3_uint64 t;
323 VfslogFile *p = (VfslogFile *)pFile;
324 t = vfslog_time();
325 rc = p->pReal->pMethods->xSync(p->pReal, flags);
326 t = vfslog_time() - t;
327 vfslog_call(p->pVfslog, OS_SYNC, p->iFileId, t, rc, flags, 0);
328 return rc;
332 ** Return the current file-size of an vfslog-file.
334 static int vfslogFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
335 int rc;
336 sqlite3_uint64 t;
337 VfslogFile *p = (VfslogFile *)pFile;
338 t = vfslog_time();
339 rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
340 t = vfslog_time() - t;
341 vfslog_call(p->pVfslog, OS_FILESIZE, p->iFileId, t, rc, 0, (int)*pSize);
342 return rc;
346 ** Lock an vfslog-file.
348 static int vfslogLock(sqlite3_file *pFile, int eLock){
349 int rc;
350 sqlite3_uint64 t;
351 VfslogFile *p = (VfslogFile *)pFile;
352 t = vfslog_time();
353 rc = p->pReal->pMethods->xLock(p->pReal, eLock);
354 t = vfslog_time() - t;
355 vfslog_call(p->pVfslog, OS_LOCK, p->iFileId, t, rc, eLock, 0);
356 return rc;
360 ** Unlock an vfslog-file.
362 static int vfslogUnlock(sqlite3_file *pFile, int eLock){
363 int rc;
364 sqlite3_uint64 t;
365 VfslogFile *p = (VfslogFile *)pFile;
366 t = vfslog_time();
367 rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
368 t = vfslog_time() - t;
369 vfslog_call(p->pVfslog, OS_UNLOCK, p->iFileId, t, rc, eLock, 0);
370 return rc;
374 ** Check if another file-handle holds a RESERVED lock on an vfslog-file.
376 static int vfslogCheckReservedLock(sqlite3_file *pFile, int *pResOut){
377 int rc;
378 sqlite3_uint64 t;
379 VfslogFile *p = (VfslogFile *)pFile;
380 t = vfslog_time();
381 rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
382 t = vfslog_time() - t;
383 vfslog_call(p->pVfslog, OS_CHECKRESERVEDLOCK, p->iFileId, t, rc, *pResOut, 0);
384 return rc;
388 ** File control method. For custom operations on an vfslog-file.
390 static int vfslogFileControl(sqlite3_file *pFile, int op, void *pArg){
391 VfslogFile *p = (VfslogFile *)pFile;
392 int rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
393 if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
394 *(char**)pArg = sqlite3_mprintf("vfslog/%z", *(char**)pArg);
396 return rc;
400 ** Return the sector-size in bytes for an vfslog-file.
402 static int vfslogSectorSize(sqlite3_file *pFile){
403 int rc;
404 sqlite3_uint64 t;
405 VfslogFile *p = (VfslogFile *)pFile;
406 t = vfslog_time();
407 rc = p->pReal->pMethods->xSectorSize(p->pReal);
408 t = vfslog_time() - t;
409 vfslog_call(p->pVfslog, OS_SECTORSIZE, p->iFileId, t, rc, 0, 0);
410 return rc;
414 ** Return the device characteristic flags supported by an vfslog-file.
416 static int vfslogDeviceCharacteristics(sqlite3_file *pFile){
417 int rc;
418 sqlite3_uint64 t;
419 VfslogFile *p = (VfslogFile *)pFile;
420 t = vfslog_time();
421 rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
422 t = vfslog_time() - t;
423 vfslog_call(p->pVfslog, OS_DEVCHAR, p->iFileId, t, rc, 0, 0);
424 return rc;
427 static int vfslogShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
428 int rc;
429 sqlite3_uint64 t;
430 VfslogFile *p = (VfslogFile *)pFile;
431 t = vfslog_time();
432 rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
433 t = vfslog_time() - t;
434 vfslog_call(p->pVfslog, OS_SHMLOCK, p->iFileId, t, rc, 0, 0);
435 return rc;
437 static int vfslogShmMap(
438 sqlite3_file *pFile,
439 int iRegion,
440 int szRegion,
441 int isWrite,
442 volatile void **pp
444 int rc;
445 sqlite3_uint64 t;
446 VfslogFile *p = (VfslogFile *)pFile;
447 t = vfslog_time();
448 rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
449 t = vfslog_time() - t;
450 vfslog_call(p->pVfslog, OS_SHMMAP, p->iFileId, t, rc, 0, 0);
451 return rc;
453 static void vfslogShmBarrier(sqlite3_file *pFile){
454 sqlite3_uint64 t;
455 VfslogFile *p = (VfslogFile *)pFile;
456 t = vfslog_time();
457 p->pReal->pMethods->xShmBarrier(p->pReal);
458 t = vfslog_time() - t;
459 vfslog_call(p->pVfslog, OS_SHMBARRIER, p->iFileId, t, SQLITE_OK, 0, 0);
461 static int vfslogShmUnmap(sqlite3_file *pFile, int deleteFlag){
462 int rc;
463 sqlite3_uint64 t;
464 VfslogFile *p = (VfslogFile *)pFile;
465 t = vfslog_time();
466 rc = p->pReal->pMethods->xShmUnmap(p->pReal, deleteFlag);
467 t = vfslog_time() - t;
468 vfslog_call(p->pVfslog, OS_SHMUNMAP, p->iFileId, t, rc, 0, 0);
469 return rc;
474 ** Open an vfslog file handle.
476 static int vfslogOpen(
477 sqlite3_vfs *pVfs,
478 const char *zName,
479 sqlite3_file *pFile,
480 int flags,
481 int *pOutFlags
483 int rc;
484 sqlite3_uint64 t;
485 VfslogFile *p = (VfslogFile *)pFile;
486 VfslogVfs *pLog = (VfslogVfs *)pVfs;
488 pFile->pMethods = &vfslog_io_methods;
489 p->pReal = (sqlite3_file *)&p[1];
490 p->pVfslog = pVfs;
491 p->iFileId = ++pLog->iNextFileId;
493 t = vfslog_time();
494 rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags);
495 t = vfslog_time() - t;
497 vfslog_call(pVfs, OS_OPEN, p->iFileId, t, rc, 0, 0);
498 vfslog_string(pVfs, zName);
499 return rc;
503 ** Delete the file located at zPath. If the dirSync argument is true,
504 ** ensure the file-system modifications are synced to disk before
505 ** returning.
507 static int vfslogDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
508 int rc;
509 sqlite3_uint64 t;
510 t = vfslog_time();
511 rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync);
512 t = vfslog_time() - t;
513 vfslog_call(pVfs, OS_DELETE, 0, t, rc, dirSync, 0);
514 vfslog_string(pVfs, zPath);
515 return rc;
519 ** Test for access permissions. Return true if the requested permission
520 ** is available, or false otherwise.
522 static int vfslogAccess(
523 sqlite3_vfs *pVfs,
524 const char *zPath,
525 int flags,
526 int *pResOut
528 int rc;
529 sqlite3_uint64 t;
530 t = vfslog_time();
531 rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut);
532 t = vfslog_time() - t;
533 vfslog_call(pVfs, OS_ACCESS, 0, t, rc, flags, *pResOut);
534 vfslog_string(pVfs, zPath);
535 return rc;
539 ** Populate buffer zOut with the full canonical pathname corresponding
540 ** to the pathname in zPath. zOut is guaranteed to point to a buffer
541 ** of at least (INST_MAX_PATHNAME+1) bytes.
543 static int vfslogFullPathname(
544 sqlite3_vfs *pVfs,
545 const char *zPath,
546 int nOut,
547 char *zOut
549 return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
553 ** Open the dynamic library located at zPath and return a handle.
555 static void *vfslogDlOpen(sqlite3_vfs *pVfs, const char *zPath){
556 return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath);
560 ** Populate the buffer zErrMsg (size nByte bytes) with a human readable
561 ** utf-8 string describing the most recent error encountered associated
562 ** with dynamic libraries.
564 static void vfslogDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
565 REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg);
569 ** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
571 static void (*vfslogDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
572 return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym);
576 ** Close the dynamic library handle pHandle.
578 static void vfslogDlClose(sqlite3_vfs *pVfs, void *pHandle){
579 REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle);
583 ** Populate the buffer pointed to by zBufOut with nByte bytes of
584 ** random data.
586 static int vfslogRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
587 return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
591 ** Sleep for nMicro microseconds. Return the number of microseconds
592 ** actually slept.
594 static int vfslogSleep(sqlite3_vfs *pVfs, int nMicro){
595 return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro);
599 ** Return the current time as a Julian Day number in *pTimeOut.
601 static int vfslogCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
602 return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut);
605 static int vfslogGetLastError(sqlite3_vfs *pVfs, int a, char *b){
606 return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b);
608 static int vfslogCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
609 return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p);
612 static void vfslog_flush(VfslogVfs *p){
613 #ifdef SQLITE_TEST
614 extern int sqlite3_io_error_pending;
615 extern int sqlite3_io_error_persist;
616 extern int sqlite3_diskfull_pending;
618 int pending = sqlite3_io_error_pending;
619 int persist = sqlite3_io_error_persist;
620 int diskfull = sqlite3_diskfull_pending;
622 sqlite3_io_error_pending = 0;
623 sqlite3_io_error_persist = 0;
624 sqlite3_diskfull_pending = 0;
625 #endif
627 if( p->nBuf ){
628 p->pLog->pMethods->xWrite(p->pLog, p->aBuf, p->nBuf, p->iOffset);
629 p->iOffset += p->nBuf;
630 p->nBuf = 0;
633 #ifdef SQLITE_TEST
634 sqlite3_io_error_pending = pending;
635 sqlite3_io_error_persist = persist;
636 sqlite3_diskfull_pending = diskfull;
637 #endif
640 static void put32bits(unsigned char *p, unsigned int v){
641 p[0] = v>>24;
642 p[1] = v>>16;
643 p[2] = v>>8;
644 p[3] = v;
647 static void vfslog_call(
648 sqlite3_vfs *pVfs,
649 int eEvent,
650 int iFileid,
651 sqlite3_int64 nClick,
652 int return_code,
653 int size,
654 int offset
656 VfslogVfs *p = (VfslogVfs *)pVfs;
657 unsigned char *zRec;
658 if( (24+p->nBuf)>sizeof(p->aBuf) ){
659 vfslog_flush(p);
661 zRec = (unsigned char *)&p->aBuf[p->nBuf];
662 put32bits(&zRec[0], eEvent);
663 put32bits(&zRec[4], iFileid);
664 put32bits(&zRec[8], (unsigned int)(nClick&0xffff));
665 put32bits(&zRec[12], return_code);
666 put32bits(&zRec[16], size);
667 put32bits(&zRec[20], offset);
668 p->nBuf += 24;
671 static void vfslog_string(sqlite3_vfs *pVfs, const char *zStr){
672 VfslogVfs *p = (VfslogVfs *)pVfs;
673 unsigned char *zRec;
674 int nStr = zStr ? (int)strlen(zStr) : 0;
675 if( (4+nStr+p->nBuf)>sizeof(p->aBuf) ){
676 vfslog_flush(p);
678 zRec = (unsigned char *)&p->aBuf[p->nBuf];
679 put32bits(&zRec[0], nStr);
680 if( zStr ){
681 memcpy(&zRec[4], zStr, nStr);
683 p->nBuf += (4 + nStr);
686 static void vfslog_finalize(VfslogVfs *p){
687 if( p->pLog->pMethods ){
688 vfslog_flush(p);
689 p->pLog->pMethods->xClose(p->pLog);
691 sqlite3_free(p);
694 int sqlite3_vfslog_finalize(const char *zVfs){
695 sqlite3_vfs *pVfs;
696 pVfs = sqlite3_vfs_find(zVfs);
697 if( !pVfs || pVfs->xOpen!=vfslogOpen ){
698 return SQLITE_ERROR;
700 sqlite3_vfs_unregister(pVfs);
701 vfslog_finalize((VfslogVfs *)pVfs);
702 return SQLITE_OK;
705 int sqlite3_vfslog_new(
706 const char *zVfs, /* New VFS name */
707 const char *zParentVfs, /* Parent VFS name (or NULL) */
708 const char *zLog /* Log file name */
710 VfslogVfs *p;
711 sqlite3_vfs *pParent;
712 int nByte;
713 int flags;
714 int rc;
715 char *zFile;
716 int nVfs;
718 pParent = sqlite3_vfs_find(zParentVfs);
719 if( !pParent ){
720 return SQLITE_ERROR;
723 nVfs = (int)strlen(zVfs);
724 nByte = sizeof(VfslogVfs) + pParent->szOsFile + nVfs+1+pParent->mxPathname+1;
725 p = (VfslogVfs *)sqlite3_malloc(nByte);
726 memset(p, 0, nByte);
728 p->pVfs = pParent;
729 p->pLog = (sqlite3_file *)&p[1];
730 memcpy(&p->base, &vfslog_vfs, sizeof(sqlite3_vfs));
731 p->base.zName = &((char *)p->pLog)[pParent->szOsFile];
732 p->base.szOsFile += pParent->szOsFile;
733 memcpy((char *)p->base.zName, zVfs, nVfs);
735 zFile = (char *)&p->base.zName[nVfs+1];
736 pParent->xFullPathname(pParent, zLog, pParent->mxPathname, zFile);
738 flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MASTER_JOURNAL;
739 pParent->xDelete(pParent, zFile, 0);
740 rc = pParent->xOpen(pParent, zFile, p->pLog, flags, &flags);
741 if( rc==SQLITE_OK ){
742 memcpy(p->aBuf, "sqlite_ostrace1.....", 20);
743 p->iOffset = 0;
744 p->nBuf = 20;
745 rc = sqlite3_vfs_register((sqlite3_vfs *)p, 1);
747 if( rc ){
748 vfslog_finalize(p);
750 return rc;
753 int sqlite3_vfslog_annotate(const char *zVfs, const char *zMsg){
754 sqlite3_vfs *pVfs;
755 pVfs = sqlite3_vfs_find(zVfs);
756 if( !pVfs || pVfs->xOpen!=vfslogOpen ){
757 return SQLITE_ERROR;
759 vfslog_call(pVfs, OS_ANNOTATE, 0, 0, 0, 0, 0);
760 vfslog_string(pVfs, zMsg);
761 return SQLITE_OK;
764 static const char *vfslog_eventname(int eEvent){
765 const char *zEvent = 0;
767 switch( eEvent ){
768 case OS_CLOSE: zEvent = "xClose"; break;
769 case OS_READ: zEvent = "xRead"; break;
770 case OS_WRITE: zEvent = "xWrite"; break;
771 case OS_TRUNCATE: zEvent = "xTruncate"; break;
772 case OS_SYNC: zEvent = "xSync"; break;
773 case OS_FILESIZE: zEvent = "xFilesize"; break;
774 case OS_LOCK: zEvent = "xLock"; break;
775 case OS_UNLOCK: zEvent = "xUnlock"; break;
776 case OS_CHECKRESERVEDLOCK: zEvent = "xCheckResLock"; break;
777 case OS_FILECONTROL: zEvent = "xFileControl"; break;
778 case OS_SECTORSIZE: zEvent = "xSectorSize"; break;
779 case OS_DEVCHAR: zEvent = "xDeviceChar"; break;
780 case OS_OPEN: zEvent = "xOpen"; break;
781 case OS_DELETE: zEvent = "xDelete"; break;
782 case OS_ACCESS: zEvent = "xAccess"; break;
783 case OS_FULLPATHNAME: zEvent = "xFullPathname"; break;
784 case OS_RANDOMNESS: zEvent = "xRandomness"; break;
785 case OS_SLEEP: zEvent = "xSleep"; break;
786 case OS_CURRENTTIME: zEvent = "xCurrentTime"; break;
788 case OS_SHMUNMAP: zEvent = "xShmUnmap"; break;
789 case OS_SHMLOCK: zEvent = "xShmLock"; break;
790 case OS_SHMBARRIER: zEvent = "xShmBarrier"; break;
791 case OS_SHMMAP: zEvent = "xShmMap"; break;
793 case OS_ANNOTATE: zEvent = "annotation"; break;
796 return zEvent;
799 typedef struct VfslogVtab VfslogVtab;
800 typedef struct VfslogCsr VfslogCsr;
803 ** Virtual table type for the vfslog reader module.
805 struct VfslogVtab {
806 sqlite3_vtab base; /* Base class */
807 sqlite3_file *pFd; /* File descriptor open on vfslog file */
808 sqlite3_int64 nByte; /* Size of file in bytes */
809 char *zFile; /* File name for pFd */
813 ** Virtual table cursor type for the vfslog reader module.
815 struct VfslogCsr {
816 sqlite3_vtab_cursor base; /* Base class */
817 sqlite3_int64 iRowid; /* Current rowid. */
818 sqlite3_int64 iOffset; /* Offset of next record in file */
819 char *zTransient; /* Transient 'file' string */
820 int nFile; /* Size of array azFile[] */
821 char **azFile; /* File strings */
822 unsigned char aBuf[1024]; /* Current vfs log entry (read from file) */
825 static unsigned int get32bits(unsigned char *p){
826 return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3];
830 ** The argument must point to a buffer containing a nul-terminated string.
831 ** If the string begins with an SQL quote character it is overwritten by
832 ** the dequoted version. Otherwise the buffer is left unmodified.
834 static void dequote(char *z){
835 char quote; /* Quote character (if any ) */
836 quote = z[0];
837 if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){
838 int iIn = 1; /* Index of next byte to read from input */
839 int iOut = 0; /* Index of next byte to write to output */
840 if( quote=='[' ) quote = ']';
841 while( z[iIn] ){
842 if( z[iIn]==quote ){
843 if( z[iIn+1]!=quote ) break;
844 z[iOut++] = quote;
845 iIn += 2;
846 }else{
847 z[iOut++] = z[iIn++];
850 z[iOut] = '\0';
854 #ifndef SQLITE_OMIT_VIRTUALTABLE
856 ** Connect to or create a vfslog virtual table.
858 static int vlogConnect(
859 sqlite3 *db,
860 void *pAux,
861 int argc, const char *const*argv,
862 sqlite3_vtab **ppVtab,
863 char **pzErr
865 sqlite3_vfs *pVfs; /* VFS used to read log file */
866 int flags; /* flags passed to pVfs->xOpen() */
867 VfslogVtab *p;
868 int rc;
869 int nByte;
870 char *zFile;
872 *ppVtab = 0;
873 pVfs = sqlite3_vfs_find(0);
874 nByte = sizeof(VfslogVtab) + pVfs->szOsFile + pVfs->mxPathname;
875 p = sqlite3_malloc(nByte);
876 if( p==0 ) return SQLITE_NOMEM;
877 memset(p, 0, nByte);
879 p->pFd = (sqlite3_file *)&p[1];
880 p->zFile = &((char *)p->pFd)[pVfs->szOsFile];
882 zFile = sqlite3_mprintf("%s", argv[3]);
883 if( !zFile ){
884 sqlite3_free(p);
885 return SQLITE_NOMEM;
887 dequote(zFile);
888 pVfs->xFullPathname(pVfs, zFile, pVfs->mxPathname, p->zFile);
889 sqlite3_free(zFile);
891 flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MASTER_JOURNAL;
892 rc = pVfs->xOpen(pVfs, p->zFile, p->pFd, flags, &flags);
894 if( rc==SQLITE_OK ){
895 p->pFd->pMethods->xFileSize(p->pFd, &p->nByte);
896 sqlite3_declare_vtab(db,
897 "CREATE TABLE xxx(event, file, click, rc, size, offset)"
899 *ppVtab = &p->base;
900 }else{
901 sqlite3_free(p);
904 return rc;
908 ** There is no "best-index". This virtual table always does a linear
909 ** scan of the binary VFS log file.
911 static int vlogBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
912 pIdxInfo->estimatedCost = 10.0;
913 return SQLITE_OK;
917 ** Disconnect from or destroy a vfslog virtual table.
919 static int vlogDisconnect(sqlite3_vtab *pVtab){
920 VfslogVtab *p = (VfslogVtab *)pVtab;
921 if( p->pFd->pMethods ){
922 p->pFd->pMethods->xClose(p->pFd);
923 p->pFd->pMethods = 0;
925 sqlite3_free(p);
926 return SQLITE_OK;
930 ** Open a new vfslog cursor.
932 static int vlogOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
933 VfslogCsr *pCsr; /* Newly allocated cursor object */
935 pCsr = sqlite3_malloc(sizeof(VfslogCsr));
936 if( !pCsr ) return SQLITE_NOMEM;
937 memset(pCsr, 0, sizeof(VfslogCsr));
938 *ppCursor = &pCsr->base;
939 return SQLITE_OK;
943 ** Close a vfslog cursor.
945 static int vlogClose(sqlite3_vtab_cursor *pCursor){
946 VfslogCsr *p = (VfslogCsr *)pCursor;
947 int i;
948 for(i=0; i<p->nFile; i++){
949 sqlite3_free(p->azFile[i]);
951 sqlite3_free(p->azFile);
952 sqlite3_free(p->zTransient);
953 sqlite3_free(p);
954 return SQLITE_OK;
958 ** Move a vfslog cursor to the next entry in the file.
960 static int vlogNext(sqlite3_vtab_cursor *pCursor){
961 VfslogCsr *pCsr = (VfslogCsr *)pCursor;
962 VfslogVtab *p = (VfslogVtab *)pCursor->pVtab;
963 int rc = SQLITE_OK;
964 int nRead;
966 sqlite3_free(pCsr->zTransient);
967 pCsr->zTransient = 0;
969 nRead = 24;
970 if( pCsr->iOffset+nRead<=p->nByte ){
971 int eEvent;
972 rc = p->pFd->pMethods->xRead(p->pFd, pCsr->aBuf, nRead, pCsr->iOffset);
974 eEvent = get32bits(pCsr->aBuf);
975 if( (rc==SQLITE_OK)
976 && (eEvent==OS_OPEN || eEvent==OS_DELETE || eEvent==OS_ACCESS)
978 char buf[4];
979 rc = p->pFd->pMethods->xRead(p->pFd, buf, 4, pCsr->iOffset+nRead);
980 nRead += 4;
981 if( rc==SQLITE_OK ){
982 int nStr = get32bits((unsigned char *)buf);
983 char *zStr = sqlite3_malloc(nStr+1);
984 rc = p->pFd->pMethods->xRead(p->pFd, zStr, nStr, pCsr->iOffset+nRead);
985 zStr[nStr] = '\0';
986 nRead += nStr;
988 if( eEvent==OS_OPEN ){
989 int iFileid = get32bits(&pCsr->aBuf[4]);
990 if( iFileid>=pCsr->nFile ){
991 int nNew = sizeof(pCsr->azFile[0])*(iFileid+1);
992 pCsr->azFile = (char **)sqlite3_realloc(pCsr->azFile, nNew);
993 nNew -= sizeof(pCsr->azFile[0])*pCsr->nFile;
994 memset(&pCsr->azFile[pCsr->nFile], 0, nNew);
995 pCsr->nFile = iFileid+1;
997 sqlite3_free(pCsr->azFile[iFileid]);
998 pCsr->azFile[iFileid] = zStr;
999 }else{
1000 pCsr->zTransient = zStr;
1006 pCsr->iRowid += 1;
1007 pCsr->iOffset += nRead;
1008 return rc;
1011 static int vlogEof(sqlite3_vtab_cursor *pCursor){
1012 VfslogCsr *pCsr = (VfslogCsr *)pCursor;
1013 VfslogVtab *p = (VfslogVtab *)pCursor->pVtab;
1014 return (pCsr->iOffset>=p->nByte);
1017 static int vlogFilter(
1018 sqlite3_vtab_cursor *pCursor,
1019 int idxNum, const char *idxStr,
1020 int argc, sqlite3_value **argv
1022 VfslogCsr *pCsr = (VfslogCsr *)pCursor;
1023 pCsr->iRowid = 0;
1024 pCsr->iOffset = 20;
1025 return vlogNext(pCursor);
1028 static int vlogColumn(
1029 sqlite3_vtab_cursor *pCursor,
1030 sqlite3_context *ctx,
1031 int i
1033 unsigned int val;
1034 VfslogCsr *pCsr = (VfslogCsr *)pCursor;
1036 assert( i<7 );
1037 val = get32bits(&pCsr->aBuf[4*i]);
1039 switch( i ){
1040 case 0: {
1041 sqlite3_result_text(ctx, vfslog_eventname(val), -1, SQLITE_STATIC);
1042 break;
1044 case 1: {
1045 char *zStr = pCsr->zTransient;
1046 if( val!=0 && val<(unsigned)pCsr->nFile ){
1047 zStr = pCsr->azFile[val];
1049 sqlite3_result_text(ctx, zStr, -1, SQLITE_TRANSIENT);
1050 break;
1052 default:
1053 sqlite3_result_int(ctx, val);
1054 break;
1057 return SQLITE_OK;
1060 static int vlogRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
1061 VfslogCsr *pCsr = (VfslogCsr *)pCursor;
1062 *pRowid = pCsr->iRowid;
1063 return SQLITE_OK;
1066 int sqlite3_vfslog_register(sqlite3 *db){
1067 static sqlite3_module vfslog_module = {
1068 0, /* iVersion */
1069 vlogConnect, /* xCreate */
1070 vlogConnect, /* xConnect */
1071 vlogBestIndex, /* xBestIndex */
1072 vlogDisconnect, /* xDisconnect */
1073 vlogDisconnect, /* xDestroy */
1074 vlogOpen, /* xOpen - open a cursor */
1075 vlogClose, /* xClose - close a cursor */
1076 vlogFilter, /* xFilter - configure scan constraints */
1077 vlogNext, /* xNext - advance a cursor */
1078 vlogEof, /* xEof - check for end of scan */
1079 vlogColumn, /* xColumn - read data */
1080 vlogRowid, /* xRowid - read data */
1081 0, /* xUpdate */
1082 0, /* xBegin */
1083 0, /* xSync */
1084 0, /* xCommit */
1085 0, /* xRollback */
1086 0, /* xFindMethod */
1087 0, /* xRename */
1090 sqlite3_create_module(db, "vfslog", &vfslog_module, 0);
1091 return SQLITE_OK;
1093 #endif /* SQLITE_OMIT_VIRTUALTABLE */
1095 /**************************************************************************
1096 ***************************************************************************
1097 ** Tcl interface starts here.
1100 #if defined(SQLITE_TEST) || defined(TCLSH)
1102 #include <tcl.h>
1104 static int test_vfslog(
1105 void *clientData,
1106 Tcl_Interp *interp,
1107 int objc,
1108 Tcl_Obj *CONST objv[]
1110 struct SqliteDb { sqlite3 *db; };
1111 sqlite3 *db;
1112 Tcl_CmdInfo cmdInfo;
1113 int rc = SQLITE_ERROR;
1115 static const char *strs[] = { "annotate", "finalize", "new", "register", 0 };
1116 enum VL_enum { VL_ANNOTATE, VL_FINALIZE, VL_NEW, VL_REGISTER };
1117 int iSub;
1119 if( objc<2 ){
1120 Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
1121 return TCL_ERROR;
1123 if( Tcl_GetIndexFromObj(interp, objv[1], strs, "sub-command", 0, &iSub) ){
1124 return TCL_ERROR;
1127 switch( (enum VL_enum)iSub ){
1128 case VL_ANNOTATE: {
1129 int rc;
1130 char *zVfs;
1131 char *zMsg;
1132 if( objc!=4 ){
1133 Tcl_WrongNumArgs(interp, 3, objv, "VFS");
1134 return TCL_ERROR;
1136 zVfs = Tcl_GetString(objv[2]);
1137 zMsg = Tcl_GetString(objv[3]);
1138 rc = sqlite3_vfslog_annotate(zVfs, zMsg);
1139 if( rc!=SQLITE_OK ){
1140 Tcl_AppendResult(interp, "failed", 0);
1141 return TCL_ERROR;
1143 break;
1145 case VL_FINALIZE: {
1146 int rc;
1147 char *zVfs;
1148 if( objc!=3 ){
1149 Tcl_WrongNumArgs(interp, 2, objv, "VFS");
1150 return TCL_ERROR;
1152 zVfs = Tcl_GetString(objv[2]);
1153 rc = sqlite3_vfslog_finalize(zVfs);
1154 if( rc!=SQLITE_OK ){
1155 Tcl_AppendResult(interp, "failed", 0);
1156 return TCL_ERROR;
1158 break;
1161 case VL_NEW: {
1162 int rc;
1163 char *zVfs;
1164 char *zParent;
1165 char *zLog;
1166 if( objc!=5 ){
1167 Tcl_WrongNumArgs(interp, 2, objv, "VFS PARENT LOGFILE");
1168 return TCL_ERROR;
1170 zVfs = Tcl_GetString(objv[2]);
1171 zParent = Tcl_GetString(objv[3]);
1172 zLog = Tcl_GetString(objv[4]);
1173 if( *zParent=='\0' ) zParent = 0;
1174 rc = sqlite3_vfslog_new(zVfs, zParent, zLog);
1175 if( rc!=SQLITE_OK ){
1176 Tcl_AppendResult(interp, "failed", 0);
1177 return TCL_ERROR;
1179 break;
1182 case VL_REGISTER: {
1183 char *zDb;
1184 if( objc!=3 ){
1185 Tcl_WrongNumArgs(interp, 2, objv, "DB");
1186 return TCL_ERROR;
1188 #ifdef SQLITE_OMIT_VIRTUALTABLE
1189 Tcl_AppendResult(interp, "vfslog not available because of "
1190 "SQLITE_OMIT_VIRTUALTABLE", (void*)0);
1191 return TCL_ERROR;
1192 #else
1193 zDb = Tcl_GetString(objv[2]);
1194 if( Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){
1195 db = ((struct SqliteDb*)cmdInfo.objClientData)->db;
1196 rc = sqlite3_vfslog_register(db);
1198 if( rc!=SQLITE_OK ){
1199 Tcl_AppendResult(interp, "bad sqlite3 handle: ", zDb, (void*)0);
1200 return TCL_ERROR;
1202 break;
1203 #endif
1207 return TCL_OK;
1210 int SqlitetestOsinst_Init(Tcl_Interp *interp){
1211 Tcl_CreateObjCommand(interp, "vfslog", test_vfslog, 0, 0);
1212 return TCL_OK;
1215 #endif /* SQLITE_TEST */