diff --git a/src/sqlite3.c b/src/sqlite3.c index 2b77ee854c623aa6b3e6bf14a0a53b56ee52082b..58ae20bc88413817e144b38e4b78158166740a51 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c @@ -874,6 +874,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) +#define SQLITE_WARNING_DUMP (SQLITE_WARNING | (2<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) #define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */ @@ -17004,6 +17005,17 @@ SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**); typedef void (*sqlite3_xDropTableHandle)(sqlite3*, const char*, const char*); +#if defined(SQLITE_HAS_CODEC) && defined(SQLITE_CODEC_ATTACH_CHANGED) +typedef struct CodecParameter { + int kdfIter; + int pageSize; + u8 cipher; + u8 hmacAlgo; + u8 kdfAlgo; + u8 reserved; +} CodecParameter; +#endif + /* ** Each database connection is an instance of the following structure. */ @@ -17153,6 +17165,9 @@ struct sqlite3 { char *mDropSchemaName; sqlite3_xDropTableHandle xDropTableHandle; /* User drop table callback */ #endif +#if defined(SQLITE_HAS_CODEC) && defined(SQLITE_CODEC_ATTACH_CHANGED) + CodecParameter codecParm; +#endif }; /* @@ -20128,6 +20143,9 @@ SQLITE_PRIVATE int sqlite3CodecQueryParameters(sqlite3*,const char*,const char #else # define sqlite3CodecQueryParameters(A,B,C) 0 #endif +#if defined(SQLITE_HAS_CODEC) && defined(SQLITE_CODEC_ATTACH_CHANGED) +SQLITE_PRIVATE void sqlite3CodecResetParameters(CodecParameter *p); +#endif SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3*,const char*); #ifdef SQLITE_UNTESTABLE @@ -36767,6 +36785,44 @@ SQLITE_PRIVATE int sqlite3KvvfsInit(void){ #endif /************** End of os_kv.c ***********************************************/ +/************** Define dump function *****************************************/ +#if SQLITE_OS_UNIX +#define DB_LOCK_NUM 3 +#define WAL_LOCK_NUM 9 +// 8 wal lock, 1 SHM_DMS lock, 1 TRX lock +#define MAX_LOCK_NUM (WAL_LOCK_NUM + 1) +#define SHM_DMS_IDX (WAL_LOCK_NUM - 1) +#define TRX_LOCK_IDX WAL_LOCK_NUM +#define LOCK_BY_PROCESS 1 +#define LOCK_BY_THREAD 0 +#define DUMP_BUF_MAX_LEN 180 +#define DUMP_MAX_STR_LEN 21 + +typedef struct LocalLockStatus { + u8 busyLockIdx; + u8 busyLockType; + u8 lockByProcess; + u8 reserved; + u32 lockLen; + u32 busyLine; + u8 lockStatus[MAX_LOCK_NUM]; // last index is trx lock +} LocalLockStatus; +__thread LocalLockStatus g_lockStatus = {0}; + +#define MARK_LAST_BUSY_LINE(rc) {if ((rc)==SQLITE_BUSY || (rc) == SQLITE_LOCKED) g_lockStatus.busyLine = __LINE__;} +static void ResetLockStatus(void); +static void TryRecordTid(int *tidBuf, int ofs, int lockLen); +static void TryClearTid(int *tidBuf, int ofs, int lockLen); +static void MarkLockBusy(u32 lockIdx, u32 lockLen, u8 lockType, u8 lockByProcess); +static void MarkLockStatus(u32 lockIdx, u32 lockLen, u8 lockType); +static void MarkLockStatusByRc(int rc, u32 lockIdx, u32 lockLen, u8 lockType, u8 lockByProcess); +#else +#define MARK_LAST_BUSY_LINE(rc) +#define ResetLockStatus(void) +#define MarkLockBusy(A, B, C, D) +#define MarkLockStatusByRc(A, B, C, D, E) +#endif +/************** End define dump function *************************************/ /************** Begin file os_unix.c *****************************************/ /* ** 2004 May 22 @@ -38541,6 +38597,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ if( pFile->eFileLock>=eFileLock ){ OSTRACE(("LOCK %d %s ok (already held) (unix)\n", pFile->h, azFileLock(eFileLock))); + MarkLockStatus(TRX_LOCK_IDX, 1, eFileLock); return SQLITE_OK; } @@ -38565,6 +38622,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK)) ){ rc = SQLITE_BUSY; + MarkLockBusy(TRX_LOCK_IDX, 1, eFileLock, LOCK_BY_THREAD); goto end_lock; } @@ -38580,6 +38638,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ pFile->eFileLock = SHARED_LOCK; pInode->nShared++; pInode->nLock++; + MarkLockStatus(TRX_LOCK_IDX, 1, eFileLock); goto end_lock; } @@ -38601,11 +38660,11 @@ static int unixLock(sqlite3_file *id, int eFileLock){ if( rc!=SQLITE_BUSY ){ storeLastErrno(pFile, tErrno); } + MarkLockStatusByRc(rc, TRX_LOCK_IDX, 1, SHARED_LOCK, LOCK_BY_PROCESS); goto end_lock; } } - /* If control gets to this point, then actually go ahead and make ** operating system calls for the specified lock. */ @@ -38621,7 +38680,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); } - + MarkLockStatusByRc(rc, TRX_LOCK_IDX, 1, SHARED_LOCK, LOCK_BY_PROCESS); /* Drop the temporary PENDING lock */ lock.l_start = PENDING_BYTE; lock.l_len = 1L; @@ -38646,6 +38705,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ /* We are trying for an exclusive lock but another thread in this ** same process is still holding a shared lock. */ rc = SQLITE_BUSY; + MarkLockBusy(TRX_LOCK_IDX, 1, eFileLock, LOCK_BY_THREAD); }else{ /* The request was for a RESERVED or EXCLUSIVE lock. It is ** assumed that there is a SHARED or greater lock on the file @@ -38670,6 +38730,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ storeLastErrno(pFile, tErrno); } } + MarkLockStatusByRc(rc, TRX_LOCK_IDX, 1, eFileLock, LOCK_BY_PROCESS); } @@ -38867,7 +38928,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ pFile->eFileLock = NO_LOCK; } } - /* Decrement the count of locks against this same file. When the ** count reaches zero, close any other file descriptors whose close ** was deferred because of outstanding locks. @@ -41179,6 +41239,7 @@ struct unixShmNode { int nRef; /* Number of unixShm objects pointing to this */ unixShm *pFirst; /* All unixShm objects pointing to this */ int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */ + int aLockTid[SQLITE_SHM_NLOCK]; #ifdef SQLITE_DEBUG u8 exclMask; /* Mask of exclusive locks held */ u8 sharedMask; /* Mask of shared locks held */ @@ -41421,6 +41482,8 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ rc = SQLITE_READONLY_CANTINIT; }else{ rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1); + MarkLockStatusByRc(rc, SHM_DMS_IDX, 1, EXCLUSIVE_LOCK, LOCK_BY_PROCESS); + MARK_LAST_BUSY_LINE(rc); /* The first connection to attach must truncate the -shm file. We ** truncate to 3 bytes (an arbitrary small number, less than the ** -shm header size) rather than 0 as a system debugging aid, to @@ -41432,11 +41495,15 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ } }else if( lock.l_type==F_WRLCK ){ rc = SQLITE_BUSY; + MarkLockStatusByRc(rc, SHM_DMS_IDX, 1, SHARED_LOCK, LOCK_BY_PROCESS); + MARK_LAST_BUSY_LINE(rc); } if( rc==SQLITE_OK ){ assert( lock.l_type==F_UNLCK || lock.l_type==F_RDLCK ); rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1); + MarkLockStatusByRc(rc, SHM_DMS_IDX, 1, SHARED_LOCK, LOCK_BY_PROCESS); + MARK_LAST_BUSY_LINE(rc); } return rc; } @@ -41821,7 +41888,7 @@ static int unixShmLock( return SQLITE_IOERR_SHMLOCK; } aLock = pShmNode->aLock; - + int *aLockTid = pShmNode->aLockTid; assert( pShmNode==pDbFd->pInode->pShmNode ); assert( pShmNode->pInode==pDbFd->pInode ); assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); @@ -41860,6 +41927,7 @@ static int unixShmLock( assert( n>1 || mask==(1<pShmMutex); assert( assertLockingArrayOk(pShmNode) ); + u8 useProcessLock = LOCK_BY_THREAD; if( flags & SQLITE_SHM_UNLOCK ){ if( (p->exclMask|p->sharedMask) & mask ){ int ii; @@ -41870,21 +41938,22 @@ static int unixShmLock( bUnlock = 0; } } - if( bUnlock ){ rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); if( rc==SQLITE_OK ){ memset(&aLock[ofst], 0, sizeof(int)*n); } + useProcessLock = LOCK_BY_PROCESS; }else if( ALWAYS(p->sharedMask & (1<1 ); aLock[ofst]--; } - + MarkLockStatusByRc(rc, ofst, n, NO_LOCK, useProcessLock); /* Undo the local locks */ if( rc==SQLITE_OK ){ p->exclMask &= ~mask; p->sharedMask &= ~mask; + TryClearTid(aLockTid, ofst, n); } } }else if( flags & SQLITE_SHM_SHARED ){ @@ -41895,12 +41964,14 @@ static int unixShmLock( rc = SQLITE_BUSY; }else if( aLock[ofst]==0 ){ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); + useProcessLock = LOCK_BY_PROCESS; } - + MarkLockStatusByRc(rc, ofst, n, SHARED_LOCK, useProcessLock); /* Get the local shared locks */ if( rc==SQLITE_OK ){ p->sharedMask |= mask; aLock[ofst]++; + TryRecordTid(aLockTid, ofst, n); } } }else{ @@ -41919,14 +41990,17 @@ static int unixShmLock( ** also update the in-memory values. */ if( rc==SQLITE_OK ){ rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n); + useProcessLock = LOCK_BY_PROCESS; if( rc==SQLITE_OK ){ assert( (p->sharedMask & mask)==0 ); p->exclMask |= mask; for(ii=ofst; iipShmMutex); @@ -59794,6 +59868,7 @@ static int syncJournal(Pager *pPager, int newHdr){ assert( !pagerUseWal(pPager) ); rc = sqlite3PagerExclusiveLock(pPager); + MARK_LAST_BUSY_LINE(rc); if( rc!=SQLITE_OK ) return rc; if( !pPager->noSync ){ @@ -60703,6 +60778,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){ sqlite3OsDelete(pVfs, pPager->zJournal, 0); if( !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK); } + MARK_LAST_BUSY_LINE(rc); sqlite3EndBenignMalloc(); }else{ /* The journal file exists and no other connection has a reserved @@ -60792,6 +60868,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){ assert( pPager->tempFile==0 || pPager->eLock==EXCLUSIVE_LOCK ); rc = pager_wait_on_lock(pPager, SHARED_LOCK); + MARK_LAST_BUSY_LINE(rc); if( rc!=SQLITE_OK ){ assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK ); goto failed; @@ -60828,6 +60905,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){ ** downgraded to SHARED_LOCK before this function returns. */ rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); + MARK_LAST_BUSY_LINE(rc); if( rc!=SQLITE_OK ){ goto failed; } @@ -61442,6 +61520,7 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory */ if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){ rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); + MARK_LAST_BUSY_LINE(rc); if( rc!=SQLITE_OK ){ return rc; } @@ -61454,6 +61533,7 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory ** holds the write-lock. If possible, the upper layer will call it. */ rc = sqlite3WalBeginWriteTransaction(pPager->pWal); + MARK_LAST_BUSY_LINE(rc); }else{ /* Obtain a RESERVED lock on the database file. If the exFlag parameter ** is true, then immediately upgrade this to an EXCLUSIVE lock. The @@ -61461,8 +61541,10 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory ** lock, but not when obtaining the RESERVED lock. */ rc = pagerLockDb(pPager, RESERVED_LOCK); + MARK_LAST_BUSY_LINE(rc); if( rc==SQLITE_OK && exFlag ){ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK); + MARK_LAST_BUSY_LINE(rc); } } @@ -62963,6 +63045,7 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ if( pPager->eState==PAGER_READER ){ assert( rc==SQLITE_OK ); rc = pagerLockDb(pPager, RESERVED_LOCK); + MARK_LAST_BUSY_LINE(rc); } if( rc==SQLITE_OK ){ sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); @@ -63127,6 +63210,7 @@ static int pagerOpenWal(Pager *pPager){ */ if( pPager->exclusiveMode ){ rc = pagerExclusiveLock(pPager); + MARK_LAST_BUSY_LINE(rc); } /* Open the connection to the log file. If this operation fails, @@ -63210,6 +63294,7 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){ if( !pPager->pWal ){ int logexists = 0; rc = pagerLockDb(pPager, SHARED_LOCK); + MARK_LAST_BUSY_LINE(rc); if( rc==SQLITE_OK ){ rc = sqlite3OsAccess( pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists @@ -63225,6 +63310,7 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){ */ if( rc==SQLITE_OK && pPager->pWal ){ rc = pagerExclusiveLock(pPager); + MARK_LAST_BUSY_LINE(rc); if( rc==SQLITE_OK ){ rc = sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize, (u8*)pPager->pTmpSpace); @@ -64599,6 +64685,7 @@ static int walIndexRecover(Wal *pWal){ iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; rc = walLockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); if( rc ){ + MARK_LAST_BUSY_LINE(rc); return rc; } @@ -65436,6 +65523,7 @@ static int walCheckpoint( if( mxSafeFrame>y ){ assert( y<=pWal->hdr.mxFrame ); rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); + MARK_LAST_BUSY_LINE(rc); if( rc==SQLITE_OK ){ u32 iMark = (i==1 ? mxSafeFrame : READMARK_NOT_USED); AtomicStore(pInfo->aReadMark+i, iMark); @@ -65528,6 +65616,8 @@ static int walCheckpoint( /* Release the reader lock held while backfilling */ walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); + } else { + MARK_LAST_BUSY_LINE(rc); } if( rc==SQLITE_BUSY ){ @@ -65546,11 +65636,13 @@ static int walCheckpoint( assert( pWal->writeLock ); if( pInfo->nBackfillhdr.mxFrame ){ rc = SQLITE_BUSY; + MARK_LAST_BUSY_LINE(rc); }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){ u32 salt1; sqlite3_randomness(4, &salt1); assert( pInfo->nBackfill==pWal->hdr.mxFrame ); rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); + MARK_LAST_BUSY_LINE(rc); if( rc==SQLITE_OK ){ if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){ /* IMPLEMENTATION-OF: R-44699-57140 This mode works the same way as @@ -65648,8 +65740,9 @@ SQLITE_PRIVATE int sqlite3WalClose( walLimitSize(pWal, 0); } } + } else { + MARK_LAST_BUSY_LINE(rc); } - walIndexClose(pWal, isDelete); sqlite3OsClose(pWal->pWalFd); if( isDelete ){ @@ -65802,6 +65895,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ walUnlockShared(pWal, WAL_WRITE_LOCK); rc = SQLITE_READONLY_RECOVERY; } + MARK_LAST_BUSY_LINE(rc); }else{ int bWriteLock = pWal->writeLock; if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){ @@ -65822,6 +65916,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); } } + MARK_LAST_BUSY_LINE(rc); } } @@ -65841,6 +65936,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ ** writer truncated the WAL out from under it. If that happens, it ** indicates that a writer has fixed the SHM file for us, so retry */ if( rc==SQLITE_IOERR_SHORT_READ ) rc = WAL_RETRY; + MARK_LAST_BUSY_LINE(rc); } pWal->exclusiveMode = WAL_NORMAL_MODE; } @@ -66072,7 +66168,7 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ ** so it takes care to hold an exclusive lock on the corresponding ** WAL_READ_LOCK() while changing values. */ -static void printLockInfoUsingWal(Wal *pWal); +static void DumpLocksByWal(Wal *pWal); static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */ u32 mxReadMark; /* Largest aReadMark[] value */ @@ -66111,7 +66207,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ } if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39; #if SQLITE_OS_UNIX - if( cnt>=15 ) printLockInfoUsingWal(pWal); + if( cnt>=15 ) DumpLocksByWal(pWal); #endif /* SQLITE_OS_UNIX */ sqlite3OsSleep(pWal->pVfs, nDelay); } @@ -66139,11 +66235,14 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ ** must be zeroed before the requested page is returned. */ rc = WAL_RETRY; + MARK_LAST_BUSY_LINE(SQLITE_BUSY); }else if( SQLITE_OK==(rc = walLockShared(pWal, WAL_RECOVER_LOCK)) ){ walUnlockShared(pWal, WAL_RECOVER_LOCK); rc = WAL_RETRY; + MARK_LAST_BUSY_LINE(SQLITE_BUSY); }else if( rc==SQLITE_BUSY ){ rc = SQLITE_BUSY_RECOVERY; + MARK_LAST_BUSY_LINE(SQLITE_BUSY); } } if( rc!=SQLITE_OK ){ @@ -66166,6 +66265,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ ** and can be safely ignored. */ rc = walLockShared(pWal, WAL_READ_LOCK(0)); + MARK_LAST_BUSY_LINE(SQLITE_BUSY); walShmBarrier(pWal); if( rc==SQLITE_OK ){ if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ @@ -66183,6 +66283,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ ** it finished. Leaving a corrupt image in the database file. */ walUnlockShared(pWal, WAL_READ_LOCK(0)); + MARK_LAST_BUSY_LINE(SQLITE_BUSY); return WAL_RETRY; } pWal->readLock = 0; @@ -66227,6 +66328,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ }else if( rc!=SQLITE_BUSY ){ return rc; } + MARK_LAST_BUSY_LINE(rc); } } if( mxI==0 ){ @@ -66236,6 +66338,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); if( rc ){ + MARK_LAST_BUSY_LINE(rc); return rc==SQLITE_BUSY ? WAL_RETRY : rc; } /* Now that the read-lock has been obtained, check that neither the @@ -66278,6 +66381,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ walUnlockShared(pWal, WAL_READ_LOCK(mxI)); + MARK_LAST_BUSY_LINE(rc); return WAL_RETRY; }else{ assert( mxReadMark<=pWal->hdr.mxFrame ); @@ -66404,7 +66508,7 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ (void)walEnableBlocking(pWal); rc = walLockShared(pWal, WAL_CKPT_LOCK); walDisableBlocking(pWal); - + MARK_LAST_BUSY_LINE(rc); if( rc!=SQLITE_OK ){ return rc; } @@ -67209,9 +67313,9 @@ SQLITE_PRIVATE int sqlite3WalFrames( WALTRACE(("WAL%p: frame write %s\n", pWal, rc ? "failed" : "ok")); return rc; } - +#ifdef LOG_DUMP static sqlite3_int64 g_lastCkptTime = 0; - +#endif /* ** This routine is called to implement sqlite3_wal_checkpoint() and ** related interfaces. @@ -67241,7 +67345,7 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( assert( pWal->ckptLock==0 ); assert( pWal->writeLock==0 ); - + ResetLockStatus(); /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); @@ -67263,6 +67367,7 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( ** it will not be invoked in this case. */ rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); + MARK_LAST_BUSY_LINE(rc); testcase( rc==SQLITE_BUSY ); testcase( rc!=SQLITE_OK && xBusy2!=0 ); if( rc==SQLITE_OK ){ @@ -67279,6 +67384,7 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( */ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1); + MARK_LAST_BUSY_LINE(rc); if( rc==SQLITE_OK ){ pWal->writeLock = 1; }else if( rc==SQLITE_BUSY ){ @@ -71032,7 +71138,7 @@ static void pageReinit(DbPage *pData){ } } -static void printLockInfoUsingPager(Pager *pPager); +static void DumpLocksByPager(Pager *pPager); /* ** Invoke the busy handler for a btree. */ @@ -71042,8 +71148,8 @@ static int btreeInvokeBusyHandler(void *pArg){ assert( sqlite3_mutex_held(pBt->db->mutex) ); int rc = sqlite3InvokeBusyHandler(&pBt->db->busyHandler); #if SQLITE_OS_UNIX - if( rc==0 ){ - printLockInfoUsingPager( pBt->pPager ); + if (rc == 0) { + DumpLocksByPager(pBt->pPager); } #endif /* SQLITE_OS_UNIX */ return rc; @@ -72134,7 +72240,7 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVers sqlite3BtreeEnter(p); btreeIntegrity(p); - + ResetLockStatus(); /* If the btree is already in a write-transaction, or it ** is already in a read-transaction and a read-transaction ** is requested, this is a no-op. @@ -79504,6 +79610,7 @@ SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int * sqlite3BtreeEnter(p); if( pBt->inTransaction!=TRANS_NONE ){ rc = SQLITE_LOCKED; + MARK_LAST_BUSY_LINE(rc); }else{ rc = sqlite3PagerCheckpoint(pBt->pPager, p->db, eMode, pnLog, pnCkpt); } @@ -85350,6 +85457,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ nTrans++; } rc = sqlite3PagerExclusiveLock(pPager); + MARK_LAST_BUSY_LINE(rc); sqlite3BtreeLeave(pBt); } } @@ -176979,8 +177087,14 @@ opendb_out: sqlite3GlobalConfig.xSqllog(pArg, db, zFilename, 0); } #endif + #if defined(SQLITE_HAS_CODEC) - if( rc==SQLITE_OK ) sqlite3CodecQueryParameters(db, 0, zOpen); + if( rc==SQLITE_OK ) { +#ifdef SQLITE_CODEC_ATTACH_CHANGED + sqlite3CodecResetParameters(&db->codecParm); +#endif + sqlite3CodecQueryParameters(db, 0, zOpen); + } #endif sqlite3_free_filename(zOpen); return rc; @@ -245165,28 +245279,31 @@ CODEC_STATIC int sqlite3CodecSetIter(KeyContext *keyCtx, int iter){ #define CIPHER_NAME_AES_256_CBC "aes-256-cbc" #define CIPHER_NAME_AES_256_GCM "aes-256-gcm" -static int g_defaultAttachKdfIter = 10000; -static int g_defaultAttachCipher = CIPHER_ID_AES_256_GCM; -static int g_defaultAttachHmacAlgo = DEFAULT_HMAC_ALGORITHM; -static int g_defaultAttachKdfAlgo = DEFAULT_KDF_ALGORITHM; -static int g_defaultAttachPageSize = DEFAULT_PAGE_SIZE; - struct CodecCipherNameId { int cipherId; const char *cipherName; }; -struct CodecCipherNameId g_cipherNameIdMap[CIPHER_TOTAL_NUM] = { +static const struct CodecCipherNameId g_cipherNameIdMap[CIPHER_TOTAL_NUM] = { { CIPHER_ID_AES_256_CBC, CIPHER_NAME_AES_256_CBC }, { CIPHER_ID_AES_256_GCM, CIPHER_NAME_AES_256_GCM } }; -CODEC_STATIC void sqlite3CodecSetDefaultAttachCipher(const char *cipherName){ +SQLITE_PRIVATE void sqlite3CodecResetParameters(CodecParameter *p) +{ + p->kdfIter = DEFAULT_ITER; + p->pageSize = DEFAULT_PAGE_SIZE; + p->cipher = CIPHER_ID_AES_256_GCM; + p->hmacAlgo = DEFAULT_HMAC_ALGORITHM; + p->kdfAlgo = DEFAULT_KDF_ALGORITHM; +} + +CODEC_STATIC void sqlite3CodecSetDefaultAttachCipher(CodecParameter *parm, const char *cipherName){ int i; for( i=0; icipher = g_cipherNameIdMap[i].cipherId; sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); return; } @@ -245194,67 +245311,67 @@ CODEC_STATIC void sqlite3CodecSetDefaultAttachCipher(const char *cipherName){ sqlite3_log(SQLITE_WARNING, "invalid attach cipher algorithm"); } -CODEC_STATIC const char *sqlite3CodecGetDefaultAttachCipher(){ +CODEC_STATIC const char *sqlite3CodecGetDefaultAttachCipher(CodecParameter *parm){ const char *attachedCipher = CIPHER_NAME_AES_256_GCM; sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); - if( (g_defaultAttachCipher>=0) && (g_defaultAttachCiphercipher>=0) && (parm->ciphercipher].cipherName; } sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); return attachedCipher; } -CODEC_STATIC void sqlite3CodecSetDefaultAttachKdfIter(int iter){ +CODEC_STATIC void sqlite3CodecSetDefaultAttachKdfIter(CodecParameter *parm, int iter){ if( iter<=0 ){ return; } sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); - g_defaultAttachKdfIter = iter; + parm->kdfIter = iter; sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); } -CODEC_STATIC int sqlite3CodecGetDefaultAttachKdfIter(){ +CODEC_STATIC int sqlite3CodecGetDefaultAttachKdfIter(CodecParameter *parm){ sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); - int iterNum = g_defaultAttachKdfIter; + int iterNum = parm->kdfIter; sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); return iterNum; } -CODEC_STATIC void sqlite3CodecSetDefaultAttachHmacAlgo(int hmacAlgo){ +CODEC_STATIC void sqlite3CodecSetDefaultAttachHmacAlgo(CodecParameter *parm, int hmacAlgo){ sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); - g_defaultAttachHmacAlgo = hmacAlgo; + parm->hmacAlgo = hmacAlgo; sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); } -CODEC_STATIC int sqlite3CodecGetDefaultAttachHmacAlgo(){ +CODEC_STATIC int sqlite3CodecGetDefaultAttachHmacAlgo(CodecParameter *parm){ sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); - int hmacAlgo = g_defaultAttachHmacAlgo; + int hmacAlgo = parm->hmacAlgo; sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); return hmacAlgo; } -CODEC_STATIC void sqlite3CodecSetDefaultAttachKdfAlgo(int kdfAlgo){ +CODEC_STATIC void sqlite3CodecSetDefaultAttachKdfAlgo(CodecParameter *parm, int kdfAlgo){ sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); - g_defaultAttachKdfAlgo = kdfAlgo; + parm->kdfAlgo = kdfAlgo; sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); } -CODEC_STATIC int sqlite3CodecGetDefaultAttachKdfAlgo(){ +CODEC_STATIC int sqlite3CodecGetDefaultAttachKdfAlgo(CodecParameter *parm){ sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); - int kdfAlgo = g_defaultAttachKdfAlgo; + int kdfAlgo = parm->kdfAlgo; sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); return kdfAlgo; } -CODEC_STATIC void sqlite3CodecSetDefaultAttachPageSize(int pageSize){ +CODEC_STATIC void sqlite3CodecSetDefaultAttachPageSize(CodecParameter *parm, int pageSize){ sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); - g_defaultAttachPageSize = pageSize; + parm->pageSize = pageSize; sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); } -CODEC_STATIC int sqlite3CodecGetDefaultAttachPageSize(){ +CODEC_STATIC int sqlite3CodecGetDefaultAttachPageSize(CodecParameter *parm){ sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); - int pageSize = g_defaultAttachPageSize; + int pageSize = parm->pageSize; sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)); return pageSize; } @@ -245341,8 +245458,7 @@ CODEC_STATIC int sqlite3CodecCopyKeyContext(KeyContext *input, KeyContext *outpu // You should clear key context before you call this function #ifdef SQLITE_CODEC_ATTACH_CHANGED -CODEC_STATIC int sqlite3CodecInitKeyContext(CodecContext *ctx, Btree *p, const void *zKey, int nKey, int attachFlag, - int hmacAlgo){ +CODEC_STATIC int sqlite3CodecInitKeyContext(CodecContext *ctx, Btree *p, const void *zKey, int nKey, int attachFlag){ #else CODEC_STATIC int sqlite3CodecInitKeyContext(CodecContext *ctx, Btree *p, const void *zKey, int nKey){ #endif @@ -245350,16 +245466,18 @@ CODEC_STATIC int sqlite3CodecInitKeyContext(CodecContext *ctx, Btree *p, const v KeyContext *keyCtx = ctx->readCtx; #ifdef SQLITE_CODEC_ATTACH_CHANGED if( attachFlag!=0 ){ - rc = sqlite3CodecSetCodecConstant(keyCtx, sqlite3CodecGetDefaultAttachCipher()); - rc += sqlite3CodecSetIter(keyCtx, sqlite3CodecGetDefaultAttachKdfIter()); + CodecParameter *parm = &p->db->codecParm; + int hmacAlgo = sqlite3CodecGetDefaultAttachHmacAlgo(parm); + rc = sqlite3CodecSetCodecConstant(keyCtx, sqlite3CodecGetDefaultAttachCipher(parm)); + rc += sqlite3CodecSetIter(keyCtx, sqlite3CodecGetDefaultAttachKdfIter(parm)); if( hmacAlgo!=0 ){ rc += sqlite3CodecSetHmacAlgorithm(keyCtx, hmacAlgo); } - int attachKdfAlgo = sqlite3CodecGetDefaultAttachKdfAlgo(); + int attachKdfAlgo = sqlite3CodecGetDefaultAttachKdfAlgo(parm); if( attachKdfAlgo!=0 ){ rc += sqlite3CodecSetKdfAlgorithm(keyCtx, attachKdfAlgo); } - int cipherPageSize = sqlite3CodecGetDefaultAttachPageSize(); + int cipherPageSize = sqlite3CodecGetDefaultAttachPageSize(parm); if( cipherPageSize!=0 ){ rc += sqlite3CodecSetCipherPageSize(ctx, cipherPageSize); if ( rc != SQLITE_OK ) { @@ -245413,31 +245531,32 @@ CODEC_STATIC void sqlite3CodecFreeContext(CodecContext *ctx){ #ifdef SQLITE_CODEC_ATTACH_CHANGED CODEC_STATIC int sqlite3CodecInitContext(CodecContext *ctx, Btree *p, const void *zKey, int nKey, int nDb){ int attachFlag = (nDb > 1) ? 1 : 0; + int defaultPageSz = attachFlag ? p->db->codecParm.pageSize : DEFAULT_PAGE_SIZE; #else CODEC_STATIC int sqlite3CodecInitContext(CodecContext *ctx, Btree *p, const void *zKey, int nKey){ + int defaultPageSz = DEFAULT_PAGE_SIZE; #endif sqlite3_file *fd = p->pBt->pPager->fd; ctx->pBt = p; ctx->savePassword = 0; - ctx->buffer = (unsigned char *)sqlite3Malloc(DEFAULT_PAGE_SIZE); + ctx->buffer = (unsigned char *)sqlite3Malloc(defaultPageSz); ctx->readCtx = (KeyContext *)sqlite3Malloc(sizeof(KeyContext)); ctx->writeCtx = (KeyContext *)sqlite3Malloc(sizeof(KeyContext)); if(ctx->buffer == NULL || ctx->readCtx == NULL || ctx->writeCtx == NULL){ sqlite3CodecFreeContext(ctx); return SQLITE_NOMEM; } - errno_t memsetRc = memset_s(ctx->buffer, DEFAULT_PAGE_SIZE, 0, DEFAULT_PAGE_SIZE); + errno_t memsetRc = memset_s(ctx->buffer, defaultPageSz, 0, defaultPageSz); memsetRc += memset_s(ctx->readCtx, sizeof(KeyContext), 0, sizeof(KeyContext)); memsetRc += memset_s(ctx->writeCtx, sizeof(KeyContext), 0, sizeof(KeyContext)); if(memsetRc != EOK){ sqlite3CodecFreeContext(ctx); return SQLITE_ERROR; } - ctx->readCtx->codecConst.cipherPageSize = DEFAULT_PAGE_SIZE; - ctx->writeCtx->codecConst.cipherPageSize = DEFAULT_PAGE_SIZE; + ctx->readCtx->codecConst.cipherPageSize = defaultPageSz; + ctx->writeCtx->codecConst.cipherPageSize = defaultPageSz; #ifdef SQLITE_CODEC_ATTACH_CHANGED - int attachHmacAlgo = sqlite3CodecGetDefaultAttachHmacAlgo(); - int rc = sqlite3CodecInitKeyContext(ctx, p, zKey, nKey, attachFlag, attachHmacAlgo); + int rc = sqlite3CodecInitKeyContext(ctx, p, zKey, nKey, attachFlag); #else int rc = sqlite3CodecInitKeyContext(ctx, p, zKey, nKey); #endif @@ -245879,11 +245998,12 @@ int sqlite3CodecPragma(sqlite3 *db, int iDb, Parse *parse, const char *zLeft, co } CodecContext *ctx = (CodecContext *)(p->pBt->pPager->pCodec); #ifdef SQLITE_CODEC_ATTACH_CHANGED + CodecParameter *parm = &db->codecParm; if(sqlite3StrICmp(zLeft, "cipher_default_attach_cipher") == 0 && zRight != NULL){ - (void)sqlite3CodecSetDefaultAttachCipher(zRight); + (void)sqlite3CodecSetDefaultAttachCipher(parm, zRight); return 1; }else if(sqlite3StrICmp(zLeft, "cipher_default_attach_kdf_iter") == 0 && zRight != NULL){ - (void)sqlite3CodecSetDefaultAttachKdfIter(atoi(zRight)); + (void)sqlite3CodecSetDefaultAttachKdfIter(parm, atoi(zRight)); return 1; }else if( sqlite3StrICmp(zLeft, "cipher_default_attach_hmac_algo")==0 && zRight!=NULL ){ /* @@ -245891,31 +246011,31 @@ int sqlite3CodecPragma(sqlite3 *db, int iDb, Parse *parse, const char *zLeft, co ** This behavior is to ensure backward compatible. */ if( sqlite3_stricmp(zRight, CIPHER_HMAC_ALGORITHM_NAME_SHA1)==0 ){ - sqlite3CodecSetDefaultAttachHmacAlgo(CIPHER_HMAC_ALGORITHM_SHA1); - sqlite3CodecSetDefaultAttachKdfAlgo(CIPHER_KDF_ALGORITHM_SHA1); + sqlite3CodecSetDefaultAttachHmacAlgo(parm, CIPHER_HMAC_ALGORITHM_SHA1); + sqlite3CodecSetDefaultAttachKdfAlgo(parm, CIPHER_KDF_ALGORITHM_SHA1); }else if( sqlite3_stricmp(zRight, CIPHER_HMAC_ALGORITHM_NAME_SHA256)==0 ){ - sqlite3CodecSetDefaultAttachHmacAlgo(CIPHER_HMAC_ALGORITHM_SHA256); - sqlite3CodecSetDefaultAttachKdfAlgo(CIPHER_KDF_ALGORITHM_SHA256); + sqlite3CodecSetDefaultAttachHmacAlgo(parm, CIPHER_HMAC_ALGORITHM_SHA256); + sqlite3CodecSetDefaultAttachKdfAlgo(parm, CIPHER_KDF_ALGORITHM_SHA256); }else if( sqlite3_stricmp(zRight, CIPHER_HMAC_ALGORITHM_NAME_SHA512)==0 ){ - sqlite3CodecSetDefaultAttachHmacAlgo(CIPHER_HMAC_ALGORITHM_SHA512); - sqlite3CodecSetDefaultAttachKdfAlgo(CIPHER_KDF_ALGORITHM_SHA512); + sqlite3CodecSetDefaultAttachHmacAlgo(parm, CIPHER_HMAC_ALGORITHM_SHA512); + sqlite3CodecSetDefaultAttachKdfAlgo(parm, CIPHER_KDF_ALGORITHM_SHA512); }else{ return 0; } return 1; }else if( sqlite3StrICmp(zLeft, "cipher_default_attach_kdf_algo")==0 && zRight!=NULL ){ if( sqlite3_stricmp(zRight, CIPHER_KDF_ALGORITHM_NAME_SHA1)==0 ){ - sqlite3CodecSetDefaultAttachKdfAlgo(CIPHER_KDF_ALGORITHM_SHA1); + sqlite3CodecSetDefaultAttachKdfAlgo(parm, CIPHER_KDF_ALGORITHM_SHA1); }else if( sqlite3_stricmp(zRight, CIPHER_KDF_ALGORITHM_NAME_SHA256)==0 ){ - sqlite3CodecSetDefaultAttachKdfAlgo(CIPHER_KDF_ALGORITHM_SHA256); + sqlite3CodecSetDefaultAttachKdfAlgo(parm, CIPHER_KDF_ALGORITHM_SHA256); }else if( sqlite3_stricmp(zRight, CIPHER_KDF_ALGORITHM_NAME_SHA512)==0 ){ - sqlite3CodecSetDefaultAttachKdfAlgo(CIPHER_KDF_ALGORITHM_SHA512); + sqlite3CodecSetDefaultAttachKdfAlgo(parm, CIPHER_KDF_ALGORITHM_SHA512); }else{ return 0; } return 1; }else if( sqlite3StrICmp(zLeft, "cipher_default_attach_page_size")==0 && zRight!=NULL ){ - (void)sqlite3CodecSetDefaultAttachPageSize(atoi(zRight)); + (void)sqlite3CodecSetDefaultAttachPageSize(parm, atoi(zRight)); return 1; } #endif @@ -246220,106 +246340,236 @@ export_finish: } /************** End file hw_codec.c *****************************************/ #endif - #if SQLITE_OS_UNIX -#define DB_LOCK_NUM 3 -#define DB_SHARED_LOCK_OFFSET 2 -#define WAL_LOCK_NUM 8 -#define WAL_READ_LOCK_POS 3 -#define LOCK_NUM (DB_LOCK_NUM+WAL_LOCK_NUM) -static void printLockInfo(unixFile *dbFile, int walStat) +#if defined(__linux__) && !defined(gettid) +#include +#define gettid() syscall(__NR_gettid) +#endif +static void ResetLockStatus(void) +{ + (void)memset(&g_lockStatus, 0, sizeof(g_lockStatus)); +} +/* +** Record lock info, correspond wal aLock buf, 1 aLock: 1 +*/ +static void TryRecordTid(int *tidBuf, int ofs, int lockLen) { - const char *lockType[DB_LOCK_NUM] = {"F_RDLCK", "F_WRLCK", "F_UNLCK"}; + int lockOfs = ofs + lockLen; + for (int i = ofs; i < lockOfs; i++) { + if (tidBuf[i] == 0) { + tidBuf[i] = gettid(); + } + } +} +/* +** Clear locks info. +*/ +static void TryClearTid(int *tidBuf, int ofs, int lockLen) +{ + int lockOfs = ofs + lockLen; + for (int i = ofs; i < lockOfs; i++) { + if (tidBuf[i] == gettid()) { + tidBuf[i] = 0; + } + } +} + +static void MarkLockBusy(u32 lockIdx, u32 lockLen, u8 lockType, u8 lockByProcess) +{ + g_lockStatus.busyLockIdx = lockIdx; + g_lockStatus.busyLockType = lockType; + g_lockStatus.lockByProcess = lockByProcess; + g_lockStatus.lockLen = lockLen; +} - sqlite3_log(SQLITE_WARNING, "*** SQLITE_LOG DB Lock ***"); - if( dbFile==NULL ){ - sqlite3_log(SQLITE_WARNING, "NO DB FILE !"); +static void MarkLockStatus(u32 lockIdx, u32 lockLen, u8 lockType) +{ + if ((lockIdx + lockLen) > MAX_LOCK_NUM || lockLen == 0) { + sqlite3_log(SQLITE_ERROR, "Unexpect lock index %u lockLen %d!", lockIdx, lockLen); return; } - unixInodeInfo *inode = dbFile->pInode; - if( inode==NULL ){ - sqlite3_log(SQLITE_ERROR, "Inode is NULL !"); + if (lockIdx == g_lockStatus.busyLockIdx) { + g_lockStatus.busyLockIdx = 0; + g_lockStatus.lockLen = 0; + } + if (lockLen == 1) { + g_lockStatus.lockStatus[lockIdx] = lockType; + } else { + size_t len = sizeof(u8) * lockLen; + (void)memset(&g_lockStatus.lockStatus[lockIdx], lockType, len); + } +} + +static void MarkLockStatusByRc(int rc, u32 lockIdx, u32 lockLen, u8 lockType, u8 lockByProcess) +{ + if (rc == SQLITE_BUSY) { + MarkLockBusy(lockIdx, lockLen, lockType, lockByProcess); return; } - sqlite3_log(SQLITE_WARNING, "fileLock %d inodeRef %d inodeLockCnt %d inodeFileLock %d inodeProcessLock %d", - dbFile->eFileLock, inode->nRef, inode->nLock, inode->eFileLock, inode->bProcessLock); - - /* Set param list */ - int fdDb = dbFile->h; - const char *lockNameList[LOCK_NUM] = {"pending lock ", - "reserved lock", - "shared lock ", - "wal_write lock ", - "wal_ckpt lock ", - "wal_recover lock"}; - for(int i=DB_LOCK_NUM+WAL_READ_LOCK_POS; ipInode->pShmNode; - int hasShm = (dbFile->pShm==NULL || pShmNode==NULL) ? 0 : 1; - off_t startPos = PENDING_BYTE; - for(int i=0; ihShm; - } - off_t lockLen = i==DB_SHARED_LOCK_OFFSET ? SHARED_SIZE : 1; - struct flock flDb = { .l_type=F_WRLCK, .l_start=startPos, .l_len=lockLen, .l_whence=SEEK_SET }; - int ret = osFcntl(fdDb, F_GETLK, &flDb); - if( ret!=SQLITE_OK ){ - sqlite3_log(SQLITE_ERROR, "Get File Lock Info Fail! errno: %d", ret); - return; - } - if( flDb.l_type!=F_UNLCK ){ - sqlite3_log(SQLITE_WARNING, "lock name: %s, pos: 0x%x, len: 0x%x, lock type: %s, owner pId: %d", - lockNameList[i], startPos, lockLen, lockType[flDb.l_type], flDb.l_pid); + if (rc == SQLITE_OK) { + MarkLockStatus(lockIdx, lockLen, lockType); + } + // only busy error code need record + if (lockIdx == g_lockStatus.busyLockIdx && g_lockStatus.lockLen != 0) { + g_lockStatus.busyLockIdx = 0; + g_lockStatus.busyLockType = NO_LOCK; + g_lockStatus.lockLen = 0; + } +} + +static inline const char *TrxLockName(int eLock) +{ + return eLock == NO_LOCK ? "NO_LOCK" : + eLock == RESERVED_LOCK ? "RESERVED" : + eLock == EXCLUSIVE_LOCK ? "EXCLUSIVE" : + eLock == SHARED_LOCK ? "SHARED" : + eLock == UNKNOWN_LOCK ? "UNKNOWN" : "?error?"; +} + +static inline const char *IdxToLockName(u32 idx) +{ + const char *lockName[MAX_LOCK_NUM] = {"write", "ckpt", "recover", "read0", + "read1", "read2", "read3", "read4", "wal_dms", "trxLock"}; + return (idx < MAX_LOCK_NUM) ? lockName[idx] : "errLock"; +} + +static void DumpHandleLock(char *dumpBuf, int dumpBufLen) +{ + char *tmp = dumpBuf; + u8 *lockStatus = g_lockStatus.lockStatus; + int availLen = dumpBufLen - 1; + dumpBuf[availLen] = '\0'; + for (int i = 0; i < MAX_LOCK_NUM && availLen > DUMP_MAX_STR_LEN; i++) { + if (lockStatus[i] != NO_LOCK) { + tmp[0] = '\0'; + sqlite3_snprintf(availLen, tmp, "<%s, %s>", IdxToLockName((u32)i), TrxLockName(lockStatus[i])); + int len = strlen(tmp); + tmp += len; + availLen -= len; } - startPos++; } - - /* thread WAL Lock Info */ - for(int i=0; ipShm->pShmNode->aLock[i] ){ - sqlite3_log(SQLITE_WARNING, "Local WAL Lock[%d] for DB file: %d", i, dbFile->pShm->pShmNode->aLock[i]); + sqlite3_log(SQLITE_WARNING_DUMP, "[SQLite]BusyLine:%d, idx:%d, type:%d, fileLock:%d, len:%d, handleLocks:%s", + g_lockStatus.busyLine, g_lockStatus.busyLockIdx, g_lockStatus.busyLockType, g_lockStatus.lockByProcess, + g_lockStatus.lockLen, tmp != dumpBuf ? dumpBuf : "none"); +} + +static const char *FlockToName(int l_type) +{ + return l_type == F_RDLCK ? "F_RDLCK" : + l_type == F_WRLCK ? "F_WRLCK" : + l_type == F_UNLCK ? "F_UNLCK" : "F_UNKNOWN"; +} + +static int DumpProcessLocks(int fd, struct flock *lock, const char *lockName, char *dumpBuf, int bufLen) +{ + dumpBuf[0] = '\0'; + if (osFcntl(fd, F_GETLK, lock) != SQLITE_OK) { + sqlite3_log(SQLITE_ERROR, "[SQLite]Get wal file lock ofs %u failed, errno: %d", lock->l_start, errno); + return 0; + } + if (lock->l_type != F_UNLCK) { + sqlite3_snprintf(bufLen, dumpBuf, "<%s, pid:%u, %s>", lockName, lock->l_pid, FlockToName(lock->l_type)); + return strlen(dumpBuf); + } + return 0; +} + +static void DumpTrxProcessLocks(unixFile *file, char *dumpBuf, int dumpBufLen) +{ + unixInodeInfo *inode = file->pInode; + if (inode == NULL) { + sqlite3_log(SQLITE_ERROR, "[SQLite]Inode is null!"); + return; + } + sqlite3_log(SQLITE_WARNING_DUMP, "[SQLite]acqLock:%s, dbRef:%d, lockCnt:%d, curLock:%s, processLock:%d", + TrxLockName(file->eFileLock), inode->nRef, inode->nLock, TrxLockName(inode->eFileLock), inode->bProcessLock); + const char *lockName[DB_LOCK_NUM] = {"pending", "reserved", "shared_first"}; + char *tmp = dumpBuf; + int availLen = dumpBufLen - 1; + dumpBuf[availLen] = '\0'; + for (int i = 0; i < DB_LOCK_NUM && availLen > DUMP_MAX_STR_LEN; i++) { + off_t ofs = i + PENDING_BYTE; + off_t lockLen = (ofs == SHARED_FIRST) ? SHARED_SIZE : 1; + struct flock lock = {.l_type = F_WRLCK, .l_start = ofs, .l_len = lockLen, .l_whence = SEEK_SET}; + int lockBufLen = DumpProcessLocks(file->h, &lock, lockName[i], tmp, availLen); + tmp += lockBufLen; + availLen -= lockBufLen; + } + if (tmp != dumpBuf) { + sqlite3_log(SQLITE_WARNING_DUMP, "[SQLite]Trx locks: %s", dumpBuf); + } +} + +static void DumpWalLocks(unixFile *file, u8 walEnabled, char *dumpBuf, int dumpBufLen) +{ + if (!walEnabled || file->pShm == NULL || file->pShm->pShmNode == NULL) { + sqlite3_log(SQLITE_ERROR, "[SQLite]Wal mode disabled!"); + return; + } + unixShmNode *pShmNode = file->pShm->pShmNode; + char *tmp = dumpBuf; + int availLen = dumpBufLen - 1; + dumpBuf[availLen] = '\0'; + for (int i = 0; i < WAL_LOCK_NUM && availLen > DUMP_MAX_STR_LEN; i++) { + if (pShmNode->aLock[i] && i < SQLITE_SHM_NLOCK) { + tmp[0] = '\0'; + sqlite3_snprintf(availLen, tmp, "<%s, %d, tid:%d>", IdxToLockName((u32)i), pShmNode->aLock[i], + pShmNode->aLockTid[i]); + int strLen = strlen(tmp); + tmp += strLen; + availLen -= strLen; } + off_t ofs = i + WALINDEX_LOCK_OFFSET; + struct flock lock = {.l_type = F_WRLCK, .l_start = ofs, .l_len = 1, .l_whence = SEEK_SET}; + int bufLen = DumpProcessLocks(pShmNode->hShm, &lock, IdxToLockName((u32)i), tmp, availLen); + tmp += bufLen; + availLen -= bufLen; + } + if (tmp != dumpBuf) { + sqlite3_log(SQLITE_WARNING_DUMP, "[SQLite]Wal locks: %s", dumpBuf); + } +} + +static void DumpLocksInfo(unixFile *file, int walEnabled) +{ + char *dumpBuf = sqlite3Malloc(DUMP_BUF_MAX_LEN); + if (dumpBuf == NULL) { + sqlite3_log(SQLITE_ERROR, "[SQLite]Can't alloc bufferSz %d for dump!", DUMP_BUF_MAX_LEN); + return; } + DumpHandleLock(dumpBuf, DUMP_BUF_MAX_LEN); + DumpTrxProcessLocks(file, dumpBuf, DUMP_BUF_MAX_LEN); + DumpWalLocks(file, walEnabled, dumpBuf, DUMP_BUF_MAX_LEN); + sqlite3_free(dumpBuf); } #ifndef SQLITE_OMIT_WAL -static void printLockInfoUsingWal(Wal *pWal) +static void DumpLocksByWal(Wal *pWal) { - if( pWal==NULL ){ + if (pWal == NULL) { sqlite3_log(SQLITE_ERROR, "Wal ptr is NULL!"); return; } - if( pWal->pVfs==NULL || sqlite3_stricmp(pWal->pVfs->zName, "unix")!=0 ){ + if (pWal->pVfs == NULL || sqlite3_stricmp(pWal->pVfs->zName, "unix") != 0) { return; } - printLockInfo((unixFile *)(pWal->pDbFd), 1); + DumpLocksInfo((unixFile *)(pWal->pDbFd), 1); } #endif /* #ifndef SQLITE_OMIT_WAL */ -static void printLockInfoUsingPager(Pager *pPager) +static void DumpLocksByPager(Pager *pPager) { - if( pPager==NULL ){ + if (pPager == NULL) { sqlite3_log(SQLITE_ERROR, "Pager ptr is NULL!"); return; } - if( pPager->pVfs==NULL || sqlite3_stricmp(pPager->pVfs->zName, "unix")!=0 ){ + if (pPager->pVfs == NULL || sqlite3_stricmp(pPager->pVfs->zName, "unix") != 0) { return; } #ifndef SQLITE_OMIT_WAL - printLockInfo((unixFile *)(pPager->fd), pPager->pWal!=NULL); + DumpLocksInfo((unixFile *)(pPager->fd), pPager->pWal != NULL); #else /* #ifndef SQLITE_OMIT_WAL */ - printLockInfo((unixFile *)(pPager->fd), 0); + DumpLocksInfo((unixFile *)(pPager->fd), 0); #endif /* #ifndef SQLITE_OMIT_WAL */ } #endif /* SQLITE_OS_UNIX */