1: /*
2: ** A utility for printing content from a write-ahead log file.
3: */
4: #include <stdio.h>
5: #include <ctype.h>
6: #include <sys/types.h>
7: #include <sys/stat.h>
8: #include <fcntl.h>
9: #include <unistd.h>
10: #include <stdlib.h>
11: #include <string.h>
12:
13:
14: static int pagesize = 1024; /* Size of a database page */
15: static int fd = -1; /* File descriptor for reading the WAL file */
16: static int mxFrame = 0; /* Last frame */
17: static int perLine = 16; /* HEX elements to print per line */
18:
19: typedef long long int i64; /* Datatype for 64-bit integers */
20:
21:
22: /*
23: ** Convert the var-int format into i64. Return the number of bytes
24: ** in the var-int. Write the var-int value into *pVal.
25: */
26: static int decodeVarint(const unsigned char *z, i64 *pVal){
27: i64 v = 0;
28: int i;
29: for(i=0; i<8; i++){
30: v = (v<<7) + (z[i]&0x7f);
31: if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; }
32: }
33: v = (v<<8) + (z[i]&0xff);
34: *pVal = v;
35: return 9;
36: }
37:
38: /* Report an out-of-memory error and die.
39: */
40: static void out_of_memory(void){
41: fprintf(stderr,"Out of memory...\n");
42: exit(1);
43: }
44:
45: /*
46: ** Read content from the file.
47: **
48: ** Space to hold the content is obtained from malloc() and needs to be
49: ** freed by the caller.
50: */
51: static unsigned char *getContent(int ofst, int nByte){
52: unsigned char *aData;
53: aData = malloc(nByte);
54: if( aData==0 ) out_of_memory();
55: lseek(fd, ofst, SEEK_SET);
56: read(fd, aData, nByte);
57: return aData;
58: }
59:
60: /*
61: ** Print a range of bytes as hex and as ascii.
62: */
63: static void print_byte_range(
64: int ofst, /* First byte in the range of bytes to print */
65: int nByte, /* Number of bytes to print */
66: unsigned char *aData, /* Content to print */
67: int printOfst /* Add this amount to the index on the left column */
68: ){
69: int i, j;
70: const char *zOfstFmt;
71:
72: if( ((printOfst+nByte)&~0xfff)==0 ){
73: zOfstFmt = " %03x: ";
74: }else if( ((printOfst+nByte)&~0xffff)==0 ){
75: zOfstFmt = " %04x: ";
76: }else if( ((printOfst+nByte)&~0xfffff)==0 ){
77: zOfstFmt = " %05x: ";
78: }else if( ((printOfst+nByte)&~0xffffff)==0 ){
79: zOfstFmt = " %06x: ";
80: }else{
81: zOfstFmt = " %08x: ";
82: }
83:
84: for(i=0; i<nByte; i += perLine){
85: fprintf(stdout, zOfstFmt, i+printOfst);
86: for(j=0; j<perLine; j++){
87: if( i+j>nByte ){
88: fprintf(stdout, " ");
89: }else{
90: fprintf(stdout,"%02x ", aData[i+j]);
91: }
92: }
93: for(j=0; j<perLine; j++){
94: if( i+j>nByte ){
95: fprintf(stdout, " ");
96: }else{
97: fprintf(stdout,"%c", isprint(aData[i+j]) ? aData[i+j] : '.');
98: }
99: }
100: fprintf(stdout,"\n");
101: }
102: }
103:
104: /* Print a line of decode output showing a 4-byte integer.
105: */
106: static void print_decode_line(
107: unsigned char *aData, /* Content being decoded */
108: int ofst, int nByte, /* Start and size of decode */
109: int asHex, /* If true, output value as hex */
110: const char *zMsg /* Message to append */
111: ){
112: int i, j;
113: int val = aData[ofst];
114: char zBuf[100];
115: sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]);
116: i = strlen(zBuf);
117: for(j=1; j<4; j++){
118: if( j>=nByte ){
119: sprintf(&zBuf[i], " ");
120: }else{
121: sprintf(&zBuf[i], " %02x", aData[ofst+j]);
122: val = val*256 + aData[ofst+j];
123: }
124: i += strlen(&zBuf[i]);
125: }
126: if( asHex ){
127: sprintf(&zBuf[i], " 0x%08x", val);
128: }else{
129: sprintf(&zBuf[i], " %9d", val);
130: }
131: printf("%s %s\n", zBuf, zMsg);
132: }
133:
134: /*
135: ** Print an entire page of content as hex
136: */
137: static void print_frame(int iFrame){
138: int iStart;
139: unsigned char *aData;
140: iStart = 32 + (iFrame-1)*(pagesize+24);
141: fprintf(stdout, "Frame %d: (offsets 0x%x..0x%x)\n",
142: iFrame, iStart, iStart+pagesize+24);
143: aData = getContent(iStart, pagesize+24);
144: print_decode_line(aData, 0, 4, 0, "Page number");
145: print_decode_line(aData, 4, 4, 0, "DB size, or 0 for non-commit");
146: print_decode_line(aData, 8, 4, 1, "Salt-1");
147: print_decode_line(aData,12, 4, 1, "Salt-2");
148: print_decode_line(aData,16, 4, 1, "Checksum-1");
149: print_decode_line(aData,20, 4, 1, "Checksum-2");
150: print_byte_range(iStart+24, pagesize, aData+24, 0);
151: free(aData);
152: }
153:
154: /*
155: ** extract a 32-bit big-endian integer
156: */
157: static unsigned int getInt32(const unsigned char *a){
158: unsigned int x = (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3];
159: return x;
160: }
161:
162: /*
163: ** Print an entire page of content as hex
164: */
165: static void print_oneline_frame(int iFrame){
166: int iStart;
167: unsigned char *aData;
168: iStart = 32 + (iFrame-1)*(pagesize+24);
169: aData = getContent(iStart, 24);
170: fprintf(stdout, "Frame %4d: %6d %6d 0x%08x 0x%08x 0x%08x 0x%08x\n",
171: iFrame,
172: getInt32(aData),
173: getInt32(aData+4),
174: getInt32(aData+8),
175: getInt32(aData+12),
176: getInt32(aData+16),
177: getInt32(aData+20)
178: );
179: free(aData);
180: }
181:
182: /*
183: ** Decode the WAL header.
184: */
185: static void print_wal_header(void){
186: unsigned char *aData;
187: aData = getContent(0, 32);
188: printf("WAL Header:\n");
189: print_decode_line(aData, 0, 4,1,"Magic. 0x377f0682 (le) or 0x377f0683 (be)");
190: print_decode_line(aData, 4, 4, 0, "File format");
191: print_decode_line(aData, 8, 4, 0, "Database page size");
192: print_decode_line(aData, 12,4, 0, "Checkpoint sequence number");
193: print_decode_line(aData, 16,4, 1, "Salt-1");
194: print_decode_line(aData, 20,4, 1, "Salt-2");
195: print_decode_line(aData, 24,4, 1, "Checksum-1");
196: print_decode_line(aData, 28,4, 1, "Checksum-2");
197: free(aData);
198: }
199:
200: /*
201: ** Create a description for a single cell.
202: */
203: static int describeCell(unsigned char cType, unsigned char *a, char **pzDesc){
204: int i;
205: int nDesc = 0;
206: int n = 0;
207: int leftChild;
208: i64 nPayload;
209: i64 rowid;
210: static char zDesc[100];
211: i = 0;
212: if( cType<=5 ){
213: leftChild = ((a[0]*256 + a[1])*256 + a[2])*256 + a[3];
214: a += 4;
215: n += 4;
216: sprintf(zDesc, "left-child: %d ", leftChild);
217: nDesc = strlen(zDesc);
218: }
219: if( cType!=5 ){
220: i = decodeVarint(a, &nPayload);
221: a += i;
222: n += i;
223: sprintf(&zDesc[nDesc], "sz: %lld ", nPayload);
224: nDesc += strlen(&zDesc[nDesc]);
225: }
226: if( cType==5 || cType==13 ){
227: i = decodeVarint(a, &rowid);
228: a += i;
229: n += i;
230: sprintf(&zDesc[nDesc], "rowid: %lld ", rowid);
231: nDesc += strlen(&zDesc[nDesc]);
232: }
233: *pzDesc = zDesc;
234: return n;
235: }
236:
237: /*
238: ** Decode a btree page
239: */
240: static void decode_btree_page(unsigned char *a, int pgno, int hdrSize){
241: const char *zType = "unknown";
242: int nCell;
243: int i;
244: int iCellPtr;
245: switch( a[0] ){
246: case 2: zType = "index interior node"; break;
247: case 5: zType = "table interior node"; break;
248: case 10: zType = "index leaf"; break;
249: case 13: zType = "table leaf"; break;
250: }
251: printf("Decode of btree page %d:\n", pgno);
252: print_decode_line(a, 0, 1, 0, zType);
253: print_decode_line(a, 1, 2, 0, "Offset to first freeblock");
254: print_decode_line(a, 3, 2, 0, "Number of cells on this page");
255: nCell = a[3]*256 + a[4];
256: print_decode_line(a, 5, 2, 0, "Offset to cell content area");
257: print_decode_line(a, 7, 1, 0, "Fragmented byte count");
258: if( a[0]==2 || a[0]==5 ){
259: print_decode_line(a, 8, 4, 0, "Right child");
260: iCellPtr = 12;
261: }else{
262: iCellPtr = 8;
263: }
264: for(i=0; i<nCell; i++){
265: int cofst = iCellPtr + i*2;
266: char *zDesc;
267: cofst = a[cofst]*256 + a[cofst+1];
268: describeCell(a[0], &a[cofst-hdrSize], &zDesc);
269: printf(" %03x: cell[%d] %s\n", cofst, i, zDesc);
270: }
271: }
272:
273: int main(int argc, char **argv){
274: struct stat sbuf;
275: unsigned char zPgSz[2];
276: if( argc<2 ){
277: fprintf(stderr,"Usage: %s FILENAME ?PAGE? ...\n", argv[0]);
278: exit(1);
279: }
280: fd = open(argv[1], O_RDONLY);
281: if( fd<0 ){
282: fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]);
283: exit(1);
284: }
285: zPgSz[0] = 0;
286: zPgSz[1] = 0;
287: lseek(fd, 10, SEEK_SET);
288: read(fd, zPgSz, 2);
289: pagesize = zPgSz[0]*256 + zPgSz[1];
290: if( pagesize==0 ) pagesize = 1024;
291: printf("Pagesize: %d\n", pagesize);
292: fstat(fd, &sbuf);
293: if( sbuf.st_size<32 ){
294: printf("file too small to be a WAL\n");
295: return 0;
296: }
297: mxFrame = (sbuf.st_size - 32)/(pagesize + 24);
298: printf("Available pages: 1..%d\n", mxFrame);
299: if( argc==2 ){
300: int i;
301: print_wal_header();
302: for(i=1; i<=mxFrame; i++) print_oneline_frame(i);
303: }else{
304: int i;
305: for(i=2; i<argc; i++){
306: int iStart, iEnd;
307: char *zLeft;
308: if( strcmp(argv[i], "header")==0 ){
309: print_wal_header();
310: continue;
311: }
312: if( !isdigit(argv[i][0]) ){
313: fprintf(stderr, "%s: unknown option: [%s]\n", argv[0], argv[i]);
314: continue;
315: }
316: iStart = strtol(argv[i], &zLeft, 0);
317: if( zLeft && strcmp(zLeft,"..end")==0 ){
318: iEnd = mxFrame;
319: }else if( zLeft && zLeft[0]=='.' && zLeft[1]=='.' ){
320: iEnd = strtol(&zLeft[2], 0, 0);
321: #if 0
322: }else if( zLeft && zLeft[0]=='b' ){
323: int ofst, nByte, hdrSize;
324: unsigned char *a;
325: if( iStart==1 ){
326: ofst = hdrSize = 100;
327: nByte = pagesize-100;
328: }else{
329: hdrSize = 0;
330: ofst = (iStart-1)*pagesize;
331: nByte = pagesize;
332: }
333: a = getContent(ofst, nByte);
334: decode_btree_page(a, iStart, hdrSize);
335: free(a);
336: continue;
337: #endif
338: }else{
339: iEnd = iStart;
340: }
341: if( iStart<1 || iEnd<iStart || iEnd>mxFrame ){
342: fprintf(stderr,
343: "Page argument should be LOWER?..UPPER?. Range 1 to %d\n",
344: mxFrame);
345: exit(1);
346: }
347: while( iStart<=iEnd ){
348: print_frame(iStart);
349: iStart++;
350: }
351: }
352: }
353: close(fd);
354: return 0;
355: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>