SQLite 3ソース学習(16)test_vfsフレームワーク

7967 ワード

test_vfsは主にテストに用いられ、ファイルシステムのエラーをシミュレートするために用いられ、コードはtest_で実現される.vfs.cの中.機械的障害やファイルシステムのスペースが尽きたなどのエラーは、実際に測定するのは難しいです.test_vfsは、OSインタフェース層とVFSとの間にテストされたVFSを挿入して、ファイルシステムのエラーをシミュレートし、SQLiteのエラー処理の安定性を検証する.Tclスクリプトでシミュレーションを制御し、通常は元のVFSを呼び出します.

1.VFSの登録


Tclでのフォーマットは次のとおりです.
testvfs VFSNAME ?SWITCHES?
testvfsの実装関数はtestvfs_cmd()は、この関数に新しいコマンドVFSNAMEを作成して、いくつかのテストシーンを設定します.SWITSCHESはtestvfsコマンドのいくつかのオプションです.
-noshm:1を設定して共有キャッシュを禁止
-default:tvfs_をvfsを優先VFSに設定
-fullshm:元のVFSを使用した共有キャッシュ
登録時にまずVFSインタフェース関数を宣言し、tvfs_vfs構造体に
static sqlite3_vfs tvfs_vfs
元のデフォルトVFSをpAppDataに記録する
 Testvfs *p;                     /* New object */
  sqlite3_vfs *pVfs;              /* New VFS */

  zVfs = Tcl_GetString(objv[1]);// VFSNAME 
  nByte = sizeof(Testvfs) + (int)strlen(zVfs)+1;
  p = (Testvfs *)ckalloc(nByte);
  memset(p, 0, nByte);

p->pParent = sqlite3_vfs_find(0);// VFS

p->zName = (char *)&p[1];
memcpy(p->zName, zVfs, strlen(zVfs)+1);// vfs 
pVfs->pAppData = (void *)p;// pVfs->pAppData 

sqlite3_vfs_register(pVfs, isDefault);// VFS

コマンドの作成時には、pVfs->pAppDataの情報(Testvfs*pのアドレス)もコマンドに転送して呼び出します.
// 
EXTERN Tcl_Command	Tcl_CreateObjCommand(Tcl_Interp *interp,
				const char *cmdName, Tcl_ObjCmdProc *proc,
				ClientData clientData,
				Tcl_CmdDeleteProc *deleteProc);
// 
Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del);
// , 1 p
static int SQLITE_TCLAPI testvfs_obj_cmd(
  ClientData cd,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
)

2.Tclコマンドパラメータ


コマンドのコールバック関数はtestvfs_obj_cmd、このコマンドには次のような複数の実行パラメータがあります.
● shm
VFSを登録するときに新しいコマンドがtvfsであると仮定し、フォーマットは次のとおりです.
tvfs shm FILE ?VALUE?
上記のコマンドでは、ファイル接続FILEに対応するキャッシュを開き、1つの配列オブジェクトに追加します.4番目のパラメータが存在する場合、VALUEオブジェクト配列の値をキャッシュにコピーします.例:
set blob [tvfs shm $file]
上記のコマンドでは、対応する接続ファイルのキャッシュを格納する配列オブジェクトを新規作成します.
Testvfs *p = (Testvfs *)cd;
/* */
      for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
        if( 0==strcmp(pBuffer->zFile, zName) ) break;
      }

配列オブジェクトにキャッシュを追加し、その配列オブジェクトに戻ります.
      
 pObj = Tcl_NewObj();
      for(i=0; pBuffer->aPage[i]; i++){
        int pgsz = pBuffer->pgsz;
        if( pgsz==0 ) pgsz = 65536;
        Tcl_AppendObjToObj(pObj, Tcl_NewByteArrayObj(pBuffer->aPage[i], pgsz));// 
      }
      Tcl_SetObjResult(interp, pObj);// Tcl 
      

blobオブジェクトの配列の内容がTclで変更され、このコマンドで4番目のパラメータでblobオブジェクトが入力されると、オブジェクトの内容がキャッシュにコピーされます.
      
  if( objc==4 ){
        int n;
        u8 *a = Tcl_GetByteArrayFromObj(objv[3], &n);// 
        int pgsz = pBuffer->pgsz;

        if( pgsz==0 ) pgsz = 65536;
        /* */
        for(i=0; i*pgszaPage[i], &a[i*pgsz], nByte);
        }
      }

● script
スクリプトのコールバック関数を登録し、p->pScriptに保存し、以下のように登録します.
tvfs script tvfs_cb   tvfs_cb 

 
● filter
VFSには、次のような方法がたくさんあります.
static struct VfsMethod {
        char *zName;
        int mask;
      } vfsmethod [] = {
        { "xShmOpen",           TESTVFS_SHMOPEN_MASK },
        { "xShmLock",           TESTVFS_SHMLOCK_MASK },
        { "xShmBarrier",        TESTVFS_SHMBARRIER_MASK },
        { "xShmUnmap",          TESTVFS_SHMCLOSE_MASK },
        { "xShmMap",            TESTVFS_SHMMAP_MASK },
         ……
}

このコマンドは、特定のメソッドに対してフィルタマスクを設定します.
/* */
      if( Tcl_ListObjGetElements(interp, objv[2], &nElem, &apElem) ){
        return TCL_ERROR;
      }
……
/* */
      for(i=0; imask = mask;// 

例えばxShmOpenとxShmLockメソッドのフィルタマスクを設定する
tvfs filter xShmOpen xShmLock

コールバック関数を実行するには、この2つの方法しかありません.
 
  if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){
    /* xShmOpen , */
    tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0, 0);
    if( tvfsResultCode(p, &rc) ){
      if( rc!=SQLITE_OK ) return rc;
    }
  }

● ioerr、fullerr、cantopenerr
この3つのネーミングは、シミュレーションエラーの回数などを設定するために使用されます.
    
 case CMD_CANTOPENERR:
    case CMD_IOERR:
    case CMD_FULLERR: {
      TestFaultInject *pTest = 0;
      int iRet;

      switch( aSubcmd[i].eCmd ){
        case CMD_IOERR: pTest = &p->ioerr_err; break;
        case CMD_FULLERR: pTest = &p->full_err; break;
        case CMD_CANTOPENERR: pTest = &p->cantopen_err; break;
        default: assert(0);
      }
      iRet = pTest->nFail;// 
      pTest->nFail = 0;// 
      pTest->eFault = 0;// 
      pTest->iCnt = 0;// 
  /* , */
……

3. open


vfsが開くインタフェースはtvfsOpen()関数で、最も主要な2つのパラメータです.
sqlite3_vfs *pVfs:
vfsに関連付けられたアプリケーションデータを取得し、スクリプトコールバック関数を呼び出す
Tcl_Obj *pId = 0;
Testvfs *p = (Testvfs *)pVfs->pAppData;
if( p->pScript && p->mask&TESTVFS_OPEN_MASK ){
    Tcl_Obj *pArg = Tcl_NewObj();
    Tcl_IncrRefCount(pArg);
 ……
    tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0, 0);
    Tcl_DecrRefCount(pArg);
    if( tvfsResultCode(p, &rc) ){
      if( rc!=SQLITE_OK ) return rc;
    }else{
      pId = Tcl_GetObjResult(p->interp);// 
    }
  }

sqlite3_file *pFile:
PagerOpenによってスペースが割り当てられている接続ハンドルで、着信時にTestvfsタイプに変換しioメソッドなどの情報を設定します
#define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent)

  TestvfsFile *pTestfile = (TestvfsFile *)pFile;
  TestvfsFd *pFd;
  pFd = (TestvfsFd *)ckalloc(sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile);// parent vfs 
  memset(pFd, 0, sizeof(TestvfsFd) + PARENTVFS(pVfs)->szOsFile);
  pFd->pShm = 0;
  pFd->pShmId = 0;
  pFd->zFilename = zName;
  pFd->pVfs = pVfs;
  pFd->pReal = (sqlite3_file *)&pFd[1];// sizeof(TestvfsFd) , parent vfs 
  memset(pTestfile, 0, sizeof(TestvfsFile));
  pTestfile->pFd = pFd;// pFile 
rc = sqlite3OsOpen(PARENTVFS(pVfs), zName, pFd->pReal, flags, pOutFlags);// parent vfsd pFd->pReal
if( pFd->pReal->pMethods ){
    sqlite3_io_methods *pMethods;
    int nByte;

    if( pVfs->iVersion>1 ){
      nByte = sizeof(sqlite3_io_methods);
    }else{
      nByte = offsetof(sqlite3_io_methods, xShmMap);//  , ((size_t) &((sqlite3_io_methods *)0)->xShmMap)
    }

    pMethods = (sqlite3_io_methods *)ckalloc(nByte);
memcpy(pMethods, &tvfs_io_methods, nByte);
……
pFile->pMethods = pMethods;// io_methods tvfs_io_methods
}

4.エラーシミュレーション


Tclにエラー情報が構成されている場合は、エラーを返します.そうしないと、parent vfsのioメソッドが正常に呼び出されます.以下はopenエラーのシミュレーションコードです.
static int tvfsInjectFault(TestFaultInject *p){
  int ret = 0;
  if( p->eFault ){// 
    p->iCnt--;// 
    if( p->iCnt==0 || (p->iCnt<0 && p->eFault==FAULT_INJECT_PERSISTENT ) ){
      ret = 1;
      p->nFail++;// 
    }
  }
  return ret;
}

static int tvfsInjectIoerr(Testvfs *p){
  return tvfsInjectFault(&p->ioerr_err);// io 
}

static int tvfsInjectFullerr(Testvfs *p){
  return tvfsInjectFault(&p->full_err);// 
}
static int tvfsInjectCantopenerr(Testvfs *p){
  return tvfsInjectFault(&p->cantopen_err);// 
}

  if( (p->mask&TESTVFS_OPEN_MASK) &&  tvfsInjectIoerr(p) ) return SQLITE_IOERR;
  if( tvfsInjectCantopenerr(p) ) return SQLITE_CANTOPEN;
  if( tvfsInjectFullerr(p) ) return SQLITE_FULL;