blob: 0c963ad4000665d7b01954953d40fdd7b2a8a404 [file] [log] [blame]
drh2f999a62007-08-15 19:16:43 +00001/*
2** 2007 August 15
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 code used to implement test interfaces to the
14** memory allocation subsystem.
15**
drhec424a52008-07-25 15:39:03 +000016** $Id: test_malloc.c,v 1.41 2008/07/25 15:39:04 drh Exp $
drh2f999a62007-08-15 19:16:43 +000017*/
18#include "sqliteInt.h"
19#include "tcl.h"
20#include <stdlib.h>
21#include <string.h>
22#include <assert.h>
23
danielk1977ef05f2d2008-06-20 11:05:37 +000024/*
25** This structure is used to encapsulate the global state variables used
26** by malloc() fault simulation.
27*/
28static struct MemFault {
29 int iCountdown; /* Number of pending successes before a failure */
30 int nRepeat; /* Number of times to repeat the failure */
31 int nBenign; /* Number of benign failures seen since last config */
32 int nFail; /* Number of failures seen since last config */
33 u8 enable; /* True if enabled */
34 int isInstalled; /* True if the fault simulation layer is installed */
danielk19772d1d86f2008-06-20 14:59:51 +000035 int isBenignMode; /* True if malloc failures are considered benign */
danielk1977ef05f2d2008-06-20 11:05:37 +000036 sqlite3_mem_methods m; /* 'Real' malloc implementation */
37} memfault;
38
39/*
40** This routine exists as a place to set a breakpoint that will
41** fire on any simulated malloc() failure.
42*/
43static void sqlite3Fault(void){
44 static int cnt = 0;
45 cnt++;
46}
47
48/*
49** Check to see if a fault should be simulated. Return true to simulate
50** the fault. Return false if the fault should not be simulated.
51*/
52static int faultsimStep(){
53 if( likely(!memfault.enable) ){
54 return 0;
55 }
56 if( memfault.iCountdown>0 ){
57 memfault.iCountdown--;
58 return 0;
59 }
60 sqlite3Fault();
61 memfault.nFail++;
danielk19772d1d86f2008-06-20 14:59:51 +000062 if( memfault.isBenignMode>0 ){
danielk1977ef05f2d2008-06-20 11:05:37 +000063 memfault.nBenign++;
64 }
65 memfault.nRepeat--;
66 if( memfault.nRepeat<=0 ){
67 memfault.enable = 0;
68 }
69 return 1;
70}
71
72/*
73** A version of sqlite3_mem_methods.xMalloc() that includes fault simulation
74** logic.
75*/
76static void *faultsimMalloc(int n){
77 void *p = 0;
78 if( !faultsimStep() ){
79 p = memfault.m.xMalloc(n);
80 }
81 return p;
82}
83
84
85/*
86** A version of sqlite3_mem_methods.xRealloc() that includes fault simulation
87** logic.
88*/
89static void *faultsimRealloc(void *pOld, int n){
90 void *p = 0;
91 if( !faultsimStep() ){
92 p = memfault.m.xRealloc(pOld, n);
93 }
94 return p;
95}
96
97/*
98** The following method calls are passed directly through to the underlying
99** malloc system:
100**
101** xFree
102** xSize
103** xRoundup
104** xInit
105** xShutdown
106*/
107static void faultsimFree(void *p){
108 memfault.m.xFree(p);
109}
110static int faultsimSize(void *p){
111 return memfault.m.xSize(p);
112}
113static int faultsimRoundup(int n){
114 return memfault.m.xRoundup(n);
115}
116static int faultsimInit(void *p){
117 return memfault.m.xInit(memfault.m.pAppData);
118}
119static void faultsimShutdown(void *p){
120 memfault.m.xShutdown(memfault.m.pAppData);
121}
122
123/*
124** This routine configures the malloc failure simulation. After
125** calling this routine, the next nDelay mallocs will succeed, followed
126** by a block of nRepeat failures, after which malloc() calls will begin
127** to succeed again.
128*/
129static void faultsimConfig(int nDelay, int nRepeat){
130 memfault.iCountdown = nDelay;
131 memfault.nRepeat = nRepeat;
132 memfault.nBenign = 0;
133 memfault.nFail = 0;
134 memfault.enable = nDelay>=0;
135}
136
137/*
138** Return the number of faults (both hard and benign faults) that have
139** occurred since the injector was last configured.
140*/
141static int faultsimFailures(void){
142 return memfault.nFail;
143}
144
145/*
146** Return the number of benign faults that have occurred since the
147** injector was last configured.
148*/
149static int faultsimBenignFailures(void){
150 return memfault.nBenign;
151}
152
153/*
154** Return the number of successes that will occur before the next failure.
155** If no failures are scheduled, return -1.
156*/
157static int faultsimPending(void){
158 if( memfault.enable ){
159 return memfault.iCountdown;
160 }else{
161 return -1;
162 }
163}
164
danielk19772d1d86f2008-06-20 14:59:51 +0000165
166static void faultsimBeginBenign(void){
167 memfault.isBenignMode++;
168}
169static void faultsimEndBenign(void){
170 memfault.isBenignMode--;
171}
172
danielk1977ef05f2d2008-06-20 11:05:37 +0000173/*
174** Add or remove the fault-simulation layer using sqlite3_config(). If
175** the argument is non-zero, the
176*/
177static int faultsimInstall(int install){
178 static struct sqlite3_mem_methods m = {
179 faultsimMalloc, /* xMalloc */
180 faultsimFree, /* xFree */
181 faultsimRealloc, /* xRealloc */
182 faultsimSize, /* xSize */
183 faultsimRoundup, /* xRoundup */
184 faultsimInit, /* xInit */
185 faultsimShutdown, /* xShutdown */
186 0 /* pAppData */
187 };
188 int rc;
189
190 install = (install ? 1 : 0);
191 assert(memfault.isInstalled==1 || memfault.isInstalled==0);
192
193 if( install==memfault.isInstalled ){
194 return SQLITE_ERROR;
195 }
196
danielk19772d1d86f2008-06-20 14:59:51 +0000197 if( install ){
198 rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memfault.m);
199 assert(memfault.m.xMalloc);
200 if( rc==SQLITE_OK ){
201 rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &m);
202 }
203 sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,
204 faultsimBeginBenign, faultsimEndBenign
205 );
206 }else{
207 assert(memfault.m.xMalloc);
208 rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memfault.m);
209 sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, 0, 0);
danielk1977ef05f2d2008-06-20 11:05:37 +0000210 }
211
212 if( rc==SQLITE_OK ){
213 memfault.isInstalled = 1;
214 }
215 return rc;
216}
217
218#ifdef SQLITE_TEST
219
220/*
221** This function is implemented in test1.c. Returns a pointer to a static
222** buffer containing the symbolic SQLite error code that corresponds to
223** the least-significant 8-bits of the integer passed as an argument.
224** For example:
225**
226** sqlite3TestErrorName(1) -> "SQLITE_ERROR"
227*/
danielk1977d09414c2008-06-19 18:17:49 +0000228const char *sqlite3TestErrorName(int);
229
drh2f999a62007-08-15 19:16:43 +0000230/*
231** Transform pointers to text and back again
232*/
233static void pointerToText(void *p, char *z){
234 static const char zHex[] = "0123456789abcdef";
235 int i, k;
drh4a50aac2007-08-23 02:47:53 +0000236 unsigned int u;
237 sqlite3_uint64 n;
drh8a42cbd2008-07-10 18:13:42 +0000238 if( p==0 ){
239 strcpy(z, "0");
240 return;
241 }
drh4a50aac2007-08-23 02:47:53 +0000242 if( sizeof(n)==sizeof(p) ){
243 memcpy(&n, &p, sizeof(p));
244 }else if( sizeof(u)==sizeof(p) ){
245 memcpy(&u, &p, sizeof(u));
246 n = u;
247 }else{
248 assert( 0 );
249 }
drh2f999a62007-08-15 19:16:43 +0000250 for(i=0, k=sizeof(p)*2-1; i<sizeof(p)*2; i++, k--){
251 z[k] = zHex[n&0xf];
252 n >>= 4;
253 }
254 z[sizeof(p)*2] = 0;
255}
256static int hexToInt(int h){
257 if( h>='0' && h<='9' ){
258 return h - '0';
259 }else if( h>='a' && h<='f' ){
260 return h - 'a' + 10;
261 }else{
262 return -1;
263 }
264}
265static int textToPointer(const char *z, void **pp){
266 sqlite3_uint64 n = 0;
267 int i;
drh4a50aac2007-08-23 02:47:53 +0000268 unsigned int u;
drh2f999a62007-08-15 19:16:43 +0000269 for(i=0; i<sizeof(void*)*2 && z[0]; i++){
270 int v;
271 v = hexToInt(*z++);
272 if( v<0 ) return TCL_ERROR;
273 n = n*16 + v;
274 }
275 if( *z!=0 ) return TCL_ERROR;
drh4a50aac2007-08-23 02:47:53 +0000276 if( sizeof(n)==sizeof(*pp) ){
277 memcpy(pp, &n, sizeof(n));
278 }else if( sizeof(u)==sizeof(*pp) ){
279 u = (unsigned int)n;
280 memcpy(pp, &u, sizeof(u));
281 }else{
282 assert( 0 );
283 }
drh2f999a62007-08-15 19:16:43 +0000284 return TCL_OK;
285}
286
287/*
288** Usage: sqlite3_malloc NBYTES
289**
290** Raw test interface for sqlite3_malloc().
291*/
292static int test_malloc(
293 void * clientData,
294 Tcl_Interp *interp,
295 int objc,
296 Tcl_Obj *CONST objv[]
297){
298 int nByte;
299 void *p;
300 char zOut[100];
301 if( objc!=2 ){
302 Tcl_WrongNumArgs(interp, 1, objv, "NBYTES");
303 return TCL_ERROR;
304 }
305 if( Tcl_GetIntFromObj(interp, objv[1], &nByte) ) return TCL_ERROR;
306 p = sqlite3_malloc((unsigned)nByte);
307 pointerToText(p, zOut);
308 Tcl_AppendResult(interp, zOut, NULL);
309 return TCL_OK;
310}
311
312/*
313** Usage: sqlite3_realloc PRIOR NBYTES
314**
315** Raw test interface for sqlite3_realloc().
316*/
317static int test_realloc(
318 void * clientData,
319 Tcl_Interp *interp,
320 int objc,
321 Tcl_Obj *CONST objv[]
322){
323 int nByte;
324 void *pPrior, *p;
325 char zOut[100];
326 if( objc!=3 ){
327 Tcl_WrongNumArgs(interp, 1, objv, "PRIOR NBYTES");
328 return TCL_ERROR;
329 }
330 if( Tcl_GetIntFromObj(interp, objv[2], &nByte) ) return TCL_ERROR;
331 if( textToPointer(Tcl_GetString(objv[1]), &pPrior) ){
332 Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
333 return TCL_ERROR;
334 }
335 p = sqlite3_realloc(pPrior, (unsigned)nByte);
336 pointerToText(p, zOut);
337 Tcl_AppendResult(interp, zOut, NULL);
338 return TCL_OK;
339}
340
drh2f999a62007-08-15 19:16:43 +0000341/*
342** Usage: sqlite3_free PRIOR
343**
344** Raw test interface for sqlite3_free().
345*/
346static int test_free(
347 void * clientData,
348 Tcl_Interp *interp,
349 int objc,
350 Tcl_Obj *CONST objv[]
351){
352 void *pPrior;
353 if( objc!=2 ){
354 Tcl_WrongNumArgs(interp, 1, objv, "PRIOR");
355 return TCL_ERROR;
356 }
357 if( textToPointer(Tcl_GetString(objv[1]), &pPrior) ){
358 Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
359 return TCL_ERROR;
360 }
361 sqlite3_free(pPrior);
362 return TCL_OK;
363}
364
365/*
drh9c7a60d2007-10-19 17:47:24 +0000366** These routines are in test_hexio.c
367*/
368int sqlite3TestHexToBin(const char *, int, char *);
369int sqlite3TestBinToHex(char*,int);
370
371/*
372** Usage: memset ADDRESS SIZE HEX
373**
374** Set a chunk of memory (obtained from malloc, probably) to a
375** specified hex pattern.
376*/
377static int test_memset(
378 void * clientData,
379 Tcl_Interp *interp,
380 int objc,
381 Tcl_Obj *CONST objv[]
382){
383 void *p;
384 int size, n, i;
385 char *zHex;
386 char *zOut;
387 char zBin[100];
388
389 if( objc!=4 ){
390 Tcl_WrongNumArgs(interp, 1, objv, "ADDRESS SIZE HEX");
391 return TCL_ERROR;
392 }
393 if( textToPointer(Tcl_GetString(objv[1]), &p) ){
394 Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
395 return TCL_ERROR;
396 }
397 if( Tcl_GetIntFromObj(interp, objv[2], &size) ){
398 return TCL_ERROR;
399 }
400 if( size<=0 ){
401 Tcl_AppendResult(interp, "size must be positive", (char*)0);
402 return TCL_ERROR;
403 }
404 zHex = Tcl_GetStringFromObj(objv[3], &n);
405 if( n>sizeof(zBin)*2 ) n = sizeof(zBin)*2;
406 n = sqlite3TestHexToBin(zHex, n, zBin);
407 if( n==0 ){
408 Tcl_AppendResult(interp, "no data", (char*)0);
409 return TCL_ERROR;
410 }
411 zOut = p;
412 for(i=0; i<size; i++){
413 zOut[i] = zBin[i%n];
414 }
415 return TCL_OK;
416}
417
418/*
419** Usage: memget ADDRESS SIZE
420**
421** Return memory as hexadecimal text.
422*/
423static int test_memget(
424 void * clientData,
425 Tcl_Interp *interp,
426 int objc,
427 Tcl_Obj *CONST objv[]
428){
429 void *p;
430 int size, n;
431 char *zBin;
432 char zHex[100];
433
434 if( objc!=3 ){
435 Tcl_WrongNumArgs(interp, 1, objv, "ADDRESS SIZE");
436 return TCL_ERROR;
437 }
438 if( textToPointer(Tcl_GetString(objv[1]), &p) ){
439 Tcl_AppendResult(interp, "bad pointer: ", Tcl_GetString(objv[1]), (char*)0);
440 return TCL_ERROR;
441 }
442 if( Tcl_GetIntFromObj(interp, objv[2], &size) ){
443 return TCL_ERROR;
444 }
445 if( size<=0 ){
446 Tcl_AppendResult(interp, "size must be positive", (char*)0);
447 return TCL_ERROR;
448 }
449 zBin = p;
450 while( size>0 ){
451 if( size>(sizeof(zHex)-1)/2 ){
452 n = (sizeof(zHex)-1)/2;
453 }else{
454 n = size;
455 }
456 memcpy(zHex, zBin, n);
457 zBin += n;
458 size -= n;
459 sqlite3TestBinToHex(zHex, n);
460 Tcl_AppendResult(interp, zHex, (char*)0);
461 }
462 return TCL_OK;
463}
464
465/*
drh2f999a62007-08-15 19:16:43 +0000466** Usage: sqlite3_memory_used
467**
468** Raw test interface for sqlite3_memory_used().
469*/
470static int test_memory_used(
471 void * clientData,
472 Tcl_Interp *interp,
473 int objc,
474 Tcl_Obj *CONST objv[]
475){
476 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sqlite3_memory_used()));
477 return TCL_OK;
478}
479
480/*
481** Usage: sqlite3_memory_highwater ?RESETFLAG?
482**
483** Raw test interface for sqlite3_memory_highwater().
484*/
485static int test_memory_highwater(
486 void * clientData,
487 Tcl_Interp *interp,
488 int objc,
489 Tcl_Obj *CONST objv[]
490){
491 int resetFlag = 0;
492 if( objc!=1 && objc!=2 ){
493 Tcl_WrongNumArgs(interp, 1, objv, "?RESET?");
494 return TCL_ERROR;
495 }
496 if( objc==2 ){
497 if( Tcl_GetBooleanFromObj(interp, objv[1], &resetFlag) ) return TCL_ERROR;
498 }
499 Tcl_SetObjResult(interp,
500 Tcl_NewWideIntObj(sqlite3_memory_highwater(resetFlag)));
501 return TCL_OK;
502}
503
504/*
505** Usage: sqlite3_memdebug_backtrace DEPTH
506**
507** Set the depth of backtracing. If SQLITE_MEMDEBUG is not defined
508** then this routine is a no-op.
509*/
510static int test_memdebug_backtrace(
511 void * clientData,
512 Tcl_Interp *interp,
513 int objc,
514 Tcl_Obj *CONST objv[]
515){
516 int depth;
517 if( objc!=2 ){
518 Tcl_WrongNumArgs(interp, 1, objv, "DEPT");
519 return TCL_ERROR;
520 }
521 if( Tcl_GetIntFromObj(interp, objv[1], &depth) ) return TCL_ERROR;
522#ifdef SQLITE_MEMDEBUG
523 {
drh49e4fd72008-02-19 15:15:15 +0000524 extern void sqlite3MemdebugBacktrace(int);
525 sqlite3MemdebugBacktrace(depth);
drh2f999a62007-08-15 19:16:43 +0000526 }
527#endif
528 return TCL_OK;
529}
530
531/*
532** Usage: sqlite3_memdebug_dump FILENAME
533**
534** Write a summary of unfreed memory to FILENAME.
535*/
536static int test_memdebug_dump(
537 void * clientData,
538 Tcl_Interp *interp,
539 int objc,
540 Tcl_Obj *CONST objv[]
541){
542 if( objc!=2 ){
543 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
544 return TCL_ERROR;
545 }
drh2d7636e2008-02-16 16:21:45 +0000546#if defined(SQLITE_MEMDEBUG) || defined(SQLITE_MEMORY_SIZE) \
547 || defined(SQLITE_POW2_MEMORY_SIZE)
drh2f999a62007-08-15 19:16:43 +0000548 {
drh49e4fd72008-02-19 15:15:15 +0000549 extern void sqlite3MemdebugDump(const char*);
550 sqlite3MemdebugDump(Tcl_GetString(objv[1]));
drh2f999a62007-08-15 19:16:43 +0000551 }
552#endif
553 return TCL_OK;
554}
555
danielk1977a7a8e142008-02-13 18:25:27 +0000556/*
557** Usage: sqlite3_memdebug_malloc_count
558**
559** Return the total number of times malloc() has been called.
560*/
561static int test_memdebug_malloc_count(
562 void * clientData,
563 Tcl_Interp *interp,
564 int objc,
565 Tcl_Obj *CONST objv[]
566){
567 int nMalloc = -1;
568 if( objc!=1 ){
569 Tcl_WrongNumArgs(interp, 1, objv, "");
570 return TCL_ERROR;
571 }
572#if defined(SQLITE_MEMDEBUG)
573 {
drh49e4fd72008-02-19 15:15:15 +0000574 extern int sqlite3MemdebugMallocCount();
575 nMalloc = sqlite3MemdebugMallocCount();
danielk1977a7a8e142008-02-13 18:25:27 +0000576 }
577#endif
578 Tcl_SetObjResult(interp, Tcl_NewIntObj(nMalloc));
579 return TCL_OK;
580}
581
drh2f999a62007-08-15 19:16:43 +0000582
583/*
danielk1977a1644fd2007-08-29 12:31:25 +0000584** Usage: sqlite3_memdebug_fail COUNTER ?OPTIONS?
585**
586** where options are:
587**
drh643167f2008-01-22 21:30:53 +0000588** -repeat <count>
danielk1977a1644fd2007-08-29 12:31:25 +0000589** -benigncnt <varname>
drh0e6f1542007-08-15 20:41:28 +0000590**
591** Arrange for a simulated malloc() failure after COUNTER successes.
drh643167f2008-01-22 21:30:53 +0000592** If a repeat count is specified, the fault is repeated that many
593** times.
drh0e6f1542007-08-15 20:41:28 +0000594**
595** Each call to this routine overrides the prior counter value.
596** This routine returns the number of simulated failures that have
597** happened since the previous call to this routine.
598**
599** To disable simulated failures, use a COUNTER of -1.
600*/
601static int test_memdebug_fail(
602 void * clientData,
603 Tcl_Interp *interp,
604 int objc,
605 Tcl_Obj *CONST objv[]
606){
danielk1977a1644fd2007-08-29 12:31:25 +0000607 int ii;
drh0e6f1542007-08-15 20:41:28 +0000608 int iFail;
drh643167f2008-01-22 21:30:53 +0000609 int nRepeat = 1;
danielk1977a1644fd2007-08-29 12:31:25 +0000610 Tcl_Obj *pBenignCnt = 0;
drh643167f2008-01-22 21:30:53 +0000611 int nBenign;
drh0e6f1542007-08-15 20:41:28 +0000612 int nFail = 0;
danielk1977a1644fd2007-08-29 12:31:25 +0000613
614 if( objc<2 ){
615 Tcl_WrongNumArgs(interp, 1, objv, "COUNTER ?OPTIONS?");
drh0e6f1542007-08-15 20:41:28 +0000616 return TCL_ERROR;
617 }
618 if( Tcl_GetIntFromObj(interp, objv[1], &iFail) ) return TCL_ERROR;
danielk1977a1644fd2007-08-29 12:31:25 +0000619
620 for(ii=2; ii<objc; ii+=2){
621 int nOption;
622 char *zOption = Tcl_GetStringFromObj(objv[ii], &nOption);
623 char *zErr = 0;
624
625 if( nOption>1 && strncmp(zOption, "-repeat", nOption)==0 ){
626 if( ii==(objc-1) ){
627 zErr = "option requires an argument: ";
628 }else{
drh643167f2008-01-22 21:30:53 +0000629 if( Tcl_GetIntFromObj(interp, objv[ii+1], &nRepeat) ){
danielk1977a1644fd2007-08-29 12:31:25 +0000630 return TCL_ERROR;
631 }
632 }
633 }else if( nOption>1 && strncmp(zOption, "-benigncnt", nOption)==0 ){
634 if( ii==(objc-1) ){
635 zErr = "option requires an argument: ";
636 }else{
637 pBenignCnt = objv[ii+1];
638 }
639 }else{
640 zErr = "unknown option: ";
641 }
642
643 if( zErr ){
644 Tcl_AppendResult(interp, zErr, zOption, 0);
645 return TCL_ERROR;
646 }
drhed138fb2007-08-22 22:04:37 +0000647 }
danielk1977a1644fd2007-08-29 12:31:25 +0000648
danielk1977ef05f2d2008-06-20 11:05:37 +0000649 nBenign = faultsimBenignFailures();
650 nFail = faultsimFailures();
651 faultsimConfig(iFail, nRepeat);
652
drh643167f2008-01-22 21:30:53 +0000653 if( pBenignCnt ){
654 Tcl_ObjSetVar2(interp, pBenignCnt, 0, Tcl_NewIntObj(nBenign), 0);
drh0e6f1542007-08-15 20:41:28 +0000655 }
drh0e6f1542007-08-15 20:41:28 +0000656 Tcl_SetObjResult(interp, Tcl_NewIntObj(nFail));
657 return TCL_OK;
658}
659
danielk1977cd037242007-08-30 15:46:06 +0000660/*
661** Usage: sqlite3_memdebug_pending
662**
663** Return the number of malloc() calls that will succeed before a
664** simulated failure occurs. A negative return value indicates that
665** no malloc() failure is scheduled.
666*/
667static int test_memdebug_pending(
668 void * clientData,
669 Tcl_Interp *interp,
670 int objc,
671 Tcl_Obj *CONST objv[]
672){
drh5efaf072008-03-18 00:07:10 +0000673 int nPending;
danielk1977cd037242007-08-30 15:46:06 +0000674 if( objc!=1 ){
675 Tcl_WrongNumArgs(interp, 1, objv, "");
676 return TCL_ERROR;
677 }
danielk1977ef05f2d2008-06-20 11:05:37 +0000678 nPending = faultsimPending();
drh5efaf072008-03-18 00:07:10 +0000679 Tcl_SetObjResult(interp, Tcl_NewIntObj(nPending));
danielk1977cd037242007-08-30 15:46:06 +0000680 return TCL_OK;
681}
682
drh0e6f1542007-08-15 20:41:28 +0000683
684/*
drh4a50aac2007-08-23 02:47:53 +0000685** Usage: sqlite3_memdebug_settitle TITLE
686**
687** Set a title string stored with each allocation. The TITLE is
688** typically the name of the test that was running when the
689** allocation occurred. The TITLE is stored with the allocation
690** and can be used to figure out which tests are leaking memory.
691**
692** Each title overwrite the previous.
693*/
694static int test_memdebug_settitle(
695 void * clientData,
696 Tcl_Interp *interp,
697 int objc,
698 Tcl_Obj *CONST objv[]
699){
700 const char *zTitle;
701 if( objc!=2 ){
702 Tcl_WrongNumArgs(interp, 1, objv, "TITLE");
703 return TCL_ERROR;
704 }
705 zTitle = Tcl_GetString(objv[1]);
706#ifdef SQLITE_MEMDEBUG
707 {
drh49e4fd72008-02-19 15:15:15 +0000708 extern int sqlite3MemdebugSettitle(const char*);
709 sqlite3MemdebugSettitle(zTitle);
drh4a50aac2007-08-23 02:47:53 +0000710 }
711#endif
712 return TCL_OK;
713}
714
danielk1977cd3e8f72008-03-25 09:47:35 +0000715#define MALLOC_LOG_FRAMES 10
danielk19776f332c12008-03-21 14:22:44 +0000716static Tcl_HashTable aMallocLog;
717static int mallocLogEnabled = 0;
718
719typedef struct MallocLog MallocLog;
720struct MallocLog {
721 int nCall;
722 int nByte;
723};
724
shaneafdd23a2008-05-29 02:57:47 +0000725#ifdef SQLITE_MEMDEBUG
danielk19776f332c12008-03-21 14:22:44 +0000726static void test_memdebug_callback(int nByte, int nFrame, void **aFrame){
727 if( mallocLogEnabled ){
728 MallocLog *pLog;
729 Tcl_HashEntry *pEntry;
730 int isNew;
731
732 int aKey[MALLOC_LOG_FRAMES];
733 int nKey = sizeof(int)*MALLOC_LOG_FRAMES;
734
735 memset(aKey, 0, nKey);
736 if( (sizeof(void*)*nFrame)<nKey ){
737 nKey = nFrame*sizeof(void*);
738 }
739 memcpy(aKey, aFrame, nKey);
740
741 pEntry = Tcl_CreateHashEntry(&aMallocLog, (const char *)aKey, &isNew);
742 if( isNew ){
743 pLog = (MallocLog *)Tcl_Alloc(sizeof(MallocLog));
744 memset(pLog, 0, sizeof(MallocLog));
745 Tcl_SetHashValue(pEntry, (ClientData)pLog);
746 }else{
747 pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
748 }
749
750 pLog->nCall++;
751 pLog->nByte += nByte;
752 }
753}
shaneafdd23a2008-05-29 02:57:47 +0000754#endif /* SQLITE_MEMDEBUG */
danielk19776f332c12008-03-21 14:22:44 +0000755
danielk19775f096132008-03-28 15:44:09 +0000756static void test_memdebug_log_clear(){
danielk1977dbdc4d42008-03-28 07:42:53 +0000757 Tcl_HashSearch search;
758 Tcl_HashEntry *pEntry;
759 for(
760 pEntry=Tcl_FirstHashEntry(&aMallocLog, &search);
761 pEntry;
762 pEntry=Tcl_NextHashEntry(&search)
763 ){
764 MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
765 Tcl_Free((char *)pLog);
766 }
767 Tcl_DeleteHashTable(&aMallocLog);
768 Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_FRAMES);
769}
770
danielk19776f332c12008-03-21 14:22:44 +0000771static int test_memdebug_log(
772 void * clientData,
773 Tcl_Interp *interp,
774 int objc,
775 Tcl_Obj *CONST objv[]
776){
777 static int isInit = 0;
778 int iSub;
779
danielk1977dbdc4d42008-03-28 07:42:53 +0000780 static const char *MB_strs[] = { "start", "stop", "dump", "clear", "sync" };
781 enum MB_enum {
782 MB_LOG_START, MB_LOG_STOP, MB_LOG_DUMP, MB_LOG_CLEAR, MB_LOG_SYNC
783 };
danielk19776f332c12008-03-21 14:22:44 +0000784
785 if( !isInit ){
786#ifdef SQLITE_MEMDEBUG
787 extern void sqlite3MemdebugBacktraceCallback(
788 void (*xBacktrace)(int, int, void **));
789 sqlite3MemdebugBacktraceCallback(test_memdebug_callback);
790#endif
791 Tcl_InitHashTable(&aMallocLog, MALLOC_LOG_FRAMES);
792 isInit = 1;
793 }
794
795 if( objc<2 ){
796 Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
797 }
798 if( Tcl_GetIndexFromObj(interp, objv[1], MB_strs, "sub-command", 0, &iSub) ){
799 return TCL_ERROR;
800 }
801
802 switch( (enum MB_enum)iSub ){
803 case MB_LOG_START:
804 mallocLogEnabled = 1;
805 break;
806 case MB_LOG_STOP:
807 mallocLogEnabled = 0;
808 break;
809 case MB_LOG_DUMP: {
810 Tcl_HashSearch search;
811 Tcl_HashEntry *pEntry;
812 Tcl_Obj *pRet = Tcl_NewObj();
813
814 assert(sizeof(int)==sizeof(void*));
815
816 for(
817 pEntry=Tcl_FirstHashEntry(&aMallocLog, &search);
818 pEntry;
819 pEntry=Tcl_NextHashEntry(&search)
820 ){
821 Tcl_Obj *apElem[MALLOC_LOG_FRAMES+2];
822 MallocLog *pLog = (MallocLog *)Tcl_GetHashValue(pEntry);
823 int *aKey = (int *)Tcl_GetHashKey(&aMallocLog, pEntry);
824 int ii;
825
826 apElem[0] = Tcl_NewIntObj(pLog->nCall);
827 apElem[1] = Tcl_NewIntObj(pLog->nByte);
828 for(ii=0; ii<MALLOC_LOG_FRAMES; ii++){
829 apElem[ii+2] = Tcl_NewIntObj(aKey[ii]);
830 }
831
832 Tcl_ListObjAppendElement(interp, pRet,
833 Tcl_NewListObj(MALLOC_LOG_FRAMES+2, apElem)
834 );
835 }
836
837 Tcl_SetObjResult(interp, pRet);
838 break;
839 }
840 case MB_LOG_CLEAR: {
danielk1977dbdc4d42008-03-28 07:42:53 +0000841 test_memdebug_log_clear();
842 break;
843 }
844
845 case MB_LOG_SYNC: {
drhb9404922008-03-28 12:53:38 +0000846#ifdef SQLITE_MEMDEBUG
danielk1977dbdc4d42008-03-28 07:42:53 +0000847 extern void sqlite3MemdebugSync();
848 test_memdebug_log_clear();
849 mallocLogEnabled = 1;
850 sqlite3MemdebugSync();
drhb9404922008-03-28 12:53:38 +0000851#endif
danielk1977dbdc4d42008-03-28 07:42:53 +0000852 break;
danielk19776f332c12008-03-21 14:22:44 +0000853 }
854 }
855
856 return TCL_OK;
857}
drh4a50aac2007-08-23 02:47:53 +0000858
859/*
drh9ac3fe92008-06-18 18:12:04 +0000860** Usage: sqlite3_config_scratch SIZE N
861**
862** Set the scratch memory buffer using SQLITE_CONFIG_SCRATCH.
863** The buffer is static and is of limited size. N might be
864** adjusted downward as needed to accomodate the requested size.
865** The revised value of N is returned.
866**
867** A negative SIZE causes the buffer pointer to be NULL.
868*/
869static int test_config_scratch(
870 void * clientData,
871 Tcl_Interp *interp,
872 int objc,
873 Tcl_Obj *CONST objv[]
874){
875 int sz, N, rc;
876 Tcl_Obj *pResult;
drhf7141992008-06-19 00:16:08 +0000877 static char buf[30000];
drh9ac3fe92008-06-18 18:12:04 +0000878 if( objc!=3 ){
879 Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
880 return TCL_ERROR;
881 }
drhf7141992008-06-19 00:16:08 +0000882 if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
883 if( Tcl_GetIntFromObj(interp, objv[2], &N) ) return TCL_ERROR;
drh9ac3fe92008-06-18 18:12:04 +0000884 if( sz<0 ){
885 rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, 0, 0, 0);
drhf7141992008-06-19 00:16:08 +0000886 }else{
drh9ac3fe92008-06-18 18:12:04 +0000887 int mx = sizeof(buf)/(sz+4);
888 if( N>mx ) N = mx;
889 rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, buf, sz, N);
890 }
891 pResult = Tcl_NewObj();
892 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
893 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
894 Tcl_SetObjResult(interp, pResult);
895 return TCL_OK;
896}
897
898/*
899** Usage: sqlite3_config_pagecache SIZE N
900**
901** Set the page-cache memory buffer using SQLITE_CONFIG_PAGECACHE.
902** The buffer is static and is of limited size. N might be
903** adjusted downward as needed to accomodate the requested size.
904** The revised value of N is returned.
905**
906** A negative SIZE causes the buffer pointer to be NULL.
907*/
908static int test_config_pagecache(
909 void * clientData,
910 Tcl_Interp *interp,
911 int objc,
912 Tcl_Obj *CONST objv[]
913){
914 int sz, N, rc;
915 Tcl_Obj *pResult;
916 static char buf[100000];
917 if( objc!=3 ){
918 Tcl_WrongNumArgs(interp, 1, objv, "SIZE N");
919 return TCL_ERROR;
920 }
drhf7141992008-06-19 00:16:08 +0000921 if( Tcl_GetIntFromObj(interp, objv[1], &sz) ) return TCL_ERROR;
922 if( Tcl_GetIntFromObj(interp, objv[2], &N) ) return TCL_ERROR;
drh9ac3fe92008-06-18 18:12:04 +0000923 if( sz<0 ){
drhf7141992008-06-19 00:16:08 +0000924 rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, 0, 0, 0);
925 }else{
drh9ac3fe92008-06-18 18:12:04 +0000926 int mx = sizeof(buf)/(sz+4);
927 if( N>mx ) N = mx;
928 rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, buf, sz, N);
929 }
930 pResult = Tcl_NewObj();
931 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
932 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
933 Tcl_SetObjResult(interp, pResult);
934 return TCL_OK;
935}
936
drhf7141992008-06-19 00:16:08 +0000937/*
drh8a42cbd2008-07-10 18:13:42 +0000938** Usage: sqlite3_config_memstatus BOOLEAN
939**
940** Enable or disable memory status reporting using SQLITE_CONFIG_MEMSTATUS.
941*/
942static int test_config_memstatus(
943 void * clientData,
944 Tcl_Interp *interp,
945 int objc,
946 Tcl_Obj *CONST objv[]
947){
948 int enable, rc;
949 if( objc!=2 ){
950 Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
951 return TCL_ERROR;
952 }
953 if( Tcl_GetBooleanFromObj(interp, objv[1], &enable) ) return TCL_ERROR;
954 rc = sqlite3_config(SQLITE_CONFIG_MEMSTATUS, enable);
955 Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
956 return TCL_OK;
957}
958
959/*
danielk19772d340812008-07-24 08:20:40 +0000960** Usage: sqlite3_config_chunkalloc
961**
962*/
963static int test_config_chunkalloc(
964 void * clientData,
965 Tcl_Interp *interp,
966 int objc,
967 Tcl_Obj *CONST objv[]
968){
969 int rc;
danielk197731fab4f2008-07-25 08:48:59 +0000970 int nThreshold;
971 if( objc!=2 ){
972 Tcl_WrongNumArgs(interp, 1, objv, "THRESHOLD");
danielk19772d340812008-07-24 08:20:40 +0000973 return TCL_ERROR;
974 }
danielk197731fab4f2008-07-25 08:48:59 +0000975 if( Tcl_GetIntFromObj(interp, objv[1], &nThreshold) ) return TCL_ERROR;
976 rc = sqlite3_config(SQLITE_CONFIG_CHUNKALLOC, nThreshold);
danielk19772d340812008-07-24 08:20:40 +0000977 Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
978 return TCL_OK;
979}
980
981/*
danielk19775099be52008-06-27 13:27:03 +0000982** Usage:
983**
drh8a42cbd2008-07-10 18:13:42 +0000984** sqlite3_config_heap NBYTE NMINALLOC
danielk19775099be52008-06-27 13:27:03 +0000985*/
986static int test_config_heap(
987 void * clientData,
988 Tcl_Interp *interp,
989 int objc,
990 Tcl_Obj *CONST objv[]
991){
drh7830cd42008-07-16 12:25:32 +0000992 static char *zBuf; /* Use this memory */
993 static int szBuf; /* Bytes allocated for zBuf */
danielk19775099be52008-06-27 13:27:03 +0000994 int nByte; /* Size of buffer to pass to sqlite3_config() */
995 int nMinAlloc; /* Size of minimum allocation */
996 int rc; /* Return code of sqlite3_config() */
danielk19775099be52008-06-27 13:27:03 +0000997
998 Tcl_Obj * CONST *aArg = &objv[1];
999 int nArg = objc-1;
1000
danielk19775099be52008-06-27 13:27:03 +00001001 if( nArg!=2 ){
drh8a42cbd2008-07-10 18:13:42 +00001002 Tcl_WrongNumArgs(interp, 1, objv, "NBYTE NMINALLOC");
danielk19775099be52008-06-27 13:27:03 +00001003 return TCL_ERROR;
1004 }
1005 if( Tcl_GetIntFromObj(interp, aArg[0], &nByte) ) return TCL_ERROR;
1006 if( Tcl_GetIntFromObj(interp, aArg[1], &nMinAlloc) ) return TCL_ERROR;
1007
danielk19770d84e5b2008-06-27 14:05:24 +00001008 if( nByte==0 ){
drh7830cd42008-07-16 12:25:32 +00001009 free( zBuf );
1010 zBuf = 0;
1011 szBuf = 0;
drh8a42cbd2008-07-10 18:13:42 +00001012 rc = sqlite3_config(SQLITE_CONFIG_HEAP, (void*)0, 0, 0);
danielk19770d84e5b2008-06-27 14:05:24 +00001013 }else{
drh7830cd42008-07-16 12:25:32 +00001014 zBuf = realloc(zBuf, nByte);
1015 szBuf = nByte;
danielk19770d84e5b2008-06-27 14:05:24 +00001016 rc = sqlite3_config(SQLITE_CONFIG_HEAP, zBuf, nByte, nMinAlloc);
danielk19775099be52008-06-27 13:27:03 +00001017 }
danielk19775099be52008-06-27 13:27:03 +00001018
1019 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
1020 return TCL_OK;
1021}
1022
1023/*
danielk1977c66c0e12008-06-25 14:26:07 +00001024** Usage:
1025**
1026** sqlite3_dump_memsys3 FILENAME
1027** sqlite3_dump_memsys5 FILENAME
danielk197732155ef2008-06-25 10:34:34 +00001028**
1029** Write a summary of unfreed memsys3 allocations to FILENAME.
1030*/
1031static int test_dump_memsys3(
1032 void * clientData,
1033 Tcl_Interp *interp,
1034 int objc,
1035 Tcl_Obj *CONST objv[]
1036){
1037 if( objc!=2 ){
1038 Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
1039 return TCL_ERROR;
1040 }
danielk1977c66c0e12008-06-25 14:26:07 +00001041
1042 switch( (int)clientData ){
danielk19770d84e5b2008-06-27 14:05:24 +00001043 case 3: {
danielk1977c66c0e12008-06-25 14:26:07 +00001044#ifdef SQLITE_ENABLE_MEMSYS3
1045 extern void sqlite3Memsys3Dump(const char*);
1046 sqlite3Memsys3Dump(Tcl_GetString(objv[1]));
1047 break;
danielk197732155ef2008-06-25 10:34:34 +00001048#endif
danielk1977c66c0e12008-06-25 14:26:07 +00001049 }
danielk19770d84e5b2008-06-27 14:05:24 +00001050 case 5: {
danielk1977c66c0e12008-06-25 14:26:07 +00001051#ifdef SQLITE_ENABLE_MEMSYS5
1052 extern void sqlite3Memsys5Dump(const char*);
1053 sqlite3Memsys5Dump(Tcl_GetString(objv[1]));
1054 break;
1055#endif
1056 }
1057 }
danielk197732155ef2008-06-25 10:34:34 +00001058 return TCL_OK;
1059}
1060
1061/*
drhf7141992008-06-19 00:16:08 +00001062** Usage: sqlite3_status OPCODE RESETFLAG
1063**
1064** Return a list of three elements which are the sqlite3_status() return
1065** code, the current value, and the high-water mark value.
1066*/
1067static int test_status(
1068 void * clientData,
1069 Tcl_Interp *interp,
1070 int objc,
1071 Tcl_Obj *CONST objv[]
1072){
1073 int rc, iValue, mxValue;
1074 int i, op, resetFlag;
1075 const char *zOpName;
1076 static const struct {
1077 const char *zName;
1078 int op;
1079 } aOp[] = {
1080 { "SQLITE_STATUS_MEMORY_USED", SQLITE_STATUS_MEMORY_USED },
1081 { "SQLITE_STATUS_PAGECACHE_USED", SQLITE_STATUS_PAGECACHE_USED },
1082 { "SQLITE_STATUS_PAGECACHE_OVERFLOW", SQLITE_STATUS_PAGECACHE_OVERFLOW },
1083 { "SQLITE_STATUS_SCRATCH_USED", SQLITE_STATUS_SCRATCH_USED },
1084 { "SQLITE_STATUS_SCRATCH_OVERFLOW", SQLITE_STATUS_SCRATCH_OVERFLOW },
1085 { "SQLITE_STATUS_MALLOC_SIZE", SQLITE_STATUS_MALLOC_SIZE },
drhec424a52008-07-25 15:39:03 +00001086 { "SQLITE_STATUS_PARSER_STACK", SQLITE_STATUS_PARSER_STACK },
drhf7141992008-06-19 00:16:08 +00001087 };
1088 Tcl_Obj *pResult;
1089 if( objc!=3 ){
1090 Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG");
1091 return TCL_ERROR;
1092 }
1093 zOpName = Tcl_GetString(objv[1]);
1094 for(i=0; i<ArraySize(aOp); i++){
1095 if( strcmp(aOp[i].zName, zOpName)==0 ){
1096 op = aOp[i].op;
1097 break;
1098 }
1099 }
1100 if( i>=ArraySize(aOp) ){
1101 if( Tcl_GetIntFromObj(interp, objv[1], &op) ) return TCL_ERROR;
1102 }
1103 if( Tcl_GetBooleanFromObj(interp, objv[2], &resetFlag) ) return TCL_ERROR;
drhaf005fb2008-07-09 16:51:51 +00001104 iValue = 0;
1105 mxValue = 0;
drhf7141992008-06-19 00:16:08 +00001106 rc = sqlite3_status(op, &iValue, &mxValue, resetFlag);
1107 pResult = Tcl_NewObj();
1108 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
1109 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(iValue));
1110 Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(mxValue));
1111 Tcl_SetObjResult(interp, pResult);
1112 return TCL_OK;
1113}
drh9ac3fe92008-06-18 18:12:04 +00001114
1115/*
danielk1977d09414c2008-06-19 18:17:49 +00001116** install_malloc_faultsim BOOLEAN
1117*/
1118static int test_install_malloc_faultsim(
1119 void * clientData,
1120 Tcl_Interp *interp,
1121 int objc,
1122 Tcl_Obj *CONST objv[]
1123){
1124 int rc;
1125 int isInstall;
1126
1127 if( objc!=2 ){
1128 Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
1129 return TCL_ERROR;
1130 }
1131 if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){
1132 return TCL_ERROR;
1133 }
danielk1977ef05f2d2008-06-20 11:05:37 +00001134 rc = faultsimInstall(isInstall);
danielk1977d09414c2008-06-19 18:17:49 +00001135 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
1136 return TCL_OK;
1137}
1138
1139/*
drh2f999a62007-08-15 19:16:43 +00001140** Register commands with the TCL interpreter.
1141*/
1142int Sqlitetest_malloc_Init(Tcl_Interp *interp){
1143 static struct {
1144 char *zName;
1145 Tcl_ObjCmdProc *xProc;
danielk1977c66c0e12008-06-25 14:26:07 +00001146 int clientData;
drh2f999a62007-08-15 19:16:43 +00001147 } aObjCmd[] = {
drh8a42cbd2008-07-10 18:13:42 +00001148 { "sqlite3_malloc", test_malloc ,0 },
1149 { "sqlite3_realloc", test_realloc ,0 },
1150 { "sqlite3_free", test_free ,0 },
1151 { "memset", test_memset ,0 },
1152 { "memget", test_memget ,0 },
1153 { "sqlite3_memory_used", test_memory_used ,0 },
1154 { "sqlite3_memory_highwater", test_memory_highwater ,0 },
1155 { "sqlite3_memdebug_backtrace", test_memdebug_backtrace ,0 },
1156 { "sqlite3_memdebug_dump", test_memdebug_dump ,0 },
1157 { "sqlite3_memdebug_fail", test_memdebug_fail ,0 },
1158 { "sqlite3_memdebug_pending", test_memdebug_pending ,0 },
1159 { "sqlite3_memdebug_settitle", test_memdebug_settitle ,0 },
1160 { "sqlite3_memdebug_malloc_count", test_memdebug_malloc_count ,0 },
1161 { "sqlite3_memdebug_log", test_memdebug_log ,0 },
1162 { "sqlite3_config_scratch", test_config_scratch ,0 },
1163 { "sqlite3_config_pagecache", test_config_pagecache ,0 },
1164 { "sqlite3_status", test_status ,0 },
1165 { "install_malloc_faultsim", test_install_malloc_faultsim ,0 },
danielk19770d84e5b2008-06-27 14:05:24 +00001166 { "sqlite3_config_heap", test_config_heap ,0 },
drh8a42cbd2008-07-10 18:13:42 +00001167 { "sqlite3_config_memstatus", test_config_memstatus ,0 },
danielk19772d340812008-07-24 08:20:40 +00001168 { "sqlite3_config_chunkalloc", test_config_chunkalloc ,0 },
danielk19770d84e5b2008-06-27 14:05:24 +00001169 { "sqlite3_dump_memsys3", test_dump_memsys3 ,3 },
1170 { "sqlite3_dump_memsys5", test_dump_memsys3 ,5 }
drh2f999a62007-08-15 19:16:43 +00001171 };
1172 int i;
1173 for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
danielk1977c66c0e12008-06-25 14:26:07 +00001174 ClientData c = (ClientData)aObjCmd[i].clientData;
1175 Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0);
drh2f999a62007-08-15 19:16:43 +00001176 }
1177 return TCL_OK;
1178}
danielk1977ef05f2d2008-06-20 11:05:37 +00001179#endif