@@ -596,6 +596,119 @@ static struct unix_syscall {
596596}; /* End of the overrideable system calls */
597597
598598
599+ #if defined(SQLITE_DEBUG ) || defined(SQLITE_ENABLE_FILESTAT )
600+ /*
601+ ** Extract Posix Advisory Locking information about file description fd
602+ ** from the /proc/PID/fdinfo/FD pseudo-file. Fill the string buffer a[16]
603+ ** with characters to indicate which SQLite-relevant locks are held.
604+ ** a[16] will be a 15-character zero-terminated string with the following
605+ ** schema:
606+ **
607+ ** AAA/B.DDD.DDDDD
608+ **
609+ ** Each of character A-D will be "w" or "r" or "-" to indicate either a
610+ ** write-lock, a read-lock, or no-lock, respectively. The "." and "/"
611+ ** characters are delimiters intended to make the string more easily
612+ ** readable by humans. Here are the meaning of the specific letters:
613+ **
614+ ** AAA -> The main database locks. PENDING_BYTE, RESERVED_BYTE,
615+ ** and SHARED_FIRST, respectively.
616+ **
617+ ** B -> The deadman switch lock. Offset 128 of the -shm file.
618+ **
619+ ** CCC -> WAL locks: WRITE, CKPT, RECOVER
620+ **
621+ ** DDDDD -> WAL read-locks 0 through 5
622+ **
623+ ** Note that elements before the "/" apply to the main database file and
624+ ** elements after the "/" apply to the -shm file in WAL mode.
625+ **
626+ ** Here is another way of thinking about the meaning of the result string:
627+ **
628+ ** AAA/B.CCC.DDDDD
629+ ** ||| | ||| \___/
630+ ** PENDING--'|| | ||| `----- READ 0-5
631+ ** RESERVED--'| | ||`---- RECOVER
632+ ** SHARED ----' | |`----- CKPT
633+ ** DMS ------' `------ WRITE
634+ **
635+ ** Return SQLITE_OK on success and SQLITE_ERROR_UNABLE if the /proc
636+ ** pseudo-filesystem is unavailable.
637+ */
638+ static int unixPosixAdvisoryLocks (
639+ int fd , /* The file descriptor to analyze */
640+ char a [16 ] /* Write a text description of PALs here */
641+ ){
642+ int in ;
643+ ssize_t n ;
644+ char * p , * pNext , * x ;
645+ char z [2000 ];
646+
647+ /* 1 */
648+ /* 012 4 678 01234 */
649+ memcpy (a , "---/-.---.-----" , 16 );
650+ sqlite3_snprintf (sizeof (z ), z , "/proc/%d/fdinfo/%d" , getpid (), fd );
651+ in = osOpen (z , O_RDONLY , 0 );
652+ if ( in < 0 ){
653+ return SQLITE_ERROR_UNABLE ;
654+ }
655+ n = osRead (in , z , sizeof (z )- 1 );
656+ osClose (in );
657+ if ( n <=0 ) return SQLITE_ERROR_UNABLE ;
658+ z [n ] = 0 ;
659+
660+ /* We are looking for lines that begin with "lock:\t". Examples:
661+ **
662+ ** lock: 1: POSIX ADVISORY READ 494716 08:02:5277597 1073741826 1073742335
663+ ** lock: 1: POSIX ADVISORY WRITE 494716 08:02:5282282 120 120
664+ ** lock: 2: POSIX ADVISORY READ 494716 08:02:5282282 123 123
665+ ** lock: 3: POSIX ADVISORY READ 494716 08:02:5282282 128 128
666+ */
667+ pNext = strstr (z , "lock:\t" );
668+ while ( pNext ){
669+ char cType = 0 ;
670+ sqlite3_int64 iFirst , iLast ;
671+ p = pNext + 6 ;
672+ pNext = strstr (p , "lock:\t" );
673+ if ( pNext ) pNext [-1 ] = 0 ;
674+ if ( (x = strstr (p , " READ " ))!= 0 ){
675+ cType = 'r' ;
676+ x += 6 ;
677+ }else if ( (x = strstr (p , " WRITE " ))!= 0 ){
678+ cType = 'w' ;
679+ x += 7 ;
680+ }else {
681+ continue ;
682+ }
683+ x = strrchr (x , ' ' );
684+ if ( x == 0 ) continue ;
685+ iLast = strtoll (x + 1 , 0 , 10 );
686+ * x = 0 ;
687+ x = strrchr (p , ' ' );
688+ if ( x == 0 ) continue ;
689+ iFirst = strtoll (x + 1 , 0 , 10 );
690+ if ( iLast >=PENDING_BYTE ){
691+ if ( iFirst <=PENDING_BYTE && iLast >=PENDING_BYTE ) a [0 ] = cType ;
692+ if ( iFirst <=PENDING_BYTE + 1 && iLast >=PENDING_BYTE + 1 ) a [1 ] = cType ;
693+ if ( iFirst <=PENDING_BYTE + 2 && iLast >=PENDING_BYTE + 510 ) a [2 ] = cType ;
694+ }else if ( iLast <=128 ){
695+ if ( iFirst <=128 && iLast >=128 ) a [4 ] = cType ;
696+ if ( iFirst <=120 && iLast >=120 ) a [6 ] = cType ;
697+ if ( iFirst <=121 && iLast >=121 ) a [7 ] = cType ;
698+ if ( iFirst <=122 && iLast >=122 ) a [8 ] = cType ;
699+ if ( iFirst <=123 && iLast >=123 ) a [10 ] = cType ;
700+ if ( iFirst <=124 && iLast >=124 ) a [11 ] = cType ;
701+ if ( iFirst <=125 && iLast >=125 ) a [12 ] = cType ;
702+ if ( iFirst <=126 && iLast >=126 ) a [13 ] = cType ;
703+ if ( iFirst <=127 && iLast >=127 ) a [14 ] = cType ;
704+ }
705+ }
706+ return SQLITE_OK ;
707+ }
708+ #else
709+ # define unixPosixAdvisoryLocks (A ,B ) SQLITE_ERROR_UNABLE
710+ #endif /* SQLITE_DEBUG || SQLITE_ENABLE_FILESTAT */
711+
599712/*
600713** On some systems, calls to fchown() will trigger a message in a security
601714** log if they come from non-root processes. So avoid calling fchown() if
@@ -4002,6 +4115,10 @@ static int unixGetTempname(int nBuf, char *zBuf);
40024115#if !defined(SQLITE_WASI ) && !defined(SQLITE_OMIT_WAL )
40034116 static int unixFcntlExternalReader (unixFile * , int * );
40044117#endif
4118+ #if defined(SQLITE_DEBUG ) || defined(SQLITE_ENABLE_FILESTAT )
4119+ static void unixDescribeShm (sqlite3_str * ,unixShm * );
4120+ #endif
4121+
40054122
40064123/*
40074124** Information and control of an open file handle.
@@ -4144,6 +4261,66 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
41444261 return SQLITE_OK ;
41454262#endif
41464263 }
4264+
4265+ #if defined(SQLITE_DEBUG ) || defined(SQLITE_ENABLE_FILESTAT )
4266+ case SQLITE_FCNTL_FILESTAT : {
4267+ sqlite3_str * pStr = (sqlite3_str * )pArg ;
4268+ char aLck [16 ];
4269+ unixInodeInfo * pInode ;
4270+ static const char * azLock [] = { "SHARED" , "RESERVED" ,
4271+ "PENDING" , "EXCLUSIVE" };
4272+ sqlite3_str_appendf (pStr , "{\"h\":%d" , pFile -> h );
4273+ sqlite3_str_appendf (pStr , ",\"vfs\":\"%s\"" , pFile -> pVfs -> zName );
4274+ if ( pFile -> eFileLock ){
4275+ sqlite3_str_appendf (pStr , ",\"eFileLock\":\"%s\"" ,
4276+ azLock [pFile -> eFileLock - 1 ]);
4277+ if ( unixPosixAdvisoryLocks (pFile -> h , aLck )== SQLITE_OK ){
4278+ sqlite3_str_appendf (pStr , ",\"pal\":\"%s\"" , aLck );
4279+ }
4280+ }
4281+ unixEnterMutex ();
4282+ if ( pFile -> pShm ){
4283+ sqlite3_str_appendall (pStr , ",\"shm\":" );
4284+ unixDescribeShm (pStr , pFile -> pShm );
4285+ }
4286+ #if SQLITE_MAX_MMAP_SIZE > 0
4287+ if ( pFile -> mmapSize ){
4288+ sqlite3_str_appendf (pStr , ",\"mmapSize\":%lld" , pFile -> mmapSize );
4289+ sqlite3_str_appendf (pStr , ",\"nFetchOut\":%d" , pFile -> nFetchOut );
4290+ }
4291+ #endif
4292+ if ( (pInode = pFile -> pInode )!= 0 ){
4293+ sqlite3_str_appendf (pStr , ",\"inode\":{\"nRef\":%d" ,pInode -> nRef );
4294+ sqlite3_mutex_enter (pInode -> pLockMutex );
4295+ sqlite3_str_appendf (pStr , ",\"nShared\":%d" , pInode -> nShared );
4296+ if ( pInode -> eFileLock ){
4297+ sqlite3_str_appendf (pStr , ",\"eFileLock\":\"%s\"" ,
4298+ azLock [pInode -> eFileLock - 1 ]);
4299+ }
4300+ if ( pInode -> pUnused ){
4301+ char cSep = '[' ;
4302+ UnixUnusedFd * pUFd = pFile -> pInode -> pUnused ;
4303+ sqlite3_str_appendall (pStr , ",\"unusedFd\":" );
4304+ while ( pUFd ){
4305+ sqlite3_str_appendf (pStr , "%c{\"fd\":%d,\"flags\":%d" ,
4306+ cSep , pUFd -> fd , pUFd -> flags );
4307+ cSep = ',' ;
4308+ if ( unixPosixAdvisoryLocks (pUFd -> fd , aLck )== SQLITE_OK ){
4309+ sqlite3_str_appendf (pStr , ",\"pal\":\"%s\"" , aLck );
4310+ }
4311+ sqlite3_str_append (pStr , "}" , 1 );
4312+ pUFd = pUFd -> pNext ;
4313+ }
4314+ sqlite3_str_append (pStr , "]" , 1 );
4315+ }
4316+ sqlite3_mutex_leave (pInode -> pLockMutex );
4317+ sqlite3_str_append (pStr , "}" , 1 );
4318+ }
4319+ unixLeaveMutex ();
4320+ sqlite3_str_append (pStr , "}" , 1 );
4321+ return SQLITE_OK ;
4322+ }
4323+ #endif /* SQLITE_DEBUG || SQLITE_ENABLE_FILESTAT */
41474324 }
41484325 return SQLITE_NOTFOUND ;
41494326}
@@ -4410,6 +4587,26 @@ struct unixShm {
44104587#define UNIX_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
44114588#define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
44124589
4590+ #if defined(SQLITE_DEBUG ) || defined(SQLITE_ENABLE_FILESTAT )
4591+ /*
4592+ ** Describe the pShm object using JSON. Used for diagnostics only.
4593+ */
4594+ static void unixDescribeShm (sqlite3_str * pStr , unixShm * pShm ){
4595+ unixShmNode * pNode = pShm -> pShmNode ;
4596+ char aLck [16 ];
4597+ sqlite3_str_appendf (pStr , "{\"h\":%d" , pNode -> hShm );
4598+ assert ( unixMutexHeld () );
4599+ sqlite3_str_appendf (pStr , ",\"nRef\":%d" , pNode -> nRef );
4600+ sqlite3_str_appendf (pStr , ",\"id\":%d" , pShm -> id );
4601+ sqlite3_str_appendf (pStr , ",\"sharedMask\":%d" , pShm -> sharedMask );
4602+ sqlite3_str_appendf (pStr , ",\"exclMask\":%d" , pShm -> exclMask );
4603+ if ( unixPosixAdvisoryLocks (pNode -> hShm , aLck )== SQLITE_OK ){
4604+ sqlite3_str_appendf (pStr , ",\"pal\":\"%s\"" , aLck );
4605+ }
4606+ sqlite3_str_append (pStr , "}" , 1 );
4607+ }
4608+ #endif /* SQLITE_DEBUG || SQLITE_ENABLE_FILESTAT */
4609+
44134610/*
44144611** Use F_GETLK to check whether or not there are any readers with open
44154612** wal-mode transactions in other processes on database file pFile. If
0 commit comments