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 } 356