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 ** 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. 23 ** 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. 29 ** 30 ** The binary log files are read using a virtual table implementation 31 ** also contained in this file. 32 ** 33 ** CREATING LOG FILES: 34 ** 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 ** ); 40 ** 41 ** int sqlite3_vfslog_finalize(const char *zVfs); 42 ** 43 ** ANNOTATING LOG FILES: 44 ** 45 ** To write an arbitrary message into a log file: 46 ** 47 ** int sqlite3_vfslog_annotate(const char *zVfs, const char *zMsg); 48 ** 49 ** READING LOG FILES: 50 ** 51 ** Log files are read using the "vfslog" virtual table implementation 52 ** in this file. To register the virtual table with SQLite, use: 53 ** 54 ** int sqlite3_vfslog_register(sqlite3 *db); 55 ** 56 ** Then, if the log file is named "vfs.log", the following SQL command: 57 ** 58 ** CREATE VIRTUAL TABLE v USING vfslog('vfs.log'); 59 ** 60 ** creates a virtual table with 6 columns, as follows: 61 ** 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 ** ); 70 */ 71 72 #include "sqlite3.h" 73 #include <string.h> 74 #include <assert.h> 75 76 77 /* 78 ** Maximum pathname length supported by the vfslog backend. 79 */ 80 #define INST_MAX_PATHNAME 512 81 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 106 107 #define OS_NUMEVENTS 29 108 109 #define VFSLOG_BUFFERSIZE 8192 110 111 typedef struct VfslogVfs VfslogVfs; 112 typedef struct VfslogFile VfslogFile; 113 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 */ 122 }; 123 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 */ 129 }; 130 131 #define REALVFS(p) (((VfslogVfs *)(p))->pVfs) 132 133 134 135 /* 136 ** Method declarations for vfslog_file. 137 */ 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*); 150 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); 155 156 /* 157 ** Method declarations for vfslog_vfs. 158 */ 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*); 170 171 static int vfslogGetLastError(sqlite3_vfs*, int, char *); 172 static int vfslogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); 173 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 */ 194 }; 195 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 */ 214 }; 215 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; 222 } 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; 229 230 GetSystemTimeAsFileTime(&ft); 231 232 u64time |= ft.dwHighDateTime; 233 u64time <<= 32; 234 u64time |= ft.dwLowDateTime; 235 236 /* ft is 100-nanosecond intervals, we want microseconds */ 237 return u64time /(sqlite3_uint64)10; 238 } 239 #else 240 static sqlite3_uint64 vfslog_time(){ 241 return 0; 242 } 243 #endif 244 245 static void vfslog_call(sqlite3_vfs *, int, int, int, int, int, int); 246 static void vfslog_string(sqlite3_vfs *, const char *); 247 248 /* 249 ** Close an vfslog-file. 250 */ 251 static int vfslogClose(sqlite3_file *pFile){ 252 sqlite3_uint64 t; 253 int rc = SQLITE_OK; 254 VfslogFile *p = (VfslogFile *)pFile; 255 256 t = vfslog_time(); 257 if( p->pReal->pMethods ){ 258 rc = p->pReal->pMethods->xClose(p->pReal); 259 } 260 t = vfslog_time() - t; 261 vfslog_call(p->pVfslog, OS_CLOSE, p->iFileId, t, rc, 0, 0); 262 return rc; 263 } 264 265 /* 266 ** Read data from an vfslog-file. 267 */ 268 static int vfslogRead( 269 sqlite3_file *pFile, 270 void *zBuf, 271 int iAmt, 272 sqlite_int64 iOfst 273 ){ 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; 282 } 283 284 /* 285 ** Write data to an vfslog-file. 286 */ 287 static int vfslogWrite( 288 sqlite3_file *pFile, 289 const void *z, 290 int iAmt, 291 sqlite_int64 iOfst 292 ){ 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; 301 } 302 303 /* 304 ** Truncate an vfslog-file. 305 */ 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; 315 } 316 317 /* 318 ** Sync an vfslog-file. 319 */ 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; 329 } 330 331 /* 332 ** Return the current file-size of an vfslog-file. 333 */ 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; 343 } 344 345 /* 346 ** Lock an vfslog-file. 347 */ 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; 357 } 358 359 /* 360 ** Unlock an vfslog-file. 361 */ 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; 371 } 372 373 /* 374 ** Check if another file-handle holds a RESERVED lock on an vfslog-file. 375 */ 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; 385 } 386 387 /* 388 ** File control method. For custom operations on an vfslog-file. 389 */ 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); 395 } 396 return rc; 397 } 398 399 /* 400 ** Return the sector-size in bytes for an vfslog-file. 401 */ 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; 411 } 412 413 /* 414 ** Return the device characteristic flags supported by an vfslog-file. 415 */ 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; 425 } 426 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; 436 } 437 static int vfslogShmMap( 438 sqlite3_file *pFile, 439 int iRegion, 440 int szRegion, 441 int isWrite, 442 volatile void **pp 443 ){ 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; 452 } 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); 460 } 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; 470 } 471 472 473 /* 474 ** Open an vfslog file handle. 475 */ 476 static int vfslogOpen( 477 sqlite3_vfs *pVfs, 478 const char *zName, 479 sqlite3_file *pFile, 480 int flags, 481 int *pOutFlags 482 ){ 483 int rc; 484 sqlite3_uint64 t; 485 VfslogFile *p = (VfslogFile *)pFile; 486 VfslogVfs *pLog = (VfslogVfs *)pVfs; 487 488 pFile->pMethods = &vfslog_io_methods; 489 p->pReal = (sqlite3_file *)&p[1]; 490 p->pVfslog = pVfs; 491 p->iFileId = ++pLog->iNextFileId; 492 493 t = vfslog_time(); 494 rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags); 495 t = vfslog_time() - t; 496 497 vfslog_call(pVfs, OS_OPEN, p->iFileId, t, rc, 0, 0); 498 vfslog_string(pVfs, zName); 499 return rc; 500 } 501 502 /* 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. 506 */ 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; 516 } 517 518 /* 519 ** Test for access permissions. Return true if the requested permission 520 ** is available, or false otherwise. 521 */ 522 static int vfslogAccess( 523 sqlite3_vfs *pVfs, 524 const char *zPath, 525 int flags, 526 int *pResOut 527 ){ 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; 536 } 537 538 /* 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. 542 */ 543 static int vfslogFullPathname( 544 sqlite3_vfs *pVfs, 545 const char *zPath, 546 int nOut, 547 char *zOut 548 ){ 549 return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut); 550 } 551 552 /* 553 ** Open the dynamic library located at zPath and return a handle. 554 */ 555 static void *vfslogDlOpen(sqlite3_vfs *pVfs, const char *zPath){ 556 return REALVFS(pVfs)->xDlOpen(REALVFS(pVfs), zPath); 557 } 558 559 /* 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. 563 */ 564 static void vfslogDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ 565 REALVFS(pVfs)->xDlError(REALVFS(pVfs), nByte, zErrMsg); 566 } 567 568 /* 569 ** Return a pointer to the symbol zSymbol in the dynamic library pHandle. 570 */ 571 static void (*vfslogDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ 572 return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym); 573 } 574 575 /* 576 ** Close the dynamic library handle pHandle. 577 */ 578 static void vfslogDlClose(sqlite3_vfs *pVfs, void *pHandle){ 579 REALVFS(pVfs)->xDlClose(REALVFS(pVfs), pHandle); 580 } 581 582 /* 583 ** Populate the buffer pointed to by zBufOut with nByte bytes of 584 ** random data. 585 */ 586 static int vfslogRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ 587 return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut); 588 } 589 590 /* 591 ** Sleep for nMicro microseconds. Return the number of microseconds 592 ** actually slept. 593 */ 594 static int vfslogSleep(sqlite3_vfs *pVfs, int nMicro){ 595 return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro); 596 } 597 598 /* 599 ** Return the current time as a Julian Day number in *pTimeOut. 600 */ 601 static int vfslogCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ 602 return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut); 603 } 604 605 static int vfslogGetLastError(sqlite3_vfs *pVfs, int a, char *b){ 606 return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b); 607 } 608 static int vfslogCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){ 609 return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p); 610 } 611 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; 617 618 int pending = sqlite3_io_error_pending; 619 int persist = sqlite3_io_error_persist; 620 int diskfull = sqlite3_diskfull_pending; 621 622 sqlite3_io_error_pending = 0; 623 sqlite3_io_error_persist = 0; 624 sqlite3_diskfull_pending = 0; 625 #endif 626 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; 631 } 632 633 #ifdef SQLITE_TEST 634 sqlite3_io_error_pending = pending; 635 sqlite3_io_error_persist = persist; 636 sqlite3_diskfull_pending = diskfull; 637 #endif 638 } 639 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; 645 } 646 647 static void vfslog_call( 648 sqlite3_vfs *pVfs, 649 int eEvent, 650 int iFileid, 651 int nClick, 652 int return_code, 653 int size, 654 int offset 655 ){ 656 VfslogVfs *p = (VfslogVfs *)pVfs; 657 unsigned char *zRec; 658 if( (24+p->nBuf)>sizeof(p->aBuf) ){ 659 vfslog_flush(p); 660 } 661 zRec = (unsigned char *)&p->aBuf[p->nBuf]; 662 put32bits(&zRec[0], eEvent); 663 put32bits(&zRec[4], iFileid); 664 put32bits(&zRec[8], nClick); 665 put32bits(&zRec[12], return_code); 666 put32bits(&zRec[16], size); 667 put32bits(&zRec[20], offset); 668 p->nBuf += 24; 669 } 670 671 static void vfslog_string(sqlite3_vfs *pVfs, const char *zStr){ 672 VfslogVfs *p = (VfslogVfs *)pVfs; 673 unsigned char *zRec; 674 int nStr = zStr ? strlen(zStr) : 0; 675 if( (4+nStr+p->nBuf)>sizeof(p->aBuf) ){ 676 vfslog_flush(p); 677 } 678 zRec = (unsigned char *)&p->aBuf[p->nBuf]; 679 put32bits(&zRec[0], nStr); 680 if( zStr ){ 681 memcpy(&zRec[4], zStr, nStr); 682 } 683 p->nBuf += (4 + nStr); 684 } 685 686 static void vfslog_finalize(VfslogVfs *p){ 687 if( p->pLog->pMethods ){ 688 vfslog_flush(p); 689 p->pLog->pMethods->xClose(p->pLog); 690 } 691 sqlite3_free(p); 692 } 693 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; 699 } 700 sqlite3_vfs_unregister(pVfs); 701 vfslog_finalize((VfslogVfs *)pVfs); 702 return SQLITE_OK; 703 } 704 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 */ 709 ){ 710 VfslogVfs *p; 711 sqlite3_vfs *pParent; 712 int nByte; 713 int flags; 714 int rc; 715 char *zFile; 716 int nVfs; 717 718 pParent = sqlite3_vfs_find(zParentVfs); 719 if( !pParent ){ 720 return SQLITE_ERROR; 721 } 722 723 nVfs = strlen(zVfs); 724 nByte = sizeof(VfslogVfs) + pParent->szOsFile + nVfs+1+pParent->mxPathname+1; 725 p = (VfslogVfs *)sqlite3_malloc(nByte); 726 memset(p, 0, nByte); 727 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); 734 735 zFile = (char *)&p->base.zName[nVfs+1]; 736 pParent->xFullPathname(pParent, zLog, pParent->mxPathname, zFile); 737 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); 746 } 747 if( rc ){ 748 vfslog_finalize(p); 749 } 750 return rc; 751 } 752 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; 758 } 759 vfslog_call(pVfs, OS_ANNOTATE, 0, 0, 0, 0, 0); 760 vfslog_string(pVfs, zMsg); 761 return SQLITE_OK; 762 } 763 764 static const char *vfslog_eventname(int eEvent){ 765 const char *zEvent = 0; 766 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; 787 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; 792 793 case OS_ANNOTATE: zEvent = "annotation"; break; 794 } 795 796 return zEvent; 797 } 798 799 typedef struct VfslogVtab VfslogVtab; 800 typedef struct VfslogCsr VfslogCsr; 801 802 /* 803 ** Virtual table type for the vfslog reader module. 804 */ 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 */ 810 }; 811 812 /* 813 ** Virtual table cursor type for the vfslog reader module. 814 */ 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) */ 823 }; 824 825 static unsigned int get32bits(unsigned char *p){ 826 return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; 827 } 828 829 /* 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. 833 */ 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++]; 848 } 849 } 850 z[iOut] = '\0'; 851 } 852 } 853 854 #ifndef SQLITE_OMIT_VIRTUALTABLE 855 /* 856 ** Connect to or create a vfslog virtual table. 857 */ 858 static int vlogConnect( 859 sqlite3 *db, 860 void *pAux, 861 int argc, const char *const*argv, 862 sqlite3_vtab **ppVtab, 863 char **pzErr 864 ){ 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; 871 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); 878 879 p->pFd = (sqlite3_file *)&p[1]; 880 p->zFile = &((char *)p->pFd)[pVfs->szOsFile]; 881 882 zFile = sqlite3_mprintf("%s", argv[3]); 883 if( !zFile ){ 884 sqlite3_free(p); 885 return SQLITE_NOMEM; 886 } 887 dequote(zFile); 888 pVfs->xFullPathname(pVfs, zFile, pVfs->mxPathname, p->zFile); 889 sqlite3_free(zFile); 890 891 flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MASTER_JOURNAL; 892 rc = pVfs->xOpen(pVfs, p->zFile, p->pFd, flags, &flags); 893 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)" 898 ); 899 *ppVtab = &p->base; 900 }else{ 901 sqlite3_free(p); 902 } 903 904 return rc; 905 } 906 907 /* 908 ** There is no "best-index". This virtual table always does a linear 909 ** scan of the binary VFS log file. 910 */ 911 static int vlogBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ 912 pIdxInfo->estimatedCost = 10.0; 913 return SQLITE_OK; 914 } 915 916 /* 917 ** Disconnect from or destroy a vfslog virtual table. 918 */ 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; 924 } 925 sqlite3_free(p); 926 return SQLITE_OK; 927 } 928 929 /* 930 ** Open a new vfslog cursor. 931 */ 932 static int vlogOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ 933 VfslogCsr *pCsr; /* Newly allocated cursor object */ 934 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; 940 } 941 942 /* 943 ** Close a vfslog cursor. 944 */ 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]); 950 } 951 sqlite3_free(p->azFile); 952 sqlite3_free(p->zTransient); 953 sqlite3_free(p); 954 return SQLITE_OK; 955 } 956 957 /* 958 ** Move a vfslog cursor to the next entry in the file. 959 */ 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; 965 966 sqlite3_free(pCsr->zTransient); 967 pCsr->zTransient = 0; 968 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); 973 974 eEvent = get32bits(pCsr->aBuf); 975 if( (rc==SQLITE_OK) 976 && (eEvent==OS_OPEN || eEvent==OS_DELETE || eEvent==OS_ACCESS) 977 ){ 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; 987 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; 996 } 997 sqlite3_free(pCsr->azFile[iFileid]); 998 pCsr->azFile[iFileid] = zStr; 999 }else{ 1000 pCsr->zTransient = zStr; 1001 } 1002 } 1003 } 1004 } 1005 1006 pCsr->iRowid += 1; 1007 pCsr->iOffset += nRead; 1008 return rc; 1009 } 1010 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); 1015 } 1016 1017 static int vlogFilter( 1018 sqlite3_vtab_cursor *pCursor, 1019 int idxNum, const char *idxStr, 1020 int argc, sqlite3_value **argv 1021 ){ 1022 VfslogCsr *pCsr = (VfslogCsr *)pCursor; 1023 pCsr->iRowid = 0; 1024 pCsr->iOffset = 20; 1025 return vlogNext(pCursor); 1026 } 1027 1028 static int vlogColumn( 1029 sqlite3_vtab_cursor *pCursor, 1030 sqlite3_context *ctx, 1031 int i 1032 ){ 1033 unsigned int val; 1034 VfslogCsr *pCsr = (VfslogCsr *)pCursor; 1035 1036 assert( i<7 ); 1037 val = get32bits(&pCsr->aBuf[4*i]); 1038 1039 switch( i ){ 1040 case 0: { 1041 sqlite3_result_text(ctx, vfslog_eventname(val), -1, SQLITE_STATIC); 1042 break; 1043 } 1044 case 1: { 1045 char *zStr = pCsr->zTransient; 1046 if( val!=0 && val<pCsr->nFile ){ 1047 zStr = pCsr->azFile[val]; 1048 } 1049 sqlite3_result_text(ctx, zStr, -1, SQLITE_TRANSIENT); 1050 break; 1051 } 1052 default: 1053 sqlite3_result_int(ctx, val); 1054 break; 1055 } 1056 1057 return SQLITE_OK; 1058 } 1059 1060 static int vlogRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ 1061 VfslogCsr *pCsr = (VfslogCsr *)pCursor; 1062 *pRowid = pCsr->iRowid; 1063 return SQLITE_OK; 1064 } 1065 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 */ 1088 }; 1089 1090 sqlite3_create_module(db, "vfslog", &vfslog_module, 0); 1091 return SQLITE_OK; 1092 } 1093 #endif /* SQLITE_OMIT_VIRTUALTABLE */ 1094 1095 /************************************************************************** 1096 *************************************************************************** 1097 ** Tcl interface starts here. 1098 */ 1099 1100 #if defined(SQLITE_TEST) || defined(TCLSH) 1101 1102 #include <tcl.h> 1103 1104 static int test_vfslog( 1105 void *clientData, 1106 Tcl_Interp *interp, 1107 int objc, 1108 Tcl_Obj *CONST objv[] 1109 ){ 1110 struct SqliteDb { sqlite3 *db; }; 1111 sqlite3 *db; 1112 Tcl_CmdInfo cmdInfo; 1113 int rc = SQLITE_ERROR; 1114 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; 1118 1119 if( objc<2 ){ 1120 Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ..."); 1121 return TCL_ERROR; 1122 } 1123 if( Tcl_GetIndexFromObj(interp, objv[1], strs, "sub-command", 0, &iSub) ){ 1124 return TCL_ERROR; 1125 } 1126 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; 1135 } 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; 1142 } 1143 break; 1144 } 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; 1151 } 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; 1157 } 1158 break; 1159 }; 1160 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; 1169 } 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; 1178 } 1179 break; 1180 }; 1181 1182 case VL_REGISTER: { 1183 char *zDb; 1184 if( objc!=3 ){ 1185 Tcl_WrongNumArgs(interp, 2, objv, "DB"); 1186 return TCL_ERROR; 1187 } 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); 1197 } 1198 if( rc!=SQLITE_OK ){ 1199 Tcl_AppendResult(interp, "bad sqlite3 handle: ", zDb, (void*)0); 1200 return TCL_ERROR; 1201 } 1202 break; 1203 #endif 1204 } 1205 } 1206 1207 return TCL_OK; 1208 } 1209 1210 int SqlitetestOsinst_Init(Tcl_Interp *interp){ 1211 Tcl_CreateObjCommand(interp, "vfslog", test_vfslog, 0, 0); 1212 return TCL_OK; 1213 } 1214 1215 #endif /* SQLITE_TEST */ 1216