SQLite4
Check-in [3f1a52c793]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add simple OOM injection test to show that the sqlite4_mm based test infrastructure works.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 3f1a52c793f623280f064cf74b4c53815a5290ec
User & Date: dan 2013-05-22 17:40:57
Context
2013-05-23
09:39
Changed TLIBS= to TLIBS?= to allow override from CLI. check-in: 9199b1fa38 user: stephan tags: trunk
2013-05-22
17:40
Add simple OOM injection test to show that the sqlite4_mm based test infrastructure works. check-in: 3f1a52c793 user: dan tags: trunk
2013-05-21
20:08
Remove sqlite4_mem_methods from sqlite.h.in. Other modifications so that src4.test works again. check-in: 7091e990cd user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/build.c.

1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
  sqlite4 *db = pParse->db;
  Index *pIndex;                  /* New index */
  char *zExtra;

  assert( !pTab->pIndex || pTab->pIndex->eIndexType!=SQLITE4_INDEX_PRIMARYKEY );
  assert( sqlite4Strlen30("binary")==6 );
  pIndex = newIndex(pParse, pTab, pTab->zName, 1, OE_Abort, 1+6, &zExtra);
  if( addIndexToHash(db, pIndex) ){
    sqlite4DbFree(db, pIndex);
    pIndex = 0;
  }
  if( pIndex ){
    pIndex->aiColumn[0] = -1;
    pIndex->azColl[0] = zExtra;
    memcpy(zExtra, "binary", 7);







|







1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
  sqlite4 *db = pParse->db;
  Index *pIndex;                  /* New index */
  char *zExtra;

  assert( !pTab->pIndex || pTab->pIndex->eIndexType!=SQLITE4_INDEX_PRIMARYKEY );
  assert( sqlite4Strlen30("binary")==6 );
  pIndex = newIndex(pParse, pTab, pTab->zName, 1, OE_Abort, 1+6, &zExtra);
  if( pIndex && addIndexToHash(db, pIndex) ){
    sqlite4DbFree(db, pIndex);
    pIndex = 0;
  }
  if( pIndex ){
    pIndex->aiColumn[0] = -1;
    pIndex->azColl[0] = zExtra;
    memcpy(zExtra, "binary", 7);

Changes to src/mem.c.

381
382
383
384
385
386
387








388
389
390
391
392
393
394
...
424
425
426
427
428
429
430





431
432
433
434
435
436
437
...
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
...
656
657
658
659
660
661
662
663
664
665
666



667
668
669
670
671
672
673
674
675
676
677
678
}

static int mmStatsMember(sqlite4_mm *pMM, const void *pOld){
  struct mmStats *pStats = (struct mmStats*)pMM;
  return sqlite4_mm_member(pStats->p, pOld);
}










static sqlite4_int64 mmStatsStat(
  sqlite4_mm *pMM, 
  unsigned int eType, 
  unsigned int flags
){
  struct mmStats *pStats = (struct mmStats*)pMM;
................................................................................
      if( flags & SQLITE4_MMSTAT_RESET ) pStats->nFault = 0;
      break;
    }
  }
  sqlite4_mutex_leave(pStats->mutex);
  return iRet;
}






/*
** Destroy the allocator object passed as the first argument.
*/
static void mmStatsFinal(sqlite4_mm *pMM){
  struct mmStats *pStats = (struct mmStats*)pMM;
  sqlite4_mm *p = pStats->p;
................................................................................
  /* xMalloc  */    mmStatsMalloc,
  /* xRealloc */    mmStatsRealloc,
  /* xFree    */    mmStatsFree,
  /* xMsize   */    mmStatsMsize,
  /* xMember  */    mmStatsMember,
  /* xBenign  */    0,
  /* xStat    */    mmStatsStat,
  /* xCtrl    */    0,
  /* xFinal   */    mmStatsFinal
};

/*
** Allocate a new stats allocator.
*/
static sqlite4_mm *mmStatsNew(sqlite4_mm *p){
................................................................................
    pMM->pMethods->xBenign(pMM, bEnable);
  }
}
sqlite4_int64 sqlite4_mm_stat(sqlite4_mm *pMM, int eStatType, unsigned flags){
  if( pMM==0 ) return -1;
  if( pMM->pMethods->xStat==0 ) return -1;
  return pMM->pMethods->xStat(pMM, eStatType, flags);
}
int sqlite4_mm_control(sqlite4_mm *pMM, int eCtrlType, ...){
  int rc = SQLITE4_NOTFOUND;
  if( pMM && pMM->pMethods->xCtrl ){



    va_list ap;
    va_start(ap, eCtrlType);
    rc = pMM->pMethods->xCtrl(pMM, eCtrlType, ap);
    va_end(ap);
  }
  return rc;
}
void sqlite4_mm_destroy(sqlite4_mm *pMM){
  if( pMM && pMM->pMethods->xFinal ) pMM->pMethods->xFinal(pMM);
}

/*







>
>
>
>
>
>
>
>







 







>
>
>
>
>







 







|







 








|
|
|
>
>
>
|
|
|
|
<







381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
...
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
...
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
...
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686

687
688
689
690
691
692
693
}

static int mmStatsMember(sqlite4_mm *pMM, const void *pOld){
  struct mmStats *pStats = (struct mmStats*)pMM;
  return sqlite4_mm_member(pStats->p, pOld);
}

/*
** sqlite4_mm_methods.xBenign method.
*/
static void mmStatsBenign(sqlite4_mm *pMM, int bBenign){
  struct mmStats *pStats = (struct mmStats *)pMM;
  sqlite4_mm_benign_failures(pStats->p, bBenign);
}


static sqlite4_int64 mmStatsStat(
  sqlite4_mm *pMM, 
  unsigned int eType, 
  unsigned int flags
){
  struct mmStats *pStats = (struct mmStats*)pMM;
................................................................................
      if( flags & SQLITE4_MMSTAT_RESET ) pStats->nFault = 0;
      break;
    }
  }
  sqlite4_mutex_leave(pStats->mutex);
  return iRet;
}

static int mmStatsCtrl(sqlite4_mm *pMM, unsigned int eType, va_list ap){
  struct mmStats *pStats = (struct mmStats*)pMM;
  return sqlite4_mm_control_va(pStats->p, eType, ap);
}

/*
** Destroy the allocator object passed as the first argument.
*/
static void mmStatsFinal(sqlite4_mm *pMM){
  struct mmStats *pStats = (struct mmStats*)pMM;
  sqlite4_mm *p = pStats->p;
................................................................................
  /* xMalloc  */    mmStatsMalloc,
  /* xRealloc */    mmStatsRealloc,
  /* xFree    */    mmStatsFree,
  /* xMsize   */    mmStatsMsize,
  /* xMember  */    mmStatsMember,
  /* xBenign  */    0,
  /* xStat    */    mmStatsStat,
  /* xCtrl    */    mmStatsCtrl,
  /* xFinal   */    mmStatsFinal
};

/*
** Allocate a new stats allocator.
*/
static sqlite4_mm *mmStatsNew(sqlite4_mm *p){
................................................................................
    pMM->pMethods->xBenign(pMM, bEnable);
  }
}
sqlite4_int64 sqlite4_mm_stat(sqlite4_mm *pMM, int eStatType, unsigned flags){
  if( pMM==0 ) return -1;
  if( pMM->pMethods->xStat==0 ) return -1;
  return pMM->pMethods->xStat(pMM, eStatType, flags);
}
int sqlite4_mm_control_va(sqlite4_mm *pMM, int eCtrlType, va_list ap){
  if( pMM==0 || pMM->pMethods->xCtrl==0 ) return SQLITE4_NOTFOUND;
  return pMM->pMethods->xCtrl(pMM, eCtrlType, ap);
}
int sqlite4_mm_control(sqlite4_mm *pMM, int eCtrlType, ...){
  int rc;
  va_list ap;
  va_start(ap, eCtrlType);
  rc = sqlite4_mm_control_va(pMM, eCtrlType, ap);
  va_end(ap);

  return rc;
}
void sqlite4_mm_destroy(sqlite4_mm *pMM){
  if( pMM && pMM->pMethods->xFinal ) pMM->pMethods->xFinal(pMM);
}

/*

Changes to src/sqlite.h.in.

194
195
196
197
198
199
200

201
202
203
204
205
206
207
*/
sqlite4_int64 sqlite4_mm_stat(sqlite4_mm *pMM, int eType, unsigned flags);

/*
** Send a control message into a memory allocator.
*/
int sqlite4_mm_control(sqlite4_mm *pMM, int eType, ...);


/*
** Enable or disable benign failure mode.  Benign failure mode can be
** nested.  In benign failure mode, OOM errors do not necessarily propagate
** back out to the application but can be dealt with internally.  Memory
** allocations that occur in benign failure mode are considered "optional".
*/







>







194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
*/
sqlite4_int64 sqlite4_mm_stat(sqlite4_mm *pMM, int eType, unsigned flags);

/*
** Send a control message into a memory allocator.
*/
int sqlite4_mm_control(sqlite4_mm *pMM, int eType, ...);
int sqlite4_mm_control_va(sqlite4_mm *pMM, int eType, va_list ap);

/*
** Enable or disable benign failure mode.  Benign failure mode can be
** nested.  In benign failure mode, OOM errors do not necessarily propagate
** back out to the application but can be dealt with internally.  Memory
** allocations that occur in benign failure mode are considered "optional".
*/

Added test/fault1.test.





















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 2010 June 15
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
set ::testprefix fault1


do_faultsim_test 1.0 -faults oom* -body {
  execsql { SELECT * FROM sqlite_master }
} -test {
  faultsim_test_result {0 {}} 
}

finish_test

Changes to test/malloc_common.tcl.

171
172
173
174
175
176
177
178
179
180
181


182
183
184
185
186
187
188
}


# The following procs are used as [do_one_faultsim_test] callbacks when 
# injecting OOM faults into test cases.
#
proc oom_injectstart {nRepeat iFail} {
  sqlite4_memdebug_fail [expr $iFail-1] -repeat $nRepeat
}
proc oom_injectstop {} {
  sqlite4_memdebug_fail -1


}

# The following procs are used as [do_one_faultsim_test] callbacks when 
# injecting IO error faults into test cases.
#
proc ioerr_injectstart {persist iFail} {
  set ::sqlite_io_error_persist $persist







|


|
>
>







171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
}


# The following procs are used as [do_one_faultsim_test] callbacks when 
# injecting OOM faults into test cases.
#
proc oom_injectstart {nRepeat iFail} {
  test_mm_faultconfig $iFail [expr ($nRepeat>0)]
}
proc oom_injectstop {} {
  foreach {nFault nBenign} [test_mm_faultreport] break
  test_mm_faultconfig 0 0
  return $nFault
}

# The following procs are used as [do_one_faultsim_test] callbacks when 
# injecting IO error faults into test cases.
#
proc ioerr_injectstart {persist iFail} {
  set ::sqlite_io_error_persist $persist

Changes to test/testInt.h.

21
22
23
24
25
26
27






































28
29

30
31
32

/* test_main.c */
void sqlite4TestInit(Tcl_Interp*);
void *sqlite4TestTextToPtr(const char *z);
int sqlite4TestDbHandle(Tcl_Interp *, Tcl_Obj *, sqlite4 **);
int sqlite4TestSetResult(Tcl_Interp *interp, int rc);







































sqlite4_mm *test_mm_debug(sqlite4_mm *p);
void test_mm_debug_report(sqlite4_mm *p, FILE *pFile);


#endif








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<
>



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

67
68
69
70

/* test_main.c */
void sqlite4TestInit(Tcl_Interp*);
void *sqlite4TestTextToPtr(const char *z);
int sqlite4TestDbHandle(Tcl_Interp *, Tcl_Obj *, sqlite4 **);
int sqlite4TestSetResult(Tcl_Interp *interp, int rc);

/* test_mem.c */

/*
** The following values are interpreted by the xCtrl() methods of the 
** special sqlite4_mm objects used for testing.
**
** TESTMEM_CTRL_REPORT:
**   Write a report concerning all mallocs, outstanding and otherwise,
**   to the open file (type FILE*) passed as the third argument.
**
** TESTMEM_CTRL_FAULTCONFIG:
**   Used to configure the OOM fault injector. The third and fourth
**   arguments passed to the sqlite4_mm_control() call should be of
**   type int. 
**
**   The first is the number of allocation requests that will succeed,
**   plus one, before the next failure is injected. In other words, 
**   passing a value of 1 means the very next allocation attempt will
**   fail. If the second argument is non-zero, then all allocations
**   following a failure also fail (persistent OOM). Otherwise, subsequent
**   allocations succeed (transient OOM).
**
**   Passing zero as the first integer argument effectively disables
**   fault injection - no OOM faults will be injected until after 
**   TESTMEM_CTRL_FAULTCONFIG is called again.
**
** TESTMEM_CTRL_FAULTREPORT:
**   Used to query the OOM fault injector. The third and fourth arguments
**   should both be of type (int*). Before returning, the value pointed
**   to by the third argument is set to the total number of faults injected
**   since the most recent call to FAULTCONFIG. The location pointed to
**   by the fourth argument is set to the number of "benign" faults that 
**   occurred.
*/
#define TESTMEM_CTRL_REPORT         62930001
#define TESTMEM_CTRL_FAULTCONFIG    62930002
#define TESTMEM_CTRL_FAULTREPORT    62930003

sqlite4_mm *test_mm_debug(sqlite4_mm *p);

sqlite4_mm *test_mm_faultsim(sqlite4_mm *p);

#endif

Changes to test/test_malloc.c.

1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148

1149
1150
1151
1152
1153
1154
1155

1156
1157
1158
1159
1160
1161
1162
....
1165
1166
1167
1168
1169
1170
1171



1172
1173
1174
1175
1176
1177
1178



1179
1180
1181
1182
1183
1184
1185
....
1241
1242
1243
1244
1245
1246
1247

1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262


1263

























































1264
1265
1266
1267
1268
1269
1270
....
1296
1297
1298
1299
1300
1301
1302


1303
1304
1305
1306
1307
1308
1309
1310
1311
#endif
  Tcl_SetResult(interp, (char *)sqlite4TestErrorName(rc), TCL_VOLATILE);
  return TCL_OK;
}

#endif

static sqlite4_mm *pMMDebug = 0;

/*
** tclcmd: test_mm_install ?debug? ?backtrace? ?stats?
*/
static int test_mm_install(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  const char *azOpt[] = { 
    "debug",                      /* 0 */
    "backtrace",                  /* 1 */
    "stats",                      /* 2 */

    0 
  };
  sqlite4_mm *pMM = 0;
  int i;
  int bDebug = 0;
  int bBacktrace = 0;
  int bStats = 0;


  for(i=1; i<objc; i++){
    int iOpt = 0;
    int rc = Tcl_GetIndexFromObj(interp, objv[i], azOpt, "option", 0, &iOpt); 
    if( rc!=TCL_OK ) return rc;

    switch( iOpt ){
................................................................................
        break;
      case 1:
        bBacktrace = 1;
        break;
      case 2:
        bStats = 1;
        break;



    }
  }

  sqlite4_env_config(0, SQLITE4_ENVCONFIG_GETMM, &pMM);
  if( pMM==sqlite4_mm_default() ){
    if( bDebug ){
      pMMDebug = pMM = test_mm_debug(pMM);



    }
    if( bStats ){
      pMM = sqlite4_mm_new(SQLITE4_MM_STATS, pMM);
    }

    sqlite4_env_config(0, SQLITE4_ENVCONFIG_SETMM, pMM);
  }
................................................................................
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  FILE *pFile;
  const char *zFile;


  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "FILE");
    return TCL_ERROR;
  }

  if( pMMDebug ){
    zFile = Tcl_GetString(objv[1]);
    pFile = fopen(zFile, "w");
    if( pFile==0 ){
      Tcl_AppendResult(interp, "Failed to open file: ", zFile, 0);
      return TCL_ERROR;
    }

    test_mm_debug_report(pMMDebug, pFile);


  }

























































  return TCL_OK;
}


/*
** Register commands with the TCL interpreter.
*/
................................................................................
     { "sqlite4_db_config_lookaside",    test_db_config_lookaside      ,0 },
     { "sqlite4_install_memsys3",        test_install_memsys3          ,0 },
#endif

     { "test_mm_install",                test_mm_install               ,0 },
     { "test_mm_stat",                   test_mm_stat                  ,0 },
     { "test_mm_report",                 test_mm_report                ,0 },


  };
  int i;
  for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
    ClientData c = (ClientData)SQLITE4_INT_TO_PTR(aObjCmd[i].clientData);
    Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0);
  }
  return TCL_OK;
}
#endif







<
<













>







>







 







>
>
>






|
>
>
>







 







>






<
|
|
|
|
|
|

|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>









1127
1128
1129
1130
1131
1132
1133


1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
....
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
....
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260

1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
....
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
#endif
  Tcl_SetResult(interp, (char *)sqlite4TestErrorName(rc), TCL_VOLATILE);
  return TCL_OK;
}

#endif



/*
** tclcmd: test_mm_install ?debug? ?backtrace? ?stats?
*/
static int test_mm_install(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  const char *azOpt[] = { 
    "debug",                      /* 0 */
    "backtrace",                  /* 1 */
    "stats",                      /* 2 */
    "faultsim",                   /* 3 */
    0 
  };
  sqlite4_mm *pMM = 0;
  int i;
  int bDebug = 0;
  int bBacktrace = 0;
  int bStats = 0;
  int bFaultsim = 0;

  for(i=1; i<objc; i++){
    int iOpt = 0;
    int rc = Tcl_GetIndexFromObj(interp, objv[i], azOpt, "option", 0, &iOpt); 
    if( rc!=TCL_OK ) return rc;

    switch( iOpt ){
................................................................................
        break;
      case 1:
        bBacktrace = 1;
        break;
      case 2:
        bStats = 1;
        break;
      case 3:
        bFaultsim = 1;
        break;
    }
  }

  sqlite4_env_config(0, SQLITE4_ENVCONFIG_GETMM, &pMM);
  if( pMM==sqlite4_mm_default() ){
    if( bDebug ){
      pMM = test_mm_debug(pMM);
    }
    if( bFaultsim ){
      pMM = test_mm_faultsim(pMM);
    }
    if( bStats ){
      pMM = sqlite4_mm_new(SQLITE4_MM_STATS, pMM);
    }

    sqlite4_env_config(0, SQLITE4_ENVCONFIG_SETMM, pMM);
  }
................................................................................
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  FILE *pFile;
  const char *zFile;
  sqlite4_mm *pMM;

  if( objc!=2 ){
    Tcl_WrongNumArgs(interp, 1, objv, "FILE");
    return TCL_ERROR;
  }


  zFile = Tcl_GetString(objv[1]);
  pFile = fopen(zFile, "w");
  if( pFile==0 ){
    Tcl_AppendResult(interp, "Failed to open file: ", zFile, 0);
    return TCL_ERROR;
  }

  sqlite4_env_config(0, SQLITE4_ENVCONFIG_GETMM, &pMM);
  sqlite4_mm_control(pMM, TESTMEM_CTRL_REPORT, pFile);
  fclose(pFile);

  return TCL_OK;
}

/*
** tclcmd: test_mm_faultconfig COUNT PERSISTENT
*/
static int test_mm_faultconfig(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  sqlite4_mm *pMM = 0;
  int iCnt = 0;
  int bPersist = 0;

  if( objc!=3 ){
    Tcl_WrongNumArgs(interp, 1, objv, "COUNT PERSISTENT");
    return TCL_ERROR;
  }

  if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[1], &iCnt)
   || TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[2], &bPersist)
  ){
    return TCL_ERROR;
  }

  sqlite4_env_config(0, SQLITE4_ENVCONFIG_GETMM, &pMM);
  sqlite4_mm_control(pMM, TESTMEM_CTRL_FAULTCONFIG, iCnt, bPersist);
  return TCL_OK;
}

/*
** tclcmd: test_mm_faultreport
*/
static int test_mm_faultreport(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  sqlite4_mm *pMM = 0;
  int nFault = 0;
  int nBenign = 0;
  Tcl_Obj *pRet;

  if( objc!=1 ){
    Tcl_WrongNumArgs(interp, 1, objv, "");
    return TCL_ERROR;
  }

  sqlite4_env_config(0, SQLITE4_ENVCONFIG_GETMM, &pMM);
  sqlite4_mm_control(pMM, TESTMEM_CTRL_FAULTREPORT, &nFault, &nBenign);
  pRet = Tcl_NewObj();
  Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nFault));
  Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nBenign));
  Tcl_SetObjResult(interp, pRet);
  return TCL_OK;
}


/*
** Register commands with the TCL interpreter.
*/
................................................................................
     { "sqlite4_db_config_lookaside",    test_db_config_lookaside      ,0 },
     { "sqlite4_install_memsys3",        test_install_memsys3          ,0 },
#endif

     { "test_mm_install",                test_mm_install               ,0 },
     { "test_mm_stat",                   test_mm_stat                  ,0 },
     { "test_mm_report",                 test_mm_report                ,0 },
     { "test_mm_faultconfig",            test_mm_faultconfig           ,0 },
     { "test_mm_faultreport",            test_mm_faultreport           ,0 },
  };
  int i;
  for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
    ClientData c = (ClientData)SQLITE4_INT_TO_PTR(aObjCmd[i].clientData);
    Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0);
  }
  return TCL_OK;
}
#endif

Changes to test/test_mem.c.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
..
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
...
225
226
227
228
229
230
231








232
233
234
235
236
237
238
...
274
275
276
277
278
279
280












281
282
283
284
285
286
287
...
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
























































































319










































320






















321
322

323

324
325
#include <stdio.h>
#include <assert.h>
#include <string.h>

#include "sqliteInt.h"
#include "testInt.h"

#define ArraySize(x) ((int)(sizeof(x) / sizeof((x)[0])))

#define MIN(x,y) ((x)<(y) ? (x) : (y))

typedef unsigned int  u32;
typedef unsigned char u8;
typedef long long int i64;
typedef unsigned long long int u64;

................................................................................
  extern int backtrace(void**,int);
  extern void backtrace_symbols_fd(void*const*,int,int);
# define TM_BACKTRACE 12
#else
# define backtrace(A,B) 1
# define backtrace_symbols_fd(A,B,C)
#endif


typedef struct TmBlockHdr TmBlockHdr;
typedef struct TmAgg TmAgg;
typedef struct Testmem Testmem;

/*
** The object that implements the sqlite4_mm interface for this allocator.
................................................................................
/*
** sqlite4_mm_methods.xMember method.
*/
static int mmDebugMember(sqlite4_mm *pMM, const void *p){
  Testmem *pTest = (Testmem *)pMM;
  return sqlite4_mm_member(pTest->p, (const void *)userToBlock(p));
}









/*
** sqlite4_mm_methods.xStat method.
*/
static sqlite4_int64 mmDebugStat(
  sqlite4_mm *pMM, 
  unsigned int eType, 
................................................................................
    }
  }
#else
  (void)pFile;
  (void)pTest;
#endif
}













/*
** Destroy the allocator object passed as the first argument.
*/
static void mmDebugFinal(sqlite4_mm *pMM){
  Testmem *pTest = (Testmem *)pMM;
  sqlite4_mm *p = pTest->p;
................................................................................
  static const sqlite4_mm_methods mmDebugMethods = {
    /* iVersion */    1,
    /* xMalloc  */    mmDebugMalloc,
    /* xRealloc */    mmDebugRealloc,
    /* xFree    */    mmDebugFree,
    /* xMsize   */    mmDebugMsize,
    /* xMember  */    mmDebugMember,
    /* xBenign  */    0,
    /* xStat    */    mmDebugStat,
    /* xCtrl    */    0,
    /* xFinal   */    mmDebugFinal
  };
  Testmem *pTest;
  pTest = (Testmem *)sqlite4_mm_malloc(p, sizeof(Testmem));
  if( pTest ){
    memset(pTest, 0, sizeof(Testmem));
    pTest->base.pMethods = &mmDebugMethods;
    pTest->p = p;
  }
  return (sqlite4_mm *)pTest;
}

























































































void test_mm_debug_report(sqlite4_mm *p, FILE *pFile){










































  Testmem *pTest = (Testmem *)p;






















  assert( pTest->base.pMethods->xMalloc==mmDebugMalloc );
  mmDebugReport(pTest, pFile);

}










<
<







 







<







 







>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>







 







|

|












>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<
>
|
>
|

12
13
14
15
16
17
18


19
20
21
22
23
24
25
..
27
28
29
30
31
32
33

34
35
36
37
38
39
40
...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
...
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
...
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490

491
492
493
494
495
#include <stdio.h>
#include <assert.h>
#include <string.h>

#include "sqliteInt.h"
#include "testInt.h"



#define MIN(x,y) ((x)<(y) ? (x) : (y))

typedef unsigned int  u32;
typedef unsigned char u8;
typedef long long int i64;
typedef unsigned long long int u64;

................................................................................
  extern int backtrace(void**,int);
  extern void backtrace_symbols_fd(void*const*,int,int);
# define TM_BACKTRACE 12
#else
# define backtrace(A,B) 1
# define backtrace_symbols_fd(A,B,C)
#endif


typedef struct TmBlockHdr TmBlockHdr;
typedef struct TmAgg TmAgg;
typedef struct Testmem Testmem;

/*
** The object that implements the sqlite4_mm interface for this allocator.
................................................................................
/*
** sqlite4_mm_methods.xMember method.
*/
static int mmDebugMember(sqlite4_mm *pMM, const void *p){
  Testmem *pTest = (Testmem *)pMM;
  return sqlite4_mm_member(pTest->p, (const void *)userToBlock(p));
}

/*
** sqlite4_mm_methods.xBenign method.
*/
static void mmDebugBenign(sqlite4_mm *pMM, int bBenign){
  Testmem *pTest = (Testmem *)pMM;
  sqlite4_mm_benign_failures(pTest->p, bBenign);
}

/*
** sqlite4_mm_methods.xStat method.
*/
static sqlite4_int64 mmDebugStat(
  sqlite4_mm *pMM, 
  unsigned int eType, 
................................................................................
    }
  }
#else
  (void)pFile;
  (void)pTest;
#endif
}

static int mmDebugCtrl(sqlite4_mm *pMM, unsigned int eType, va_list ap){
  Testmem *pTest = (Testmem *)pMM;
  int rc = SQLITE4_OK;
  if( eType==TESTMEM_CTRL_REPORT ){
    FILE *pFile = va_arg(ap, FILE*);
    mmDebugReport(pTest, pFile);
  }else{
    rc = sqlite4_mm_control_va(pTest->p, eType, ap);
  }
  return rc;
}

/*
** Destroy the allocator object passed as the first argument.
*/
static void mmDebugFinal(sqlite4_mm *pMM){
  Testmem *pTest = (Testmem *)pMM;
  sqlite4_mm *p = pTest->p;
................................................................................
  static const sqlite4_mm_methods mmDebugMethods = {
    /* iVersion */    1,
    /* xMalloc  */    mmDebugMalloc,
    /* xRealloc */    mmDebugRealloc,
    /* xFree    */    mmDebugFree,
    /* xMsize   */    mmDebugMsize,
    /* xMember  */    mmDebugMember,
    /* xBenign  */    mmDebugBenign,
    /* xStat    */    mmDebugStat,
    /* xCtrl    */    mmDebugCtrl,
    /* xFinal   */    mmDebugFinal
  };
  Testmem *pTest;
  pTest = (Testmem *)sqlite4_mm_malloc(p, sizeof(Testmem));
  if( pTest ){
    memset(pTest, 0, sizeof(Testmem));
    pTest->base.pMethods = &mmDebugMethods;
    pTest->p = p;
  }
  return (sqlite4_mm *)pTest;
}

/**************************************************************************
** Start of "Fault MM" implementation.
*/
typedef struct FaultMM FaultMM;
struct FaultMM {
  sqlite4_mm base;                /* Base class. Must be first. */
  sqlite4_mm *p;                  /* Underlying allocator object */
  sqlite4_mutex *mutex;           /* Mutex protecting this object (or NULL) */

  int iCnt;                       /* First FAULTCONFIG parameter */
  int bPersistent;                /* Second FAULTCONFIG parameter */
  int bBenign;                    /* Most recent value passed to xBenign */
  int nFault;                     /* Number of faults that have occurred */
  int nBenignFault;               /* Number of benign faults */
};

static int injectOOM(FaultMM *pFault){
  if( pFault->nFault && pFault->bPersistent ) return 1;
  if( pFault->iCnt>0 ){
    pFault->iCnt--;
    if( pFault->iCnt==0 ) return 1;
  }
  return 0;
}

/*
** sqlite4_mm_methods.xMalloc method.
*/
static void *mmFaultMalloc(sqlite4_mm *pMM, sqlite4_size_t nByte){
  FaultMM *pFault = (FaultMM *)pMM;
  if( injectOOM(pFault) ){
    pFault->nFault++;
    if( pFault->bBenign ) pFault->nBenignFault++;
    return 0;
  }
  return sqlite4_mm_malloc(pFault->p, nByte);
}

/*
** sqlite4_mm_methods.xFree method.
*/
static void mmFaultFree(sqlite4_mm *pMM, void *p){
  FaultMM *pFault = (FaultMM *)pMM;
  sqlite4_mm_free(pFault->p, p);
}

/*
** sqlite4_mm_methods.xRealloc method.
*/
static void *mmFaultRealloc(sqlite4_mm *pMM, void *p, int nByte){
  FaultMM *pFault = (FaultMM *)pMM;
  if( injectOOM(pFault) ){
    pFault->nFault++;
    if( pFault->bBenign ) pFault->nBenignFault++;
    return 0;
  }
  return sqlite4_mm_realloc(pFault->p, p, nByte);
}

/*
** sqlite4_mm_methods.xMsize method.
*/
static sqlite4_size_t mmFaultMsize(sqlite4_mm *pMM, void *p){
  FaultMM *pFault = (FaultMM *)pMM;
  return sqlite4_mm_msize(pFault->p, p);
}

/*
** sqlite4_mm_methods.xMember method.
*/
static int mmFaultMember(sqlite4_mm *pMM, const void *p){
  FaultMM *pFault = (FaultMM *)pMM;
  return sqlite4_mm_member(pFault->p, p);
}

/*
** sqlite4_mm_methods.xBenign method.
*/
static void mmFaultBenign(sqlite4_mm *pMM, int bBenign){
  FaultMM *pFault = (FaultMM *)pMM;
  pFault->bBenign = bBenign;
  sqlite4_mm_benign_failures(pFault->p, bBenign);
}

/*
** sqlite4_mm_methods.xStat method.
*/
static sqlite4_int64 mmFaultStat(
  sqlite4_mm *pMM, 
  unsigned int eType, 
  unsigned int flags
){
  FaultMM *pFault = (FaultMM *)pMM;
  return sqlite4_mm_stat(pFault->p, eType, flags);
}

static int mmFaultCtrl(sqlite4_mm *pMM, unsigned int eType, va_list ap){
  FaultMM *pFault = (FaultMM *)pMM;
  int rc = SQLITE4_OK;

  switch( eType ){
    case TESTMEM_CTRL_FAULTCONFIG: {
      int iCnt = va_arg(ap, int);
      int bPersistent = va_arg(ap, int);
      pFault->iCnt = iCnt;
      pFault->bPersistent = bPersistent;
      pFault->nFault = 0;
      pFault->nBenignFault = 0;
      break;
    }

    case TESTMEM_CTRL_FAULTREPORT: {
      int *pnFault = va_arg(ap, int*);
      int *pnBenign = va_arg(ap, int*);
      *pnFault = pFault->nFault;
      *pnBenign = pFault->nBenignFault;
      break;
    }

    default:
      rc = sqlite4_mm_control_va(pFault->p, eType, ap);
      break;
  }

  return rc;
}

/*
** Destroy the allocator object passed as the first argument.
*/
static void mmFaultFinal(sqlite4_mm *pMM){
  Testmem *pTest = (Testmem *)pMM;
  sqlite4_mm *p = pTest->p;
  sqlite4_mm_free(p, (void *)pTest);
  sqlite4_mm_destroy(p);
}

sqlite4_mm *test_mm_faultsim(sqlite4_mm *p){
  static const sqlite4_mm_methods mmFaultMethods = {
    /* iVersion */    1,
    /* xMalloc  */    mmFaultMalloc,
    /* xRealloc */    mmFaultRealloc,
    /* xFree    */    mmFaultFree,
    /* xMsize   */    mmFaultMsize,
    /* xMember  */    mmFaultMember,
    /* xBenign  */    mmFaultBenign,
    /* xStat    */    mmFaultStat,
    /* xCtrl    */    mmFaultCtrl,
    /* xFinal   */    mmFaultFinal
  };
  Testmem *pTest;
  pTest = (Testmem *)sqlite4_mm_malloc(p, sizeof(Testmem));
  if( pTest ){
    memset(pTest, 0, sizeof(Testmem));
    pTest->base.pMethods = &mmFaultMethods;

    pTest->p = p;
  }
  return (sqlite4_mm *)pTest;
}

Changes to test/tester.tcl.

360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
    }
  }
  set argv $leftover

  # Install the various malloc wrappers. This only needs to be done 
  # once for the process.
  sqlite4_shutdown 
  set mm stats
  if {$cmdlinearg(malloctrace)} { lappend mm debug }
  test_mm_install {*}$mm


  # install_malloc_faultsim 1 
  kvwrap install
  #autoinstall_test_functions







|







360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
    }
  }
  set argv $leftover

  # Install the various malloc wrappers. This only needs to be done 
  # once for the process.
  sqlite4_shutdown 
  set mm [list stats faultsim]
  if {$cmdlinearg(malloctrace)} { lappend mm debug }
  test_mm_install {*}$mm


  # install_malloc_faultsim 1 
  kvwrap install
  #autoinstall_test_functions