]>
git.saurik.com Git - redis.git/blob - redis-check-dump.c
14 #define REDIS_STRING 0
20 /* Objects encoding. Some kind of objects like Strings and Hashes can be
21 * internally represented in multiple ways. The 'encoding' field of the object
22 * is set to one of this fields for this object. */
23 #define REDIS_ENCODING_RAW 0 /* Raw representation */
24 #define REDIS_ENCODING_INT 1 /* Encoded as integer */
25 #define REDIS_ENCODING_ZIPMAP 2 /* Encoded as zipmap */
26 #define REDIS_ENCODING_HT 3 /* Encoded as an hash table */
28 /* Object types only used for dumping to disk */
29 #define REDIS_EXPIRETIME 253
30 #define REDIS_SELECTDB 254
33 /* Defines related to the dump file format. To store 32 bits lengths for short
34 * keys requires a lot of space, so we check the most significant 2 bits of
35 * the first byte to interpreter the length:
37 * 00|000000 => if the two MSB are 00 the len is the 6 bits of this byte
38 * 01|000000 00000000 => 01, the len is 14 byes, 6 bits + 8 bits of next byte
39 * 10|000000 [32 bit integer] => if it's 01, a full 32 bit len will follow
40 * 11|000000 this means: specially encoded object will follow. The six bits
41 * number specify the kind of object that follows.
42 * See the REDIS_RDB_ENC_* defines.
44 * Lenghts up to 63 are stored using a single byte, most DB keys, and may
45 * values, will fit inside. */
46 #define REDIS_RDB_6BITLEN 0
47 #define REDIS_RDB_14BITLEN 1
48 #define REDIS_RDB_32BITLEN 2
49 #define REDIS_RDB_ENCVAL 3
50 #define REDIS_RDB_LENERR UINT_MAX
52 /* When a length of a string object stored on disk has the first two bits
53 * set, the remaining two bits specify a special encoding for the object
54 * accordingly to the following defines: */
55 #define REDIS_RDB_ENC_INT8 0 /* 8 bit signed integer */
56 #define REDIS_RDB_ENC_INT16 1 /* 16 bit signed integer */
57 #define REDIS_RDB_ENC_INT32 2 /* 32 bit signed integer */
58 #define REDIS_RDB_ENC_LZF 3 /* string compressed with FASTLZ */
60 #define ERROR(...) { \
61 printf(__VA_ARGS__); \
65 /* data type to hold offset in file and size */
72 static unsigned char level
= 0;
73 static pos positions
[16];
75 #define CURR_OFFSET (positions[level].offset)
77 /* Hold a stack of errors */
80 unsigned long offset
[16];
83 static errors_t errors
;
85 #define SHIFT_ERROR(provided_offset, ...) { \
86 sprintf(errors.error[errors.level], __VA_ARGS__); \
87 errors.offset[errors.level] = provided_offset; \
91 /* Data type to hold opcode with optional key name an success status */
98 /* Global vars that are actally used as constants. The following double
99 * values are used for double on-disk serialization, and are initialized
100 * at runtime to avoid strange compiler optimizations. */
101 static double R_Zero
, R_PosInf
, R_NegInf
, R_Nan
;
103 /* store string types for output */
104 static char types
[256][16];
106 /* when number of bytes to read is negative, do a peek */
107 int readBytes(void *target
, long num
) {
108 char peek
= (num
< 0) ? 1 : 0;
109 num
= (num
< 0) ? -num
: num
;
111 pos p
= positions
[level
];
112 if (p
.offset
+ num
> p
.size
) {
115 memcpy(target
, (void*)((unsigned long)p
.data
+ p
.offset
), num
);
116 if (!peek
) positions
[level
].offset
+= num
;
121 int processHeader() {
122 char buf
[10] = "_________";
125 if (!readBytes(buf
, 9)) {
126 ERROR("Cannot read header\n");
129 /* expect the first 5 bytes to equal REDIS */
130 if (memcmp(buf
,"REDIS",5) != 0) {
131 ERROR("Wrong signature in header\n");
134 dump_version
= (int)strtol(buf
+ 5, NULL
, 10);
135 if (dump_version
!= 1) {
136 ERROR("Unknown RDB format version: %d\n", dump_version
);
141 int loadType(entry
*e
) {
142 uint32_t offset
= CURR_OFFSET
;
144 /* this byte needs to qualify as type */
146 if (readBytes(&t
, 1)) {
147 if (t
<= 4 || t
>= 253) {
151 SHIFT_ERROR(offset
, "Unknown type (0x%02x)", t
);
154 SHIFT_ERROR(offset
, "Could not read type");
163 if (readBytes(&t
, -1) && (t
<= 4 || t
>= 253)) return t
;
167 /* discard time, just consume the bytes */
169 uint32_t offset
= CURR_OFFSET
;
171 if (readBytes(t
, 4)) {
174 SHIFT_ERROR(offset
, "Could not read time");
181 uint32_t loadLength(int *isencoded
) {
182 unsigned char buf
[2];
186 if (isencoded
) *isencoded
= 0;
187 if (!readBytes(buf
, 1)) return REDIS_RDB_LENERR
;
188 type
= (buf
[0] & 0xC0) >> 6;
189 if (type
== REDIS_RDB_6BITLEN
) {
190 /* Read a 6 bit len */
191 return buf
[0] & 0x3F;
192 } else if (type
== REDIS_RDB_ENCVAL
) {
193 /* Read a 6 bit len encoding type */
194 if (isencoded
) *isencoded
= 1;
195 return buf
[0] & 0x3F;
196 } else if (type
== REDIS_RDB_14BITLEN
) {
197 /* Read a 14 bit len */
198 if (!readBytes(buf
+1,1)) return REDIS_RDB_LENERR
;
199 return ((buf
[0] & 0x3F) << 8) | buf
[1];
201 /* Read a 32 bit len */
202 if (!readBytes(&len
, 4)) return REDIS_RDB_LENERR
;
203 return (unsigned int)ntohl(len
);
207 char *loadIntegerObject(int enctype
) {
208 uint32_t offset
= CURR_OFFSET
;
209 unsigned char enc
[4];
212 if (enctype
== REDIS_RDB_ENC_INT8
) {
214 if (!readBytes(enc
, 1)) return NULL
;
217 } else if (enctype
== REDIS_RDB_ENC_INT16
) {
219 if (!readBytes(enc
, 2)) return NULL
;
220 v
= enc
[0]|(enc
[1]<<8);
222 } else if (enctype
== REDIS_RDB_ENC_INT32
) {
224 if (!readBytes(enc
, 4)) return NULL
;
225 v
= enc
[0]|(enc
[1]<<8)|(enc
[2]<<16)|(enc
[3]<<24);
228 SHIFT_ERROR(offset
, "Unknown integer encoding (0x%02x)", enctype
);
232 /* convert val into string */
234 buf
= malloc(sizeof(char) * 128);
235 sprintf(buf
, "%lld", val
);
239 char* loadLzfStringObject() {
240 unsigned int slen
, clen
;
243 if ((clen
= loadLength(NULL
)) == REDIS_RDB_LENERR
) return NULL
;
244 if ((slen
= loadLength(NULL
)) == REDIS_RDB_LENERR
) return NULL
;
247 if (!readBytes(c
, clen
)) {
253 if (lzf_decompress(c
,clen
,s
,slen
) == 0) {
262 /* returns NULL when not processable, char* when valid */
263 char* loadStringObject() {
264 uint32_t offset
= CURR_OFFSET
;
268 len
= loadLength(&isencoded
);
271 case REDIS_RDB_ENC_INT8
:
272 case REDIS_RDB_ENC_INT16
:
273 case REDIS_RDB_ENC_INT32
:
274 return loadIntegerObject(len
);
275 case REDIS_RDB_ENC_LZF
:
276 return loadLzfStringObject();
278 /* unknown encoding */
279 SHIFT_ERROR(offset
, "Unknown string encoding (0x%02x)", len
);
284 if (len
== REDIS_RDB_LENERR
) return NULL
;
286 char *buf
= malloc(sizeof(char) * (len
+1));
288 if (!readBytes(buf
, len
)) {
295 int processStringObject(char** store
) {
296 unsigned long offset
= CURR_OFFSET
;
297 char *key
= loadStringObject();
299 SHIFT_ERROR(offset
, "Error reading string object");
312 double* loadDoubleValue() {
317 if (!readBytes(&len
,1)) return NULL
;
319 val
= malloc(sizeof(double));
321 case 255: *val
= R_NegInf
; return val
;
322 case 254: *val
= R_PosInf
; return val
;
323 case 253: *val
= R_Nan
; return val
;
325 if (!readBytes(buf
, len
)) {
330 sscanf(buf
, "%lg", val
);
335 int processDoubleValue(double** store
) {
336 unsigned long offset
= CURR_OFFSET
;
337 double *val
= loadDoubleValue();
339 SHIFT_ERROR(offset
, "Error reading double value");
352 int loadPair(entry
*e
) {
353 uint32_t offset
= CURR_OFFSET
;
358 if (processStringObject(&key
)) {
361 SHIFT_ERROR(offset
, "Error reading entry key");
366 if (e
->type
== REDIS_LIST
||
367 e
->type
== REDIS_SET
||
368 e
->type
== REDIS_ZSET
||
369 e
->type
== REDIS_HASH
) {
370 if ((length
= loadLength(NULL
)) == REDIS_RDB_LENERR
) {
371 SHIFT_ERROR(offset
, "Error reading %s length", types
[e
->type
]);
378 if (!processStringObject(NULL
)) {
379 SHIFT_ERROR(offset
, "Error reading entry value");
385 for (i
= 0; i
< length
; i
++) {
386 offset
= CURR_OFFSET
;
387 if (!processStringObject(NULL
)) {
388 SHIFT_ERROR(offset
, "Error reading element at index %d (length: %d)", i
, length
);
394 for (i
= 0; i
< length
; i
++) {
395 offset
= CURR_OFFSET
;
396 if (!processStringObject(NULL
)) {
397 SHIFT_ERROR(offset
, "Error reading element key at index %d (length: %d)", i
, length
);
400 offset
= CURR_OFFSET
;
401 if (!processDoubleValue(NULL
)) {
402 SHIFT_ERROR(offset
, "Error reading element value at index %d (length: %d)", i
, length
);
408 for (i
= 0; i
< length
; i
++) {
409 offset
= CURR_OFFSET
;
410 if (!processStringObject(NULL
)) {
411 SHIFT_ERROR(offset
, "Error reading element key at index %d (length: %d)", i
, length
);
414 offset
= CURR_OFFSET
;
415 if (!processStringObject(NULL
)) {
416 SHIFT_ERROR(offset
, "Error reading element value at index %d (length: %d)", i
, length
);
422 SHIFT_ERROR(offset
, "Type not implemented");
425 /* because we're done, we assume success */
431 entry e
= { NULL
, -1, 0 };
432 uint32_t length
, offset
[4];
434 /* reset error container */
437 offset
[0] = CURR_OFFSET
;
442 offset
[1] = CURR_OFFSET
;
443 if (e
.type
== REDIS_SELECTDB
) {
444 if ((length
= loadLength(NULL
)) == REDIS_RDB_LENERR
) {
445 SHIFT_ERROR(offset
[1], "Error reading database number");
449 SHIFT_ERROR(offset
[1], "Database number out of range (%d)", length
);
452 } else if (e
.type
== REDIS_EOF
) {
453 if (positions
[level
].offset
< positions
[level
].size
) {
454 SHIFT_ERROR(offset
[0], "Unexpected EOF");
460 /* optionally consume expire */
461 if (e
.type
== REDIS_EXPIRETIME
) {
462 if (!processTime()) return e
;
463 if (!loadType(&e
)) return e
;
466 offset
[1] = CURR_OFFSET
;
468 SHIFT_ERROR(offset
[1], "Error for type %s", types
[e
.type
]);
473 /* all entries are followed by a valid type:
474 * e.g. a new entry, SELECTDB, EXPIRE, EOF */
475 offset
[2] = CURR_OFFSET
;
476 if (peekType() == -1) {
477 SHIFT_ERROR(offset
[2], "Followed by invalid type");
478 SHIFT_ERROR(offset
[0], "Error for type %s", types
[e
.type
]);
487 void printCentered(int indent
, int width
, char* body
) {
488 char head
[256], tail
[256];
489 memset(head
, '\0', 256);
490 memset(tail
, '\0', 256);
492 memset(head
, '=', indent
);
493 memset(tail
, '=', width
- 2 - indent
- strlen(body
));
494 printf("%s %s %s\n", head
, body
, tail
);
497 void printValid(int ops
, int bytes
) {
499 sprintf(body
, "Processed %d valid opcodes (in %d bytes)", ops
, bytes
);
500 printCentered(4, 80, body
);
503 void printSkipped(int bytes
, int offset
) {
505 sprintf(body
, "Skipped %d bytes (resuming at 0x%08x)", bytes
, offset
);
506 printCentered(4, 80, body
);
509 void printErrorStack(entry
*e
) {
514 sprintf(body
, "Error trace");
515 } else if (e
->type
>= 253) {
516 sprintf(body
, "Error trace (%s)", types
[e
->type
]);
517 } else if (!e
->key
) {
518 sprintf(body
, "Error trace (%s: (unknown))", types
[e
->type
]);
521 strncpy(tmp
, e
->key
, 40);
523 /* display truncation at the last 3 chars */
524 if (strlen(e
->key
) > 40) {
525 memset(&tmp
[37], '.', 3);
528 /* display unprintable characters as ? */
529 for (i
= 0; i
< strlen(tmp
); i
++) {
530 if (tmp
[i
] <= 32) tmp
[i
] = '?';
532 sprintf(body
, "Error trace (%s: %s)", types
[e
->type
], tmp
);
535 printCentered(4, 80, body
);
537 /* display error stack */
538 for (i
= 0; i
< errors
.level
; i
++) {
539 printf("0x%08lx - %s\n", errors
.offset
[i
], errors
.error
[i
]);
544 int i
, num_errors
= 0, num_valid_ops
= 0, num_valid_bytes
= 0;
549 while(positions
[0].offset
< positions
[0].size
) {
550 positions
[1] = positions
[0];
553 if (!entry
.success
) {
554 printValid(num_valid_ops
, num_valid_bytes
);
555 printErrorStack(&entry
);
560 /* search for next valid entry */
561 unsigned long offset
= positions
[0].offset
+ 1;
562 while (!entry
.success
&& offset
< positions
[0].size
) {
563 positions
[1].offset
= offset
;
565 /* find 3 consecutive valid entries */
566 for (i
= 0; i
< 3; i
++) {
568 if (!entry
.success
) break;
570 /* check if we found 3 consecutive valid entries */
576 /* print how many bytes we have skipped to find a new valid opcode */
577 if (offset
< positions
[0].size
) {
578 printSkipped(offset
- positions
[0].offset
, offset
);
581 positions
[0].offset
= offset
;
584 num_valid_bytes
+= positions
[1].offset
- positions
[0].offset
;
586 /* advance position */
587 positions
[0] = positions
[1];
591 /* because there is another potential error,
592 * print how many valid ops we have processed */
593 printValid(num_valid_ops
, num_valid_bytes
);
596 if (entry
.type
!= REDIS_EOF
) {
597 /* last byte should be EOF, add error */
599 SHIFT_ERROR(positions
[0].offset
, "Expected EOF, got %s", types
[entry
.type
]);
601 /* this is an EOF error so reset type */
603 printErrorStack(&entry
);
608 /* print summary on errors */
609 if (num_errors
> 0) {
611 printf("Total unprocessable opcodes: %d\n", num_errors
);
615 int main(int argc
, char **argv
) {
616 /* expect the first argument to be the dump file */
618 printf("Usage: %s <dump.rdb>\n", argv
[0]);
627 fd
= open(argv
[1], O_RDONLY
);
629 ERROR("Cannot open file: %s\n", argv
[1]);
631 if (fstat(fd
, &stat
) == -1) {
632 ERROR("Cannot stat: %s\n", argv
[1]);
637 data
= mmap(NULL
, size
, PROT_READ
, MAP_SHARED
, fd
, 0);
638 if (data
== MAP_FAILED
) {
639 ERROR("Cannot mmap: %s\n", argv
[1]);
642 /* Initialize static vars */
643 positions
[0].data
= data
;
644 positions
[0].size
= size
;
645 positions
[0].offset
= 0;
649 sprintf(types
[REDIS_STRING
], "STRING");
650 sprintf(types
[REDIS_LIST
], "LIST");
651 sprintf(types
[REDIS_SET
], "SET");
652 sprintf(types
[REDIS_ZSET
], "ZSET");
653 sprintf(types
[REDIS_HASH
], "HASH");
655 /* Object types only used for dumping to disk */
656 sprintf(types
[REDIS_EXPIRETIME
], "EXPIRETIME");
657 sprintf(types
[REDIS_SELECTDB
], "SELECTDB");
658 sprintf(types
[REDIS_EOF
], "EOF");
660 /* Double constants initialization */
662 R_PosInf
= 1.0/R_Zero
;
663 R_NegInf
= -1.0/R_Zero
;
664 R_Nan
= R_Zero
/R_Zero
;