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