]>
git.saurik.com Git - redis.git/blob - src/redis-check-dump.c
14 #define REDIS_STRING 0
19 #define REDIS_HASH_ZIPMAP 9
20 #define REDIS_LIST_ZIPLIST 10
21 #define REDIS_SET_INTSET 11
22 #define REDIS_ZSET_ZIPLIST 12
23 #define REDIS_HASH_ZIPLIST 13
25 /* Objects encoding. Some kind of objects like Strings and Hashes can be
26 * internally represented in multiple ways. The 'encoding' field of the object
27 * is set to one of this fields for this object. */
28 #define REDIS_ENCODING_RAW 0 /* Raw representation */
29 #define REDIS_ENCODING_INT 1 /* Encoded as integer */
30 #define REDIS_ENCODING_ZIPMAP 2 /* Encoded as zipmap */
31 #define REDIS_ENCODING_HT 3 /* Encoded as an hash table */
33 /* Object types only used for dumping to disk */
34 #define REDIS_EXPIRETIME 253
35 #define REDIS_SELECTDB 254
38 /* Defines related to the dump file format. To store 32 bits lengths for short
39 * keys requires a lot of space, so we check the most significant 2 bits of
40 * the first byte to interpreter the length:
42 * 00|000000 => if the two MSB are 00 the len is the 6 bits of this byte
43 * 01|000000 00000000 => 01, the len is 14 byes, 6 bits + 8 bits of next byte
44 * 10|000000 [32 bit integer] => if it's 01, a full 32 bit len will follow
45 * 11|000000 this means: specially encoded object will follow. The six bits
46 * number specify the kind of object that follows.
47 * See the REDIS_RDB_ENC_* defines.
49 * Lenghts up to 63 are stored using a single byte, most DB keys, and may
50 * values, will fit inside. */
51 #define REDIS_RDB_6BITLEN 0
52 #define REDIS_RDB_14BITLEN 1
53 #define REDIS_RDB_32BITLEN 2
54 #define REDIS_RDB_ENCVAL 3
55 #define REDIS_RDB_LENERR UINT_MAX
57 /* When a length of a string object stored on disk has the first two bits
58 * set, the remaining two bits specify a special encoding for the object
59 * accordingly to the following defines: */
60 #define REDIS_RDB_ENC_INT8 0 /* 8 bit signed integer */
61 #define REDIS_RDB_ENC_INT16 1 /* 16 bit signed integer */
62 #define REDIS_RDB_ENC_INT32 2 /* 32 bit signed integer */
63 #define REDIS_RDB_ENC_LZF 3 /* string compressed with FASTLZ */
65 #define ERROR(...) { \
66 printf(__VA_ARGS__); \
70 /* data type to hold offset in file and size */
77 static unsigned char level
= 0;
78 static pos positions
[16];
80 #define CURR_OFFSET (positions[level].offset)
82 /* Hold a stack of errors */
88 static errors_t errors
;
90 #define SHIFT_ERROR(provided_offset, ...) { \
91 sprintf(errors.error[errors.level], __VA_ARGS__); \
92 errors.offset[errors.level] = provided_offset; \
96 /* Data type to hold opcode with optional key name an success status */
103 /* Global vars that are actally used as constants. The following double
104 * values are used for double on-disk serialization, and are initialized
105 * at runtime to avoid strange compiler optimizations. */
106 static double R_Zero
, R_PosInf
, R_NegInf
, R_Nan
;
108 /* store string types for output */
109 static char types
[256][16];
111 /* when number of bytes to read is negative, do a peek */
112 int readBytes(void *target
, long num
) {
113 char peek
= (num
< 0) ? 1 : 0;
114 num
= (num
< 0) ? -num
: num
;
116 pos p
= positions
[level
];
117 if (p
.offset
+ num
> p
.size
) {
120 memcpy(target
, (void*)((size_t)p
.data
+ p
.offset
), num
);
121 if (!peek
) positions
[level
].offset
+= num
;
126 int processHeader() {
127 char buf
[10] = "_________";
130 if (!readBytes(buf
, 9)) {
131 ERROR("Cannot read header\n");
134 /* expect the first 5 bytes to equal REDIS */
135 if (memcmp(buf
,"REDIS",5) != 0) {
136 ERROR("Wrong signature in header\n");
139 dump_version
= (int)strtol(buf
+ 5, NULL
, 10);
140 if (dump_version
< 1 || dump_version
> 4) {
141 ERROR("Unknown RDB format version: %d\n", dump_version
);
146 int loadType(entry
*e
) {
147 uint32_t offset
= CURR_OFFSET
;
149 /* this byte needs to qualify as type */
151 if (readBytes(&t
, 1)) {
152 if (t
<= 4 || (t
>=9 && t
<= 12) || t
>= 253) {
156 SHIFT_ERROR(offset
, "Unknown type (0x%02x)", t
);
159 SHIFT_ERROR(offset
, "Could not read type");
168 if (readBytes(&t
, -1) && (t
<= 4 || (t
>=9 && t
<= 12) || t
>= 253))
173 /* discard time, just consume the bytes */
175 uint32_t offset
= CURR_OFFSET
;
177 if (readBytes(t
, 4)) {
180 SHIFT_ERROR(offset
, "Could not read time");
187 uint32_t loadLength(int *isencoded
) {
188 unsigned char buf
[2];
192 if (isencoded
) *isencoded
= 0;
193 if (!readBytes(buf
, 1)) return REDIS_RDB_LENERR
;
194 type
= (buf
[0] & 0xC0) >> 6;
195 if (type
== REDIS_RDB_6BITLEN
) {
196 /* Read a 6 bit len */
197 return buf
[0] & 0x3F;
198 } else if (type
== REDIS_RDB_ENCVAL
) {
199 /* Read a 6 bit len encoding type */
200 if (isencoded
) *isencoded
= 1;
201 return buf
[0] & 0x3F;
202 } else if (type
== REDIS_RDB_14BITLEN
) {
203 /* Read a 14 bit len */
204 if (!readBytes(buf
+1,1)) return REDIS_RDB_LENERR
;
205 return ((buf
[0] & 0x3F) << 8) | buf
[1];
207 /* Read a 32 bit len */
208 if (!readBytes(&len
, 4)) return REDIS_RDB_LENERR
;
209 return (unsigned int)ntohl(len
);
213 char *loadIntegerObject(int enctype
) {
214 uint32_t offset
= CURR_OFFSET
;
215 unsigned char enc
[4];
218 if (enctype
== REDIS_RDB_ENC_INT8
) {
220 if (!readBytes(enc
, 1)) return NULL
;
223 } else if (enctype
== REDIS_RDB_ENC_INT16
) {
225 if (!readBytes(enc
, 2)) return NULL
;
226 v
= enc
[0]|(enc
[1]<<8);
228 } else if (enctype
== REDIS_RDB_ENC_INT32
) {
230 if (!readBytes(enc
, 4)) return NULL
;
231 v
= enc
[0]|(enc
[1]<<8)|(enc
[2]<<16)|(enc
[3]<<24);
234 SHIFT_ERROR(offset
, "Unknown integer encoding (0x%02x)", enctype
);
238 /* convert val into string */
240 buf
= malloc(sizeof(char) * 128);
241 sprintf(buf
, "%lld", val
);
245 char* loadLzfStringObject() {
246 unsigned int slen
, clen
;
249 if ((clen
= loadLength(NULL
)) == REDIS_RDB_LENERR
) return NULL
;
250 if ((slen
= loadLength(NULL
)) == REDIS_RDB_LENERR
) return NULL
;
253 if (!readBytes(c
, clen
)) {
259 if (lzf_decompress(c
,clen
,s
,slen
) == 0) {
268 /* returns NULL when not processable, char* when valid */
269 char* loadStringObject() {
270 uint32_t offset
= CURR_OFFSET
;
274 len
= loadLength(&isencoded
);
277 case REDIS_RDB_ENC_INT8
:
278 case REDIS_RDB_ENC_INT16
:
279 case REDIS_RDB_ENC_INT32
:
280 return loadIntegerObject(len
);
281 case REDIS_RDB_ENC_LZF
:
282 return loadLzfStringObject();
284 /* unknown encoding */
285 SHIFT_ERROR(offset
, "Unknown string encoding (0x%02x)", len
);
290 if (len
== REDIS_RDB_LENERR
) return NULL
;
292 char *buf
= malloc(sizeof(char) * (len
+1));
294 if (!readBytes(buf
, len
)) {
301 int processStringObject(char** store
) {
302 unsigned long offset
= CURR_OFFSET
;
303 char *key
= loadStringObject();
305 SHIFT_ERROR(offset
, "Error reading string object");
318 double* loadDoubleValue() {
323 if (!readBytes(&len
,1)) return NULL
;
325 val
= malloc(sizeof(double));
327 case 255: *val
= R_NegInf
; return val
;
328 case 254: *val
= R_PosInf
; return val
;
329 case 253: *val
= R_Nan
; return val
;
331 if (!readBytes(buf
, len
)) {
336 sscanf(buf
, "%lg", val
);
341 int processDoubleValue(double** store
) {
342 unsigned long offset
= CURR_OFFSET
;
343 double *val
= loadDoubleValue();
345 SHIFT_ERROR(offset
, "Error reading double value");
358 int loadPair(entry
*e
) {
359 uint32_t offset
= CURR_OFFSET
;
364 if (processStringObject(&key
)) {
367 SHIFT_ERROR(offset
, "Error reading entry key");
372 if (e
->type
== REDIS_LIST
||
373 e
->type
== REDIS_SET
||
374 e
->type
== REDIS_ZSET
||
375 e
->type
== REDIS_HASH
) {
376 if ((length
= loadLength(NULL
)) == REDIS_RDB_LENERR
) {
377 SHIFT_ERROR(offset
, "Error reading %s length", types
[e
->type
]);
384 case REDIS_HASH_ZIPMAP
:
385 case REDIS_LIST_ZIPLIST
:
386 case REDIS_SET_INTSET
:
387 case REDIS_ZSET_ZIPLIST
:
388 case REDIS_HASH_ZIPLIST
:
389 if (!processStringObject(NULL
)) {
390 SHIFT_ERROR(offset
, "Error reading entry value");
396 for (i
= 0; i
< length
; i
++) {
397 offset
= CURR_OFFSET
;
398 if (!processStringObject(NULL
)) {
399 SHIFT_ERROR(offset
, "Error reading element at index %d (length: %d)", i
, length
);
405 for (i
= 0; i
< length
; i
++) {
406 offset
= CURR_OFFSET
;
407 if (!processStringObject(NULL
)) {
408 SHIFT_ERROR(offset
, "Error reading element key at index %d (length: %d)", i
, length
);
411 offset
= CURR_OFFSET
;
412 if (!processDoubleValue(NULL
)) {
413 SHIFT_ERROR(offset
, "Error reading element value at index %d (length: %d)", i
, length
);
419 for (i
= 0; i
< length
; i
++) {
420 offset
= CURR_OFFSET
;
421 if (!processStringObject(NULL
)) {
422 SHIFT_ERROR(offset
, "Error reading element key at index %d (length: %d)", i
, length
);
425 offset
= CURR_OFFSET
;
426 if (!processStringObject(NULL
)) {
427 SHIFT_ERROR(offset
, "Error reading element value at index %d (length: %d)", i
, length
);
433 SHIFT_ERROR(offset
, "Type not implemented");
436 /* because we're done, we assume success */
442 entry e
= { NULL
, -1, 0 };
443 uint32_t length
, offset
[4];
445 /* reset error container */
448 offset
[0] = CURR_OFFSET
;
453 offset
[1] = CURR_OFFSET
;
454 if (e
.type
== REDIS_SELECTDB
) {
455 if ((length
= loadLength(NULL
)) == REDIS_RDB_LENERR
) {
456 SHIFT_ERROR(offset
[1], "Error reading database number");
460 SHIFT_ERROR(offset
[1], "Database number out of range (%d)", length
);
463 } else if (e
.type
== REDIS_EOF
) {
464 if (positions
[level
].offset
< positions
[level
].size
) {
465 SHIFT_ERROR(offset
[0], "Unexpected EOF");
471 /* optionally consume expire */
472 if (e
.type
== REDIS_EXPIRETIME
) {
473 if (!processTime()) return e
;
474 if (!loadType(&e
)) return e
;
477 offset
[1] = CURR_OFFSET
;
479 SHIFT_ERROR(offset
[1], "Error for type %s", types
[e
.type
]);
484 /* all entries are followed by a valid type:
485 * e.g. a new entry, SELECTDB, EXPIRE, EOF */
486 offset
[2] = CURR_OFFSET
;
487 if (peekType() == -1) {
488 SHIFT_ERROR(offset
[2], "Followed by invalid type");
489 SHIFT_ERROR(offset
[0], "Error for type %s", types
[e
.type
]);
498 void printCentered(int indent
, int width
, char* body
) {
499 char head
[256], tail
[256];
500 memset(head
, '\0', 256);
501 memset(tail
, '\0', 256);
503 memset(head
, '=', indent
);
504 memset(tail
, '=', width
- 2 - indent
- strlen(body
));
505 printf("%s %s %s\n", head
, body
, tail
);
508 void printValid(uint64_t ops
, uint64_t bytes
) {
510 sprintf(body
, "Processed %llu valid opcodes (in %llu bytes)",
511 (unsigned long long) ops
, (unsigned long long) bytes
);
512 printCentered(4, 80, body
);
515 void printSkipped(uint64_t bytes
, uint64_t offset
) {
517 sprintf(body
, "Skipped %llu bytes (resuming at 0x%08llx)",
518 (unsigned long long) bytes
, (unsigned long long) offset
);
519 printCentered(4, 80, body
);
522 void printErrorStack(entry
*e
) {
527 sprintf(body
, "Error trace");
528 } else if (e
->type
>= 253) {
529 sprintf(body
, "Error trace (%s)", types
[e
->type
]);
530 } else if (!e
->key
) {
531 sprintf(body
, "Error trace (%s: (unknown))", types
[e
->type
]);
534 strncpy(tmp
, e
->key
, 40);
536 /* display truncation at the last 3 chars */
537 if (strlen(e
->key
) > 40) {
538 memset(&tmp
[37], '.', 3);
541 /* display unprintable characters as ? */
542 for (i
= 0; i
< strlen(tmp
); i
++) {
543 if (tmp
[i
] <= 32) tmp
[i
] = '?';
545 sprintf(body
, "Error trace (%s: %s)", types
[e
->type
], tmp
);
548 printCentered(4, 80, body
);
550 /* display error stack */
551 for (i
= 0; i
< errors
.level
; i
++) {
552 printf("0x%08lx - %s\n",
553 (unsigned long) errors
.offset
[i
], errors
.error
[i
]);
558 uint64_t num_errors
= 0, num_valid_ops
= 0, num_valid_bytes
= 0;
563 while(positions
[0].offset
< positions
[0].size
) {
564 positions
[1] = positions
[0];
567 if (!entry
.success
) {
568 printValid(num_valid_ops
, num_valid_bytes
);
569 printErrorStack(&entry
);
574 /* search for next valid entry */
575 uint64_t offset
= positions
[0].offset
+ 1;
578 while (!entry
.success
&& offset
< positions
[0].size
) {
579 positions
[1].offset
= offset
;
581 /* find 3 consecutive valid entries */
582 for (i
= 0; i
< 3; i
++) {
584 if (!entry
.success
) break;
586 /* check if we found 3 consecutive valid entries */
592 /* print how many bytes we have skipped to find a new valid opcode */
593 if (offset
< positions
[0].size
) {
594 printSkipped(offset
- positions
[0].offset
, offset
);
597 positions
[0].offset
= offset
;
600 num_valid_bytes
+= positions
[1].offset
- positions
[0].offset
;
602 /* advance position */
603 positions
[0] = positions
[1];
608 /* because there is another potential error,
609 * print how many valid ops we have processed */
610 printValid(num_valid_ops
, num_valid_bytes
);
613 if (entry
.type
!= REDIS_EOF
) {
614 /* last byte should be EOF, add error */
616 SHIFT_ERROR(positions
[0].offset
, "Expected EOF, got %s", types
[entry
.type
]);
618 /* this is an EOF error so reset type */
620 printErrorStack(&entry
);
625 /* print summary on errors */
628 printf("Total unprocessable opcodes: %llu\n",
629 (unsigned long long) num_errors
);
633 int main(int argc
, char **argv
) {
634 /* expect the first argument to be the dump file */
636 printf("Usage: %s <dump.rdb>\n", argv
[0]);
645 fd
= open(argv
[1], O_RDONLY
);
647 ERROR("Cannot open file: %s\n", argv
[1]);
649 if (fstat(fd
, &stat
) == -1) {
650 ERROR("Cannot stat: %s\n", argv
[1]);
655 if (sizeof(size_t) == sizeof(int32_t) && size
>= INT_MAX
) {
656 ERROR("Cannot check dump files >2GB on a 32-bit platform\n");
659 data
= mmap(NULL
, size
, PROT_READ
, MAP_SHARED
, fd
, 0);
660 if (data
== MAP_FAILED
) {
661 ERROR("Cannot mmap: %s\n", argv
[1]);
664 /* Initialize static vars */
665 positions
[0].data
= data
;
666 positions
[0].size
= size
;
667 positions
[0].offset
= 0;
671 sprintf(types
[REDIS_STRING
], "STRING");
672 sprintf(types
[REDIS_LIST
], "LIST");
673 sprintf(types
[REDIS_SET
], "SET");
674 sprintf(types
[REDIS_ZSET
], "ZSET");
675 sprintf(types
[REDIS_HASH
], "HASH");
677 /* Object types only used for dumping to disk */
678 sprintf(types
[REDIS_EXPIRETIME
], "EXPIRETIME");
679 sprintf(types
[REDIS_SELECTDB
], "SELECTDB");
680 sprintf(types
[REDIS_EOF
], "EOF");
682 /* Double constants initialization */
684 R_PosInf
= 1.0/R_Zero
;
685 R_NegInf
= -1.0/R_Zero
;
686 R_Nan
= R_Zero
/R_Zero
;