2 ** A utility for printing content from a write-ahead log file.
10 #if !defined(_MSC_VER)
20 static int pagesize
= 1024; /* Size of a database page */
21 static int fd
= -1; /* File descriptor for reading the WAL file */
22 static int mxFrame
= 0; /* Last frame */
23 static int perLine
= 16; /* HEX elements to print per line */
25 typedef long long int i64
; /* Datatype for 64-bit integers */
27 /* Information for computing the checksum */
28 typedef struct Cksum Cksum
;
30 int bSwap
; /* True to do byte swapping on 32-bit words */
31 unsigned s0
, s1
; /* Current checksum value */
35 ** extract a 32-bit big-endian integer
37 static unsigned int getInt32(const unsigned char *a
){
38 unsigned int x
= (a
[0]<<24) + (a
[1]<<16) + (a
[2]<<8) + a
[3];
43 ** Swap bytes on a 32-bit unsigned integer
45 static unsigned int swab32(unsigned int x
){
46 return (((x
)&0x000000FF)<<24) + (((x
)&0x0000FF00)<<8)
47 + (((x
)&0x00FF0000)>>8) + (((x
)&0xFF000000)>>24);
50 /* Extend the checksum. Reinitialize the checksum if bInit is true.
52 static void extendCksum(
63 /* Host is little-endian */
64 pCksum
->bSwap
= getInt32(aData
)!=0x377f0682;
66 /* Host is big-endian */
67 pCksum
->bSwap
= getInt32(aData
)!=0x377f0683;
72 a32
= (unsigned int*)aData
;
74 unsigned int x0
= a32
[0];
75 unsigned int x1
= a32
[1];
80 pCksum
->s0
+= x0
+ pCksum
->s1
;
81 pCksum
->s1
+= x1
+ pCksum
->s0
;
88 ** Convert the var-int format into i64. Return the number of bytes
89 ** in the var-int. Write the var-int value into *pVal.
91 static int decodeVarint(const unsigned char *z
, i64
*pVal
){
95 v
= (v
<<7) + (z
[i
]&0x7f);
96 if( (z
[i
]&0x80)==0 ){ *pVal
= v
; return i
+1; }
98 v
= (v
<<8) + (z
[i
]&0xff);
103 /* Report an out-of-memory error and die.
105 static void out_of_memory(void){
106 fprintf(stderr
,"Out of memory...\n");
111 ** Read content from the file.
113 ** Space to hold the content is obtained from malloc() and needs to be
114 ** freed by the caller.
116 static unsigned char *getContent(int ofst
, int nByte
){
117 unsigned char *aData
;
118 aData
= malloc(nByte
);
119 if( aData
==0 ) out_of_memory();
120 lseek(fd
, ofst
, SEEK_SET
);
121 read(fd
, aData
, nByte
);
126 ** Print a range of bytes as hex and as ascii.
128 static void print_byte_range(
129 int ofst
, /* First byte in the range of bytes to print */
130 int nByte
, /* Number of bytes to print */
131 unsigned char *aData
, /* Content to print */
132 int printOfst
/* Add this amount to the index on the left column */
135 const char *zOfstFmt
;
137 if( ((printOfst
+nByte
)&~0xfff)==0 ){
138 zOfstFmt
= " %03x: ";
139 }else if( ((printOfst
+nByte
)&~0xffff)==0 ){
140 zOfstFmt
= " %04x: ";
141 }else if( ((printOfst
+nByte
)&~0xfffff)==0 ){
142 zOfstFmt
= " %05x: ";
143 }else if( ((printOfst
+nByte
)&~0xffffff)==0 ){
144 zOfstFmt
= " %06x: ";
146 zOfstFmt
= " %08x: ";
149 for(i
=0; i
<nByte
; i
+= perLine
){
150 fprintf(stdout
, zOfstFmt
, i
+printOfst
);
151 for(j
=0; j
<perLine
; j
++){
153 fprintf(stdout
, " ");
155 fprintf(stdout
,"%02x ", aData
[i
+j
]);
158 for(j
=0; j
<perLine
; j
++){
160 fprintf(stdout
, " ");
162 fprintf(stdout
,"%c", isprint(aData
[i
+j
]) ? aData
[i
+j
] : '.');
165 fprintf(stdout
,"\n");
169 /* Print a line of decode output showing a 4-byte integer.
171 static void print_decode_line(
172 unsigned char *aData
, /* Content being decoded */
173 int ofst
, int nByte
, /* Start and size of decode */
174 int asHex
, /* If true, output value as hex */
175 const char *zMsg
/* Message to append */
178 int val
= aData
[ofst
];
180 sprintf(zBuf
, " %03x: %02x", ofst
, aData
[ofst
]);
181 i
= (int)strlen(zBuf
);
184 sprintf(&zBuf
[i
], " ");
186 sprintf(&zBuf
[i
], " %02x", aData
[ofst
+j
]);
187 val
= val
*256 + aData
[ofst
+j
];
189 i
+= (int)strlen(&zBuf
[i
]);
192 sprintf(&zBuf
[i
], " 0x%08x", val
);
194 sprintf(&zBuf
[i
], " %9d", val
);
196 printf("%s %s\n", zBuf
, zMsg
);
200 ** Print an entire page of content as hex
202 static void print_frame(int iFrame
){
204 unsigned char *aData
;
205 iStart
= 32 + (iFrame
-1)*(pagesize
+24);
206 fprintf(stdout
, "Frame %d: (offsets 0x%x..0x%x)\n",
207 iFrame
, iStart
, iStart
+pagesize
+24);
208 aData
= getContent(iStart
, pagesize
+24);
209 print_decode_line(aData
, 0, 4, 0, "Page number");
210 print_decode_line(aData
, 4, 4, 0, "DB size, or 0 for non-commit");
211 print_decode_line(aData
, 8, 4, 1, "Salt-1");
212 print_decode_line(aData
,12, 4, 1, "Salt-2");
213 print_decode_line(aData
,16, 4, 1, "Checksum-1");
214 print_decode_line(aData
,20, 4, 1, "Checksum-2");
215 print_byte_range(iStart
+24, pagesize
, aData
+24, 0);
220 ** Summarize a single frame on a single line.
222 static void print_oneline_frame(int iFrame
, Cksum
*pCksum
){
224 unsigned char *aData
;
226 iStart
= 32 + (iFrame
-1)*(pagesize
+24);
227 aData
= getContent(iStart
, 24);
228 extendCksum(pCksum
, aData
, 8, 0);
229 extendCksum(pCksum
, getContent(iStart
+24, pagesize
), pagesize
, 0);
230 s0
= getInt32(aData
+16);
231 s1
= getInt32(aData
+20);
232 fprintf(stdout
, "Frame %4d: %6d %6d 0x%08x,%08x 0x%08x,%08x %s\n",
240 (s0
==pCksum
->s0
&& s1
==pCksum
->s1
) ? "" : "cksum-fail"
243 /* Reset the checksum so that a single frame checksum failure will not
244 ** cause all subsequent frames to also show a failure. */
251 ** Decode the WAL header.
253 static void print_wal_header(Cksum
*pCksum
){
254 unsigned char *aData
;
255 aData
= getContent(0, 32);
257 extendCksum(pCksum
, aData
, 24, 1);
258 printf("Checksum byte order: %s\n", pCksum
->bSwap
? "swapped" : "native");
260 printf("WAL Header:\n");
261 print_decode_line(aData
, 0, 4,1,"Magic. 0x377f0682 (le) or 0x377f0683 (be)");
262 print_decode_line(aData
, 4, 4, 0, "File format");
263 print_decode_line(aData
, 8, 4, 0, "Database page size");
264 print_decode_line(aData
, 12,4, 0, "Checkpoint sequence number");
265 print_decode_line(aData
, 16,4, 1, "Salt-1");
266 print_decode_line(aData
, 20,4, 1, "Salt-2");
267 print_decode_line(aData
, 24,4, 1, "Checksum-1");
268 print_decode_line(aData
, 28,4, 1, "Checksum-2");
270 if( pCksum
->s0
!=getInt32(aData
+24) ){
271 printf("**** cksum-1 mismatch: 0x%08x\n", pCksum
->s0
);
273 if( pCksum
->s1
!=getInt32(aData
+28) ){
274 printf("**** cksum-2 mismatch: 0x%08x\n", pCksum
->s1
);
280 ** Describe cell content.
282 static i64
describeContent(
283 unsigned char *a
, /* Cell content */
284 i64 nLocal
, /* Bytes in a[] */
285 char *zDesc
/* Write description here */
290 const unsigned char *pData
;
291 const unsigned char *pLimit
;
295 n
= decodeVarint(a
, &x
);
299 while( i
>0 && pData
<=pLimit
){
300 n
= decodeVarint(a
, &x
);
309 sprintf(zDesc
, "*"); /* NULL is a "*" */
310 }else if( x
>=1 && x
<=6 ){
311 v
= (signed char)pData
[0];
314 case 6: v
= (v
<<16) + (pData
[0]<<8) + pData
[1]; pData
+= 2;
315 case 5: v
= (v
<<16) + (pData
[0]<<8) + pData
[1]; pData
+= 2;
316 case 4: v
= (v
<<8) + pData
[0]; pData
++;
317 case 3: v
= (v
<<8) + pData
[0]; pData
++;
318 case 2: v
= (v
<<8) + pData
[0]; pData
++;
320 sprintf(zDesc
, "%lld", v
);
322 sprintf(zDesc
, "real");
331 sprintf(zDesc
, "blob(%lld)", size
);
333 sprintf(zDesc
, "txt(%lld)", size
);
337 j
= (int)strlen(zDesc
);
345 ** Compute the local payload size given the total payload size and
348 static i64
localPayload(i64 nPayload
, char cType
){
355 maxLocal
= pagesize
-35;
356 minLocal
= (pagesize
-12)*32/255-23;
358 maxLocal
= (pagesize
-12)*64/255-23;
359 minLocal
= (pagesize
-12)*32/255-23;
361 if( nPayload
>maxLocal
){
362 surplus
= minLocal
+ (nPayload
-minLocal
)%(pagesize
-4);
363 if( surplus
<=maxLocal
){
375 ** Create a description for a single cell.
377 ** The return value is the local cell size.
379 static i64
describeCell(
380 unsigned char cType
, /* Page type */
381 unsigned char *a
, /* Cell content */
382 int showCellContent
, /* Show cell content if true */
383 char **pzDesc
/* Store description here */
392 static char zDesc
[1000];
395 leftChild
= ((a
[0]*256 + a
[1])*256 + a
[2])*256 + a
[3];
398 sprintf(zDesc
, "lx: %d ", leftChild
);
399 nDesc
= strlen(zDesc
);
402 i
= decodeVarint(a
, &nPayload
);
405 sprintf(&zDesc
[nDesc
], "n: %lld ", nPayload
);
406 nDesc
+= strlen(&zDesc
[nDesc
]);
407 nLocal
= localPayload(nPayload
, cType
);
409 nPayload
= nLocal
= 0;
411 if( cType
==5 || cType
==13 ){
412 i
= decodeVarint(a
, &rowid
);
415 sprintf(&zDesc
[nDesc
], "r: %lld ", rowid
);
416 nDesc
+= strlen(&zDesc
[nDesc
]);
418 if( nLocal
<nPayload
){
420 unsigned char *b
= &a
[nLocal
];
421 ovfl
= ((b
[0]*256 + b
[1])*256 + b
[2])*256 + b
[3];
422 sprintf(&zDesc
[nDesc
], "ov: %d ", ovfl
);
423 nDesc
+= strlen(&zDesc
[nDesc
]);
426 if( showCellContent
&& cType
!=5 ){
427 nDesc
+= describeContent(a
, nLocal
, &zDesc
[nDesc
-1]);
434 ** Decode a btree page
436 static void decode_btree_page(
437 unsigned char *a
, /* Content of the btree page to be decoded */
438 int pgno
, /* Page number */
439 int hdrSize
, /* Size of the page1-header in bytes */
440 const char *zArgs
/* Flags to control formatting */
442 const char *zType
= "unknown";
446 int showCellContent
= 0;
450 case 2: zType
= "index interior node"; break;
451 case 5: zType
= "table interior node"; break;
452 case 10: zType
= "index leaf"; break;
453 case 13: zType
= "table leaf"; break;
457 case 'c': showCellContent
= 1; break;
458 case 'm': showMap
= 1; break;
462 printf("Decode of btree page %d:\n", pgno
);
463 print_decode_line(a
, 0, 1, 0, zType
);
464 print_decode_line(a
, 1, 2, 0, "Offset to first freeblock");
465 print_decode_line(a
, 3, 2, 0, "Number of cells on this page");
466 nCell
= a
[3]*256 + a
[4];
467 print_decode_line(a
, 5, 2, 0, "Offset to cell content area");
468 print_decode_line(a
, 7, 1, 0, "Fragmented byte count");
469 if( a
[0]==2 || a
[0]==5 ){
470 print_decode_line(a
, 8, 4, 0, "Right child");
476 printf(" key: lx=left-child n=payload-size r=rowid\n");
479 zMap
= malloc(pagesize
);
480 memset(zMap
, '.', pagesize
);
481 memset(zMap
, '1', hdrSize
);
482 memset(&zMap
[hdrSize
], 'H', iCellPtr
);
483 memset(&zMap
[hdrSize
+iCellPtr
], 'P', 2*nCell
);
485 for(i
=0; i
<nCell
; i
++){
486 int cofst
= iCellPtr
+ i
*2;
490 cofst
= a
[cofst
]*256 + a
[cofst
+1];
491 n
= describeCell(a
[0], &a
[cofst
-hdrSize
], showCellContent
, &zDesc
);
494 memset(&zMap
[cofst
], '*', (size_t)n
);
496 zMap
[cofst
+n
-1] = ']';
497 sprintf(zBuf
, "%d", i
);
498 j
= (int)strlen(zBuf
);
499 if( j
<=n
-2 ) memcpy(&zMap
[cofst
+1], zBuf
, j
);
501 printf(" %03x: cell[%d] %s\n", cofst
, i
, zDesc
);
504 for(i
=0; i
<pagesize
; i
+=64){
505 printf(" %03x: %.64s\n", i
, &zMap
[i
]);
511 int main(int argc
, char **argv
){
513 unsigned char zPgSz
[4];
515 fprintf(stderr
,"Usage: %s FILENAME ?PAGE? ...\n", argv
[0]);
518 fd
= open(argv
[1], O_RDONLY
);
520 fprintf(stderr
,"%s: can't open %s\n", argv
[0], argv
[1]);
525 lseek(fd
, 8, SEEK_SET
);
527 pagesize
= zPgSz
[1]*65536 + zPgSz
[2]*256 + zPgSz
[3];
528 if( pagesize
==0 ) pagesize
= 1024;
529 printf("Pagesize: %d\n", pagesize
);
531 if( sbuf
.st_size
<32 ){
532 printf("file too small to be a WAL\n");
535 mxFrame
= (sbuf
.st_size
- 32)/(pagesize
+ 24);
536 printf("Available pages: 1..%d\n", mxFrame
);
540 print_wal_header(&x
);
541 for(i
=1; i
<=mxFrame
; i
++){
542 print_oneline_frame(i
, &x
);
546 for(i
=2; i
<argc
; i
++){
549 if( strcmp(argv
[i
], "header")==0 ){
553 if( !isdigit(argv
[i
][0]) ){
554 fprintf(stderr
, "%s: unknown option: [%s]\n", argv
[0], argv
[i
]);
557 iStart
= strtol(argv
[i
], &zLeft
, 0);
558 if( zLeft
&& strcmp(zLeft
,"..end")==0 ){
560 }else if( zLeft
&& zLeft
[0]=='.' && zLeft
[1]=='.' ){
561 iEnd
= strtol(&zLeft
[2], 0, 0);
562 }else if( zLeft
&& zLeft
[0]=='b' ){
563 int ofst
, nByte
, hdrSize
;
567 ofst
= hdrSize
= 100;
568 nByte
= pagesize
-100;
571 ofst
= (iStart
-1)*pagesize
;
574 ofst
= 32 + hdrSize
+ (iStart
-1)*(pagesize
+24) + 24;
575 a
= getContent(ofst
, nByte
);
576 decode_btree_page(a
, iStart
, hdrSize
, zLeft
+1);
582 if( iStart
<1 || iEnd
<iStart
|| iEnd
>mxFrame
){
584 "Page argument should be LOWER?..UPPER?. Range 1 to %d\n",
588 while( iStart
<=iEnd
){