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 %s\n",
244 (s0
==pCksum
->s0
&& s1
==pCksum
->s1
) ? "" : "cksum-fail"
247 /* Reset the checksum so that a single frame checksum failure will not
248 ** cause all subsequent frames to also show a failure. */
255 ** Decode the WAL header.
257 static void print_wal_header(Cksum
*pCksum
){
258 unsigned char *aData
;
259 aData
= getContent(0, 32);
261 extendCksum(pCksum
, aData
, 24, 1);
262 printf("Checksum byte order: %s\n", pCksum
->bSwap
? "swapped" : "native");
264 printf("WAL Header:\n");
265 print_decode_line(aData
, 0, 4,1,"Magic. 0x377f0682 (le) or 0x377f0683 (be)");
266 print_decode_line(aData
, 4, 4, 0, "File format");
267 print_decode_line(aData
, 8, 4, 0, "Database page size");
268 print_decode_line(aData
, 12,4, 0, "Checkpoint sequence number");
269 print_decode_line(aData
, 16,4, 1, "Salt-1");
270 print_decode_line(aData
, 20,4, 1, "Salt-2");
271 print_decode_line(aData
, 24,4, 1, "Checksum-1");
272 print_decode_line(aData
, 28,4, 1, "Checksum-2");
274 if( pCksum
->s0
!=getInt32(aData
+24) ){
275 printf("**** cksum-1 mismatch: 0x%08x\n", pCksum
->s0
);
277 if( pCksum
->s1
!=getInt32(aData
+28) ){
278 printf("**** cksum-2 mismatch: 0x%08x\n", pCksum
->s1
);
284 ** Describe cell content.
286 static i64
describeContent(
287 unsigned char *a
, /* Cell content */
288 i64 nLocal
, /* Bytes in a[] */
289 char *zDesc
/* Write description here */
294 const unsigned char *pData
;
295 const unsigned char *pLimit
;
299 n
= decodeVarint(a
, &x
);
303 while( i
>0 && pData
<=pLimit
){
304 n
= decodeVarint(a
, &x
);
313 sprintf(zDesc
, "*"); /* NULL is a "*" */
314 }else if( x
>=1 && x
<=6 ){
315 v
= (signed char)pData
[0];
318 case 6: v
= (v
<<16) + (pData
[0]<<8) + pData
[1]; pData
+= 2;
319 case 5: v
= (v
<<16) + (pData
[0]<<8) + pData
[1]; pData
+= 2;
320 case 4: v
= (v
<<8) + pData
[0]; pData
++;
321 case 3: v
= (v
<<8) + pData
[0]; pData
++;
322 case 2: v
= (v
<<8) + pData
[0]; pData
++;
324 sprintf(zDesc
, "%lld", v
);
326 sprintf(zDesc
, "real");
335 sprintf(zDesc
, "blob(%lld)", size
);
337 sprintf(zDesc
, "txt(%lld)", size
);
341 j
= (int)strlen(zDesc
);
349 ** Compute the local payload size given the total payload size and
352 static i64
localPayload(i64 nPayload
, char cType
){
359 maxLocal
= pagesize
-35;
360 minLocal
= (pagesize
-12)*32/255-23;
362 maxLocal
= (pagesize
-12)*64/255-23;
363 minLocal
= (pagesize
-12)*32/255-23;
365 if( nPayload
>maxLocal
){
366 surplus
= minLocal
+ (nPayload
-minLocal
)%(pagesize
-4);
367 if( surplus
<=maxLocal
){
379 ** Create a description for a single cell.
381 ** The return value is the local cell size.
383 static i64
describeCell(
384 unsigned char cType
, /* Page type */
385 unsigned char *a
, /* Cell content */
386 int showCellContent
, /* Show cell content if true */
387 char **pzDesc
/* Store description here */
396 static char zDesc
[1000];
399 leftChild
= ((a
[0]*256 + a
[1])*256 + a
[2])*256 + a
[3];
402 sprintf(zDesc
, "lx: %d ", leftChild
);
403 nDesc
= strlen(zDesc
);
406 i
= decodeVarint(a
, &nPayload
);
409 sprintf(&zDesc
[nDesc
], "n: %lld ", nPayload
);
410 nDesc
+= strlen(&zDesc
[nDesc
]);
411 nLocal
= localPayload(nPayload
, cType
);
413 nPayload
= nLocal
= 0;
415 if( cType
==5 || cType
==13 ){
416 i
= decodeVarint(a
, &rowid
);
419 sprintf(&zDesc
[nDesc
], "r: %lld ", rowid
);
420 nDesc
+= strlen(&zDesc
[nDesc
]);
422 if( nLocal
<nPayload
){
424 unsigned char *b
= &a
[nLocal
];
425 ovfl
= ((b
[0]*256 + b
[1])*256 + b
[2])*256 + b
[3];
426 sprintf(&zDesc
[nDesc
], "ov: %d ", ovfl
);
427 nDesc
+= strlen(&zDesc
[nDesc
]);
430 if( showCellContent
&& cType
!=5 ){
431 nDesc
+= describeContent(a
, nLocal
, &zDesc
[nDesc
-1]);
438 ** Decode a btree page
440 static void decode_btree_page(
441 unsigned char *a
, /* Content of the btree page to be decoded */
442 int pgno
, /* Page number */
443 int hdrSize
, /* Size of the page1-header in bytes */
444 const char *zArgs
/* Flags to control formatting */
446 const char *zType
= "unknown";
450 int showCellContent
= 0;
454 case 2: zType
= "index interior node"; break;
455 case 5: zType
= "table interior node"; break;
456 case 10: zType
= "index leaf"; break;
457 case 13: zType
= "table leaf"; break;
461 case 'c': showCellContent
= 1; break;
462 case 'm': showMap
= 1; break;
466 printf("Decode of btree page %d:\n", pgno
);
467 print_decode_line(a
, 0, 1, 0, zType
);
468 print_decode_line(a
, 1, 2, 0, "Offset to first freeblock");
469 print_decode_line(a
, 3, 2, 0, "Number of cells on this page");
470 nCell
= a
[3]*256 + a
[4];
471 print_decode_line(a
, 5, 2, 0, "Offset to cell content area");
472 print_decode_line(a
, 7, 1, 0, "Fragmented byte count");
473 if( a
[0]==2 || a
[0]==5 ){
474 print_decode_line(a
, 8, 4, 0, "Right child");
480 printf(" key: lx=left-child n=payload-size r=rowid\n");
483 zMap
= malloc(pagesize
);
484 memset(zMap
, '.', pagesize
);
485 memset(zMap
, '1', hdrSize
);
486 memset(&zMap
[hdrSize
], 'H', iCellPtr
);
487 memset(&zMap
[hdrSize
+iCellPtr
], 'P', 2*nCell
);
489 for(i
=0; i
<nCell
; i
++){
490 int cofst
= iCellPtr
+ i
*2;
494 cofst
= a
[cofst
]*256 + a
[cofst
+1];
495 n
= describeCell(a
[0], &a
[cofst
-hdrSize
], showCellContent
, &zDesc
);
498 memset(&zMap
[cofst
], '*', (size_t)n
);
500 zMap
[cofst
+n
-1] = ']';
501 sprintf(zBuf
, "%d", i
);
502 j
= (int)strlen(zBuf
);
503 if( j
<=n
-2 ) memcpy(&zMap
[cofst
+1], zBuf
, j
);
505 printf(" %03x: cell[%d] %s\n", cofst
, i
, zDesc
);
508 for(i
=0; i
<pagesize
; i
+=64){
509 printf(" %03x: %.64s\n", i
, &zMap
[i
]);
516 ** Check the range validity for a page number. Print an error and
517 ** exit if the page is out of range.
519 static void checkPageValidity(int iPage
, int mxPage
){
520 if( iPage
<1 || iPage
>mxPage
){
521 fprintf(stderr
, "Invalid page number %d: valid range is 1..%d\n",
527 int main(int argc
, char **argv
){
529 unsigned char zPgSz
[4];
531 fprintf(stderr
,"Usage: %s FILENAME ?PAGE? ...\n", argv
[0]);
534 fd
= open(argv
[1], O_RDONLY
);
536 fprintf(stderr
,"%s: can't open %s\n", argv
[0], argv
[1]);
541 lseek(fd
, 8, SEEK_SET
);
543 pagesize
= zPgSz
[1]*65536 + zPgSz
[2]*256 + zPgSz
[3];
544 if( pagesize
==0 ) pagesize
= 1024;
545 printf("Pagesize: %d\n", pagesize
);
547 if( sbuf
.st_size
<32 ){
548 printf("file too small to be a WAL\n");
551 mxFrame
= (sbuf
.st_size
- 32)/(pagesize
+ 24);
552 printf("Available pages: 1..%d\n", mxFrame
);
556 print_wal_header(&x
);
557 for(i
=1; i
<=mxFrame
; i
++){
558 print_oneline_frame(i
, &x
);
562 for(i
=2; i
<argc
; i
++){
565 if( strcmp(argv
[i
], "header")==0 ){
569 if( !ISDIGIT(argv
[i
][0]) ){
570 fprintf(stderr
, "%s: unknown option: [%s]\n", argv
[0], argv
[i
]);
573 iStart
= strtol(argv
[i
], &zLeft
, 0);
574 checkPageValidity(iStart
, mxFrame
);
575 if( zLeft
&& strcmp(zLeft
,"..end")==0 ){
577 }else if( zLeft
&& zLeft
[0]=='.' && zLeft
[1]=='.' ){
578 iEnd
= strtol(&zLeft
[2], 0, 0);
579 checkPageValidity(iEnd
, mxFrame
);
580 }else if( zLeft
&& zLeft
[0]=='b' ){
586 ofst
= hdrSize
= 100;
587 nByte
= pagesize
-100;
590 ofst
= (i64
)(iStart
-1)*pagesize
;
593 ofst
= 32 + hdrSize
+ (i64
)(iStart
-1)*(pagesize
+24) + 24;
594 a
= getContent(ofst
, nByte
);
595 decode_btree_page(a
, iStart
, hdrSize
, zLeft
+1);
598 #if !defined(_MSC_VER)
599 }else if( zLeft
&& strcmp(zLeft
,"truncate")==0 ){
600 /* Frame number followed by "truncate" truncates the WAL file
601 ** after that frame */
602 off_t newSize
= 32 + iStart
*(pagesize
+24);
603 truncate(argv
[1], newSize
);
609 if( iStart
<1 || iEnd
<iStart
|| iEnd
>mxFrame
){
611 "Page argument should be LOWER?..UPPER?. Range 1 to %d\n",
615 while( iStart
<=iEnd
){