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)
15 #include <sys/types.h>
24 static int pagesize
= 1024; /* Size of a database page */
25 static int fd
= -1; /* File descriptor for reading the WAL file */
26 static int mxFrame
= 0; /* Last frame */
27 static int perLine
= 16; /* HEX elements to print per line */
29 typedef long long int i64
; /* Datatype for 64-bit integers */
31 /* Information for computing the checksum */
32 typedef struct Cksum Cksum
;
34 int bSwap
; /* True to do byte swapping on 32-bit words */
35 unsigned s0
, s1
; /* Current checksum value */
39 ** extract a 32-bit big-endian integer
41 static unsigned int getInt32(const unsigned char *a
){
42 unsigned int x
= (a
[0]<<24) + (a
[1]<<16) + (a
[2]<<8) + a
[3];
47 ** Swap bytes on a 32-bit unsigned integer
49 static unsigned int swab32(unsigned int x
){
50 return (((x
)&0x000000FF)<<24) + (((x
)&0x0000FF00)<<8)
51 + (((x
)&0x00FF0000)>>8) + (((x
)&0xFF000000)>>24);
54 /* Extend the checksum. Reinitialize the checksum if bInit is true.
56 static void extendCksum(
67 /* Host is little-endian */
68 pCksum
->bSwap
= getInt32(aData
)!=0x377f0682;
70 /* Host is big-endian */
71 pCksum
->bSwap
= getInt32(aData
)!=0x377f0683;
76 a32
= (unsigned int*)aData
;
78 unsigned int x0
= a32
[0];
79 unsigned int x1
= a32
[1];
84 pCksum
->s0
+= x0
+ pCksum
->s1
;
85 pCksum
->s1
+= x1
+ pCksum
->s0
;
92 ** Convert the var-int format into i64. Return the number of bytes
93 ** in the var-int. Write the var-int value into *pVal.
95 static int decodeVarint(const unsigned char *z
, i64
*pVal
){
99 v
= (v
<<7) + (z
[i
]&0x7f);
100 if( (z
[i
]&0x80)==0 ){ *pVal
= v
; return i
+1; }
102 v
= (v
<<8) + (z
[i
]&0xff);
107 /* Report an out-of-memory error and die.
109 static void out_of_memory(void){
110 fprintf(stderr
,"Out of memory...\n");
115 ** Read content from the file.
117 ** Space to hold the content is obtained from malloc() and needs to be
118 ** freed by the caller.
120 static unsigned char *getContent(i64 ofst
, int nByte
){
121 unsigned char *aData
;
122 aData
= malloc(nByte
);
123 if( aData
==0 ) out_of_memory();
124 lseek(fd
, ofst
, SEEK_SET
);
125 read(fd
, aData
, nByte
);
130 ** Print a range of bytes as hex and as ascii.
132 static void print_byte_range(
133 int ofst
, /* First byte in the range of bytes to print */
134 int nByte
, /* Number of bytes to print */
135 unsigned char *aData
, /* Content to print */
136 int printOfst
/* Add this amount to the index on the left column */
139 const char *zOfstFmt
;
141 if( ((printOfst
+nByte
)&~0xfff)==0 ){
142 zOfstFmt
= " %03x: ";
143 }else if( ((printOfst
+nByte
)&~0xffff)==0 ){
144 zOfstFmt
= " %04x: ";
145 }else if( ((printOfst
+nByte
)&~0xfffff)==0 ){
146 zOfstFmt
= " %05x: ";
147 }else if( ((printOfst
+nByte
)&~0xffffff)==0 ){
148 zOfstFmt
= " %06x: ";
150 zOfstFmt
= " %08x: ";
153 for(i
=0; i
<nByte
; i
+= perLine
){
154 fprintf(stdout
, zOfstFmt
, i
+printOfst
);
155 for(j
=0; j
<perLine
; j
++){
157 fprintf(stdout
, " ");
159 fprintf(stdout
,"%02x ", aData
[i
+j
]);
162 for(j
=0; j
<perLine
; j
++){
164 fprintf(stdout
, " ");
166 fprintf(stdout
,"%c", ISPRINT(aData
[i
+j
]) ? aData
[i
+j
] : '.');
169 fprintf(stdout
,"\n");
173 /* Print a line of decode output showing a 4-byte integer.
175 static void print_decode_line(
176 unsigned char *aData
, /* Content being decoded */
177 int ofst
, int nByte
, /* Start and size of decode */
178 int asHex
, /* If true, output value as hex */
179 const char *zMsg
/* Message to append */
182 int val
= aData
[ofst
];
184 sprintf(zBuf
, " %03x: %02x", ofst
, aData
[ofst
]);
185 i
= (int)strlen(zBuf
);
188 sprintf(&zBuf
[i
], " ");
190 sprintf(&zBuf
[i
], " %02x", aData
[ofst
+j
]);
191 val
= val
*256 + aData
[ofst
+j
];
193 i
+= (int)strlen(&zBuf
[i
]);
196 sprintf(&zBuf
[i
], " 0x%08x", val
);
198 sprintf(&zBuf
[i
], " %9d", val
);
200 printf("%s %s\n", zBuf
, zMsg
);
204 ** Print an entire page of content as hex
206 static void print_frame(int iFrame
){
208 unsigned char *aData
;
209 iStart
= 32 + (i64
)(iFrame
-1)*(pagesize
+24);
210 fprintf(stdout
, "Frame %d: (offsets 0x%llx..0x%llx)\n",
211 iFrame
, iStart
, iStart
+pagesize
+24);
212 aData
= getContent(iStart
, pagesize
+24);
213 print_decode_line(aData
, 0, 4, 0, "Page number");
214 print_decode_line(aData
, 4, 4, 0, "DB size, or 0 for non-commit");
215 print_decode_line(aData
, 8, 4, 1, "Salt-1");
216 print_decode_line(aData
,12, 4, 1, "Salt-2");
217 print_decode_line(aData
,16, 4, 1, "Checksum-1");
218 print_decode_line(aData
,20, 4, 1, "Checksum-2");
219 print_byte_range(iStart
+24, pagesize
, aData
+24, 0);
224 ** Summarize a single frame on a single line.
226 static void print_oneline_frame(int iFrame
, Cksum
*pCksum
){
228 unsigned char *aData
;
230 iStart
= 32 + (i64
)(iFrame
-1)*(pagesize
+24);
231 aData
= getContent(iStart
, 24);
232 extendCksum(pCksum
, aData
, 8, 0);
233 extendCksum(pCksum
, getContent(iStart
+24, pagesize
), pagesize
, 0);
234 s0
= getInt32(aData
+16);
235 s1
= getInt32(aData
+20);
236 fprintf(stdout
, "Frame %4d: %6d %6d 0x%08x,%08x 0x%08x,%08x",
245 if( s0
==pCksum
->s0
&& s1
==pCksum
->s1
){
246 fprintf(stdout
, "\n");
248 fprintf(stdout
, " should be 0x%08x,%08x\n",
249 pCksum
->s0
, pCksum
->s1
);
252 /* Reset the checksum so that a single frame checksum failure will not
253 ** cause all subsequent frames to also show a failure. */
260 ** Decode the WAL header.
262 static void print_wal_header(Cksum
*pCksum
){
263 unsigned char *aData
;
264 aData
= getContent(0, 32);
266 extendCksum(pCksum
, aData
, 24, 1);
267 printf("Checksum byte order: %s\n", pCksum
->bSwap
? "swapped" : "native");
269 printf("WAL Header:\n");
270 print_decode_line(aData
, 0, 4,1,"Magic. 0x377f0682 (le) or 0x377f0683 (be)");
271 print_decode_line(aData
, 4, 4, 0, "File format");
272 print_decode_line(aData
, 8, 4, 0, "Database page size");
273 print_decode_line(aData
, 12,4, 0, "Checkpoint sequence number");
274 print_decode_line(aData
, 16,4, 1, "Salt-1");
275 print_decode_line(aData
, 20,4, 1, "Salt-2");
276 print_decode_line(aData
, 24,4, 1, "Checksum-1");
277 print_decode_line(aData
, 28,4, 1, "Checksum-2");
279 if( pCksum
->s0
!=getInt32(aData
+24) ){
280 printf("**** cksum-1 mismatch: 0x%08x\n", pCksum
->s0
);
282 if( pCksum
->s1
!=getInt32(aData
+28) ){
283 printf("**** cksum-2 mismatch: 0x%08x\n", pCksum
->s1
);
289 ** Describe cell content.
291 static i64
describeContent(
292 unsigned char *a
, /* Cell content */
293 i64 nLocal
, /* Bytes in a[] */
294 char *zDesc
/* Write description here */
299 const unsigned char *pData
;
300 const unsigned char *pLimit
;
304 n
= decodeVarint(a
, &x
);
308 while( i
>0 && pData
<=pLimit
){
309 n
= decodeVarint(a
, &x
);
318 sprintf(zDesc
, "*"); /* NULL is a "*" */
319 }else if( x
>=1 && x
<=6 ){
320 v
= (signed char)pData
[0];
323 case 6: v
= (v
<<16) + (pData
[0]<<8) + pData
[1]; pData
+= 2;
324 case 5: v
= (v
<<16) + (pData
[0]<<8) + pData
[1]; pData
+= 2;
325 case 4: v
= (v
<<8) + pData
[0]; pData
++;
326 case 3: v
= (v
<<8) + pData
[0]; pData
++;
327 case 2: v
= (v
<<8) + pData
[0]; pData
++;
329 sprintf(zDesc
, "%lld", v
);
331 sprintf(zDesc
, "real");
340 sprintf(zDesc
, "blob(%lld)", size
);
342 sprintf(zDesc
, "txt(%lld)", size
);
346 j
= (int)strlen(zDesc
);
354 ** Compute the local payload size given the total payload size and
357 static i64
localPayload(i64 nPayload
, char cType
){
364 maxLocal
= pagesize
-35;
365 minLocal
= (pagesize
-12)*32/255-23;
367 maxLocal
= (pagesize
-12)*64/255-23;
368 minLocal
= (pagesize
-12)*32/255-23;
370 if( nPayload
>maxLocal
){
371 surplus
= minLocal
+ (nPayload
-minLocal
)%(pagesize
-4);
372 if( surplus
<=maxLocal
){
384 ** Create a description for a single cell.
386 ** The return value is the local cell size.
388 static i64
describeCell(
389 unsigned char cType
, /* Page type */
390 unsigned char *a
, /* Cell content */
391 int showCellContent
, /* Show cell content if true */
392 char **pzDesc
/* Store description here */
401 static char zDesc
[1000];
404 leftChild
= ((a
[0]*256 + a
[1])*256 + a
[2])*256 + a
[3];
407 sprintf(zDesc
, "lx: %d ", leftChild
);
408 nDesc
= strlen(zDesc
);
411 i
= decodeVarint(a
, &nPayload
);
414 sprintf(&zDesc
[nDesc
], "n: %lld ", nPayload
);
415 nDesc
+= strlen(&zDesc
[nDesc
]);
416 nLocal
= localPayload(nPayload
, cType
);
418 nPayload
= nLocal
= 0;
420 if( cType
==5 || cType
==13 ){
421 i
= decodeVarint(a
, &rowid
);
424 sprintf(&zDesc
[nDesc
], "r: %lld ", rowid
);
425 nDesc
+= strlen(&zDesc
[nDesc
]);
427 if( nLocal
<nPayload
){
429 unsigned char *b
= &a
[nLocal
];
430 ovfl
= ((b
[0]*256 + b
[1])*256 + b
[2])*256 + b
[3];
431 sprintf(&zDesc
[nDesc
], "ov: %d ", ovfl
);
432 nDesc
+= strlen(&zDesc
[nDesc
]);
435 if( showCellContent
&& cType
!=5 ){
436 nDesc
+= describeContent(a
, nLocal
, &zDesc
[nDesc
-1]);
443 ** Decode a btree page
445 static void decode_btree_page(
446 unsigned char *a
, /* Content of the btree page to be decoded */
447 int pgno
, /* Page number */
448 int hdrSize
, /* Size of the page1-header in bytes */
449 const char *zArgs
/* Flags to control formatting */
451 const char *zType
= "unknown";
455 int showCellContent
= 0;
459 case 2: zType
= "index interior node"; break;
460 case 5: zType
= "table interior node"; break;
461 case 10: zType
= "index leaf"; break;
462 case 13: zType
= "table leaf"; break;
466 case 'c': showCellContent
= 1; break;
467 case 'm': showMap
= 1; break;
471 printf("Decode of btree page %d:\n", pgno
);
472 print_decode_line(a
, 0, 1, 0, zType
);
473 print_decode_line(a
, 1, 2, 0, "Offset to first freeblock");
474 print_decode_line(a
, 3, 2, 0, "Number of cells on this page");
475 nCell
= a
[3]*256 + a
[4];
476 print_decode_line(a
, 5, 2, 0, "Offset to cell content area");
477 print_decode_line(a
, 7, 1, 0, "Fragmented byte count");
478 if( a
[0]==2 || a
[0]==5 ){
479 print_decode_line(a
, 8, 4, 0, "Right child");
485 printf(" key: lx=left-child n=payload-size r=rowid\n");
488 zMap
= malloc(pagesize
);
489 memset(zMap
, '.', pagesize
);
490 memset(zMap
, '1', hdrSize
);
491 memset(&zMap
[hdrSize
], 'H', iCellPtr
);
492 memset(&zMap
[hdrSize
+iCellPtr
], 'P', 2*nCell
);
494 for(i
=0; i
<nCell
; i
++){
495 int cofst
= iCellPtr
+ i
*2;
499 cofst
= a
[cofst
]*256 + a
[cofst
+1];
500 n
= describeCell(a
[0], &a
[cofst
-hdrSize
], showCellContent
, &zDesc
);
503 memset(&zMap
[cofst
], '*', (size_t)n
);
505 zMap
[cofst
+n
-1] = ']';
506 sprintf(zBuf
, "%d", i
);
507 j
= (int)strlen(zBuf
);
508 if( j
<=n
-2 ) memcpy(&zMap
[cofst
+1], zBuf
, j
);
510 printf(" %03x: cell[%d] %s\n", cofst
, i
, zDesc
);
513 for(i
=0; i
<pagesize
; i
+=64){
514 printf(" %03x: %.64s\n", i
, &zMap
[i
]);
521 ** Check the range validity for a page number. Print an error and
522 ** exit if the page is out of range.
524 static void checkPageValidity(int iPage
, int mxPage
){
525 if( iPage
<1 || iPage
>mxPage
){
526 fprintf(stderr
, "Invalid page number %d: valid range is 1..%d\n",
532 int main(int argc
, char **argv
){
534 unsigned char zPgSz
[4];
536 fprintf(stderr
,"Usage: %s FILENAME ?PAGE? ...\n", argv
[0]);
539 fd
= open(argv
[1], O_RDONLY
);
541 fprintf(stderr
,"%s: can't open %s\n", argv
[0], argv
[1]);
547 if( sbuf
.st_size
<32 ){
548 printf("%s: file too small to be a WAL - only %d bytes\n",
549 argv
[1], (int)sbuf
.st_size
);
552 if( lseek(fd
, 8, SEEK_SET
)!=8 ){
553 printf("\"%s\" seems to not be a valid WAL file\n", argv
[1]);
556 if( read(fd
, zPgSz
, 4)!=4 ){
557 printf("\"%s\": cannot read the page size\n", argv
[1]);
560 pagesize
= zPgSz
[1]*65536 + zPgSz
[2]*256 + zPgSz
[3];
561 if( pagesize
==0 ) pagesize
= 1024;
562 printf("Pagesize: %d\n", pagesize
);
563 if( (pagesize
& (pagesize
-1))!=0 || pagesize
<512 || pagesize
>65536 ){
564 printf("\"%s\": invalid page size.\n", argv
[1]);
567 mxFrame
= (sbuf
.st_size
- 32)/(pagesize
+ 24);
568 printf("Available pages: 1..%d\n", mxFrame
);
572 print_wal_header(&x
);
573 for(i
=1; i
<=mxFrame
; i
++){
574 print_oneline_frame(i
, &x
);
578 for(i
=2; i
<argc
; i
++){
581 if( strcmp(argv
[i
], "header")==0 ){
585 if( !ISDIGIT(argv
[i
][0]) ){
586 fprintf(stderr
, "%s: unknown option: [%s]\n", argv
[0], argv
[i
]);
589 iStart
= strtol(argv
[i
], &zLeft
, 0);
590 checkPageValidity(iStart
, mxFrame
);
591 if( zLeft
&& strcmp(zLeft
,"..end")==0 ){
593 }else if( zLeft
&& zLeft
[0]=='.' && zLeft
[1]=='.' ){
594 iEnd
= strtol(&zLeft
[2], 0, 0);
595 checkPageValidity(iEnd
, mxFrame
);
596 }else if( zLeft
&& zLeft
[0]=='b' ){
602 ofst
= hdrSize
= 100;
603 nByte
= pagesize
-100;
606 ofst
= (i64
)(iStart
-1)*pagesize
;
609 ofst
= 32 + hdrSize
+ (i64
)(iStart
-1)*(pagesize
+24) + 24;
610 a
= getContent(ofst
, nByte
);
611 decode_btree_page(a
, iStart
, hdrSize
, zLeft
+1);
614 #if !defined(_MSC_VER)
615 }else if( zLeft
&& strcmp(zLeft
,"truncate")==0 ){
616 /* Frame number followed by "truncate" truncates the WAL file
617 ** after that frame */
618 off_t newSize
= 32 + iStart
*(pagesize
+24);
619 truncate(argv
[1], newSize
);
625 if( iStart
<1 || iEnd
<iStart
|| iEnd
>mxFrame
){
627 "Page argument should be LOWER?..UPPER?. Range 1 to %d\n",
631 while( iStart
<=iEnd
){