2 ** A utility for printing content from a write-ahead log file.
10 #define ISDIGIT(X) isdigit((unsigned char)(X))
11 #define ISPRINT(X) isprint((unsigned char)(X))
13 #if !defined(_MSC_VER)
23 static int pagesize
= 1024; /* Size of a database page */
24 static int fd
= -1; /* File descriptor for reading the WAL file */
25 static int mxFrame
= 0; /* Last frame */
26 static int perLine
= 16; /* HEX elements to print per line */
28 typedef long long int i64
; /* Datatype for 64-bit integers */
30 /* Information for computing the checksum */
31 typedef struct Cksum Cksum
;
33 int bSwap
; /* True to do byte swapping on 32-bit words */
34 unsigned s0
, s1
; /* Current checksum value */
38 ** extract a 32-bit big-endian integer
40 static unsigned int getInt32(const unsigned char *a
){
41 unsigned int x
= (a
[0]<<24) + (a
[1]<<16) + (a
[2]<<8) + a
[3];
46 ** Swap bytes on a 32-bit unsigned integer
48 static unsigned int swab32(unsigned int x
){
49 return (((x
)&0x000000FF)<<24) + (((x
)&0x0000FF00)<<8)
50 + (((x
)&0x00FF0000)>>8) + (((x
)&0xFF000000)>>24);
53 /* Extend the checksum. Reinitialize the checksum if bInit is true.
55 static void extendCksum(
66 /* Host is little-endian */
67 pCksum
->bSwap
= getInt32(aData
)!=0x377f0682;
69 /* Host is big-endian */
70 pCksum
->bSwap
= getInt32(aData
)!=0x377f0683;
75 a32
= (unsigned int*)aData
;
77 unsigned int x0
= a32
[0];
78 unsigned int x1
= a32
[1];
83 pCksum
->s0
+= x0
+ pCksum
->s1
;
84 pCksum
->s1
+= x1
+ pCksum
->s0
;
91 ** Convert the var-int format into i64. Return the number of bytes
92 ** in the var-int. Write the var-int value into *pVal.
94 static int decodeVarint(const unsigned char *z
, i64
*pVal
){
98 v
= (v
<<7) + (z
[i
]&0x7f);
99 if( (z
[i
]&0x80)==0 ){ *pVal
= v
; return i
+1; }
101 v
= (v
<<8) + (z
[i
]&0xff);
106 /* Report an out-of-memory error and die.
108 static void out_of_memory(void){
109 fprintf(stderr
,"Out of memory...\n");
114 ** Read content from the file.
116 ** Space to hold the content is obtained from malloc() and needs to be
117 ** freed by the caller.
119 static unsigned char *getContent(int ofst
, int nByte
){
120 unsigned char *aData
;
121 aData
= malloc(nByte
);
122 if( aData
==0 ) out_of_memory();
123 lseek(fd
, ofst
, SEEK_SET
);
124 read(fd
, aData
, nByte
);
129 ** Print a range of bytes as hex and as ascii.
131 static void print_byte_range(
132 int ofst
, /* First byte in the range of bytes to print */
133 int nByte
, /* Number of bytes to print */
134 unsigned char *aData
, /* Content to print */
135 int printOfst
/* Add this amount to the index on the left column */
138 const char *zOfstFmt
;
140 if( ((printOfst
+nByte
)&~0xfff)==0 ){
141 zOfstFmt
= " %03x: ";
142 }else if( ((printOfst
+nByte
)&~0xffff)==0 ){
143 zOfstFmt
= " %04x: ";
144 }else if( ((printOfst
+nByte
)&~0xfffff)==0 ){
145 zOfstFmt
= " %05x: ";
146 }else if( ((printOfst
+nByte
)&~0xffffff)==0 ){
147 zOfstFmt
= " %06x: ";
149 zOfstFmt
= " %08x: ";
152 for(i
=0; i
<nByte
; i
+= perLine
){
153 fprintf(stdout
, zOfstFmt
, i
+printOfst
);
154 for(j
=0; j
<perLine
; j
++){
156 fprintf(stdout
, " ");
158 fprintf(stdout
,"%02x ", aData
[i
+j
]);
161 for(j
=0; j
<perLine
; j
++){
163 fprintf(stdout
, " ");
165 fprintf(stdout
,"%c", ISPRINT(aData
[i
+j
]) ? aData
[i
+j
] : '.');
168 fprintf(stdout
,"\n");
172 /* Print a line of decode output showing a 4-byte integer.
174 static void print_decode_line(
175 unsigned char *aData
, /* Content being decoded */
176 int ofst
, int nByte
, /* Start and size of decode */
177 int asHex
, /* If true, output value as hex */
178 const char *zMsg
/* Message to append */
181 int val
= aData
[ofst
];
183 sprintf(zBuf
, " %03x: %02x", ofst
, aData
[ofst
]);
184 i
= (int)strlen(zBuf
);
187 sprintf(&zBuf
[i
], " ");
189 sprintf(&zBuf
[i
], " %02x", aData
[ofst
+j
]);
190 val
= val
*256 + aData
[ofst
+j
];
192 i
+= (int)strlen(&zBuf
[i
]);
195 sprintf(&zBuf
[i
], " 0x%08x", val
);
197 sprintf(&zBuf
[i
], " %9d", val
);
199 printf("%s %s\n", zBuf
, zMsg
);
203 ** Print an entire page of content as hex
205 static void print_frame(int iFrame
){
207 unsigned char *aData
;
208 iStart
= 32 + (iFrame
-1)*(pagesize
+24);
209 fprintf(stdout
, "Frame %d: (offsets 0x%x..0x%x)\n",
210 iFrame
, iStart
, iStart
+pagesize
+24);
211 aData
= getContent(iStart
, pagesize
+24);
212 print_decode_line(aData
, 0, 4, 0, "Page number");
213 print_decode_line(aData
, 4, 4, 0, "DB size, or 0 for non-commit");
214 print_decode_line(aData
, 8, 4, 1, "Salt-1");
215 print_decode_line(aData
,12, 4, 1, "Salt-2");
216 print_decode_line(aData
,16, 4, 1, "Checksum-1");
217 print_decode_line(aData
,20, 4, 1, "Checksum-2");
218 print_byte_range(iStart
+24, pagesize
, aData
+24, 0);
223 ** Summarize a single frame on a single line.
225 static void print_oneline_frame(int iFrame
, Cksum
*pCksum
){
227 unsigned char *aData
;
229 iStart
= 32 + (iFrame
-1)*(pagesize
+24);
230 aData
= getContent(iStart
, 24);
231 extendCksum(pCksum
, aData
, 8, 0);
232 extendCksum(pCksum
, getContent(iStart
+24, pagesize
), pagesize
, 0);
233 s0
= getInt32(aData
+16);
234 s1
= getInt32(aData
+20);
235 fprintf(stdout
, "Frame %4d: %6d %6d 0x%08x,%08x 0x%08x,%08x %s\n",
243 (s0
==pCksum
->s0
&& s1
==pCksum
->s1
) ? "" : "cksum-fail"
246 /* Reset the checksum so that a single frame checksum failure will not
247 ** cause all subsequent frames to also show a failure. */
254 ** Decode the WAL header.
256 static void print_wal_header(Cksum
*pCksum
){
257 unsigned char *aData
;
258 aData
= getContent(0, 32);
260 extendCksum(pCksum
, aData
, 24, 1);
261 printf("Checksum byte order: %s\n", pCksum
->bSwap
? "swapped" : "native");
263 printf("WAL Header:\n");
264 print_decode_line(aData
, 0, 4,1,"Magic. 0x377f0682 (le) or 0x377f0683 (be)");
265 print_decode_line(aData
, 4, 4, 0, "File format");
266 print_decode_line(aData
, 8, 4, 0, "Database page size");
267 print_decode_line(aData
, 12,4, 0, "Checkpoint sequence number");
268 print_decode_line(aData
, 16,4, 1, "Salt-1");
269 print_decode_line(aData
, 20,4, 1, "Salt-2");
270 print_decode_line(aData
, 24,4, 1, "Checksum-1");
271 print_decode_line(aData
, 28,4, 1, "Checksum-2");
273 if( pCksum
->s0
!=getInt32(aData
+24) ){
274 printf("**** cksum-1 mismatch: 0x%08x\n", pCksum
->s0
);
276 if( pCksum
->s1
!=getInt32(aData
+28) ){
277 printf("**** cksum-2 mismatch: 0x%08x\n", pCksum
->s1
);
283 ** Describe cell content.
285 static i64
describeContent(
286 unsigned char *a
, /* Cell content */
287 i64 nLocal
, /* Bytes in a[] */
288 char *zDesc
/* Write description here */
293 const unsigned char *pData
;
294 const unsigned char *pLimit
;
298 n
= decodeVarint(a
, &x
);
302 while( i
>0 && pData
<=pLimit
){
303 n
= decodeVarint(a
, &x
);
312 sprintf(zDesc
, "*"); /* NULL is a "*" */
313 }else if( x
>=1 && x
<=6 ){
314 v
= (signed char)pData
[0];
317 case 6: v
= (v
<<16) + (pData
[0]<<8) + pData
[1]; pData
+= 2;
318 case 5: v
= (v
<<16) + (pData
[0]<<8) + pData
[1]; pData
+= 2;
319 case 4: v
= (v
<<8) + pData
[0]; pData
++;
320 case 3: v
= (v
<<8) + pData
[0]; pData
++;
321 case 2: v
= (v
<<8) + pData
[0]; pData
++;
323 sprintf(zDesc
, "%lld", v
);
325 sprintf(zDesc
, "real");
334 sprintf(zDesc
, "blob(%lld)", size
);
336 sprintf(zDesc
, "txt(%lld)", size
);
340 j
= (int)strlen(zDesc
);
348 ** Compute the local payload size given the total payload size and
351 static i64
localPayload(i64 nPayload
, char cType
){
358 maxLocal
= pagesize
-35;
359 minLocal
= (pagesize
-12)*32/255-23;
361 maxLocal
= (pagesize
-12)*64/255-23;
362 minLocal
= (pagesize
-12)*32/255-23;
364 if( nPayload
>maxLocal
){
365 surplus
= minLocal
+ (nPayload
-minLocal
)%(pagesize
-4);
366 if( surplus
<=maxLocal
){
378 ** Create a description for a single cell.
380 ** The return value is the local cell size.
382 static i64
describeCell(
383 unsigned char cType
, /* Page type */
384 unsigned char *a
, /* Cell content */
385 int showCellContent
, /* Show cell content if true */
386 char **pzDesc
/* Store description here */
395 static char zDesc
[1000];
398 leftChild
= ((a
[0]*256 + a
[1])*256 + a
[2])*256 + a
[3];
401 sprintf(zDesc
, "lx: %d ", leftChild
);
402 nDesc
= strlen(zDesc
);
405 i
= decodeVarint(a
, &nPayload
);
408 sprintf(&zDesc
[nDesc
], "n: %lld ", nPayload
);
409 nDesc
+= strlen(&zDesc
[nDesc
]);
410 nLocal
= localPayload(nPayload
, cType
);
412 nPayload
= nLocal
= 0;
414 if( cType
==5 || cType
==13 ){
415 i
= decodeVarint(a
, &rowid
);
418 sprintf(&zDesc
[nDesc
], "r: %lld ", rowid
);
419 nDesc
+= strlen(&zDesc
[nDesc
]);
421 if( nLocal
<nPayload
){
423 unsigned char *b
= &a
[nLocal
];
424 ovfl
= ((b
[0]*256 + b
[1])*256 + b
[2])*256 + b
[3];
425 sprintf(&zDesc
[nDesc
], "ov: %d ", ovfl
);
426 nDesc
+= strlen(&zDesc
[nDesc
]);
429 if( showCellContent
&& cType
!=5 ){
430 nDesc
+= describeContent(a
, nLocal
, &zDesc
[nDesc
-1]);
437 ** Decode a btree page
439 static void decode_btree_page(
440 unsigned char *a
, /* Content of the btree page to be decoded */
441 int pgno
, /* Page number */
442 int hdrSize
, /* Size of the page1-header in bytes */
443 const char *zArgs
/* Flags to control formatting */
445 const char *zType
= "unknown";
449 int showCellContent
= 0;
453 case 2: zType
= "index interior node"; break;
454 case 5: zType
= "table interior node"; break;
455 case 10: zType
= "index leaf"; break;
456 case 13: zType
= "table leaf"; break;
460 case 'c': showCellContent
= 1; break;
461 case 'm': showMap
= 1; break;
465 printf("Decode of btree page %d:\n", pgno
);
466 print_decode_line(a
, 0, 1, 0, zType
);
467 print_decode_line(a
, 1, 2, 0, "Offset to first freeblock");
468 print_decode_line(a
, 3, 2, 0, "Number of cells on this page");
469 nCell
= a
[3]*256 + a
[4];
470 print_decode_line(a
, 5, 2, 0, "Offset to cell content area");
471 print_decode_line(a
, 7, 1, 0, "Fragmented byte count");
472 if( a
[0]==2 || a
[0]==5 ){
473 print_decode_line(a
, 8, 4, 0, "Right child");
479 printf(" key: lx=left-child n=payload-size r=rowid\n");
482 zMap
= malloc(pagesize
);
483 memset(zMap
, '.', pagesize
);
484 memset(zMap
, '1', hdrSize
);
485 memset(&zMap
[hdrSize
], 'H', iCellPtr
);
486 memset(&zMap
[hdrSize
+iCellPtr
], 'P', 2*nCell
);
488 for(i
=0; i
<nCell
; i
++){
489 int cofst
= iCellPtr
+ i
*2;
493 cofst
= a
[cofst
]*256 + a
[cofst
+1];
494 n
= describeCell(a
[0], &a
[cofst
-hdrSize
], showCellContent
, &zDesc
);
497 memset(&zMap
[cofst
], '*', (size_t)n
);
499 zMap
[cofst
+n
-1] = ']';
500 sprintf(zBuf
, "%d", i
);
501 j
= (int)strlen(zBuf
);
502 if( j
<=n
-2 ) memcpy(&zMap
[cofst
+1], zBuf
, j
);
504 printf(" %03x: cell[%d] %s\n", cofst
, i
, zDesc
);
507 for(i
=0; i
<pagesize
; i
+=64){
508 printf(" %03x: %.64s\n", i
, &zMap
[i
]);
514 int main(int argc
, char **argv
){
516 unsigned char zPgSz
[4];
518 fprintf(stderr
,"Usage: %s FILENAME ?PAGE? ...\n", argv
[0]);
521 fd
= open(argv
[1], O_RDONLY
);
523 fprintf(stderr
,"%s: can't open %s\n", argv
[0], argv
[1]);
528 lseek(fd
, 8, SEEK_SET
);
530 pagesize
= zPgSz
[1]*65536 + zPgSz
[2]*256 + zPgSz
[3];
531 if( pagesize
==0 ) pagesize
= 1024;
532 printf("Pagesize: %d\n", pagesize
);
534 if( sbuf
.st_size
<32 ){
535 printf("file too small to be a WAL\n");
538 mxFrame
= (sbuf
.st_size
- 32)/(pagesize
+ 24);
539 printf("Available pages: 1..%d\n", mxFrame
);
543 print_wal_header(&x
);
544 for(i
=1; i
<=mxFrame
; i
++){
545 print_oneline_frame(i
, &x
);
549 for(i
=2; i
<argc
; i
++){
552 if( strcmp(argv
[i
], "header")==0 ){
556 if( !ISDIGIT(argv
[i
][0]) ){
557 fprintf(stderr
, "%s: unknown option: [%s]\n", argv
[0], argv
[i
]);
560 iStart
= strtol(argv
[i
], &zLeft
, 0);
561 if( zLeft
&& strcmp(zLeft
,"..end")==0 ){
563 }else if( zLeft
&& zLeft
[0]=='.' && zLeft
[1]=='.' ){
564 iEnd
= strtol(&zLeft
[2], 0, 0);
565 }else if( zLeft
&& zLeft
[0]=='b' ){
566 int ofst
, nByte
, hdrSize
;
570 ofst
= hdrSize
= 100;
571 nByte
= pagesize
-100;
574 ofst
= (iStart
-1)*pagesize
;
577 ofst
= 32 + hdrSize
+ (iStart
-1)*(pagesize
+24) + 24;
578 a
= getContent(ofst
, nByte
);
579 decode_btree_page(a
, iStart
, hdrSize
, zLeft
+1);
585 if( iStart
<1 || iEnd
<iStart
|| iEnd
>mxFrame
){
587 "Page argument should be LOWER?..UPPER?. Range 1 to %d\n",
591 while( iStart
<=iEnd
){