]>
git.saurik.com Git - redis.git/blob - src/redis-check-dump.c
2 * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>
3 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of Redis nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without
16 * specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
39 #include <arpa/inet.h>
45 #define REDIS_STRING 0
50 #define REDIS_HASH_ZIPMAP 9
51 #define REDIS_LIST_ZIPLIST 10
52 #define REDIS_SET_INTSET 11
53 #define REDIS_ZSET_ZIPLIST 12
54 #define REDIS_HASH_ZIPLIST 13
56 /* Objects encoding. Some kind of objects like Strings and Hashes can be
57 * internally represented in multiple ways. The 'encoding' field of the object
58 * is set to one of this fields for this object. */
59 #define REDIS_ENCODING_RAW 0 /* Raw representation */
60 #define REDIS_ENCODING_INT 1 /* Encoded as integer */
61 #define REDIS_ENCODING_ZIPMAP 2 /* Encoded as zipmap */
62 #define REDIS_ENCODING_HT 3 /* Encoded as an hash table */
64 /* Object types only used for dumping to disk */
65 #define REDIS_EXPIRETIME_MS 252
66 #define REDIS_EXPIRETIME 253
67 #define REDIS_SELECTDB 254
70 /* Defines related to the dump file format. To store 32 bits lengths for short
71 * keys requires a lot of space, so we check the most significant 2 bits of
72 * the first byte to interpreter the length:
74 * 00|000000 => if the two MSB are 00 the len is the 6 bits of this byte
75 * 01|000000 00000000 => 01, the len is 14 byes, 6 bits + 8 bits of next byte
76 * 10|000000 [32 bit integer] => if it's 01, a full 32 bit len will follow
77 * 11|000000 this means: specially encoded object will follow. The six bits
78 * number specify the kind of object that follows.
79 * See the REDIS_RDB_ENC_* defines.
81 * Lenghts up to 63 are stored using a single byte, most DB keys, and may
82 * values, will fit inside. */
83 #define REDIS_RDB_6BITLEN 0
84 #define REDIS_RDB_14BITLEN 1
85 #define REDIS_RDB_32BITLEN 2
86 #define REDIS_RDB_ENCVAL 3
87 #define REDIS_RDB_LENERR UINT_MAX
89 /* When a length of a string object stored on disk has the first two bits
90 * set, the remaining two bits specify a special encoding for the object
91 * accordingly to the following defines: */
92 #define REDIS_RDB_ENC_INT8 0 /* 8 bit signed integer */
93 #define REDIS_RDB_ENC_INT16 1 /* 16 bit signed integer */
94 #define REDIS_RDB_ENC_INT32 2 /* 32 bit signed integer */
95 #define REDIS_RDB_ENC_LZF 3 /* string compressed with FASTLZ */
97 #define ERROR(...) { \
98 printf(__VA_ARGS__); \
102 /* data type to hold offset in file and size */
109 static unsigned char level
= 0;
110 static pos positions
[16];
112 #define CURR_OFFSET (positions[level].offset)
114 /* Hold a stack of errors */
116 char error
[16][1024];
120 static errors_t errors
;
122 #define SHIFT_ERROR(provided_offset, ...) { \
123 sprintf(errors.error[errors.level], __VA_ARGS__); \
124 errors.offset[errors.level] = provided_offset; \
128 /* Data type to hold opcode with optional key name an success status */
135 /* Global vars that are actally used as constants. The following double
136 * values are used for double on-disk serialization, and are initialized
137 * at runtime to avoid strange compiler optimizations. */
138 static double R_Zero
, R_PosInf
, R_NegInf
, R_Nan
;
140 /* store string types for output */
141 static char types
[256][16];
144 uint64_t crc64(uint64_t crc
, const unsigned char *s
, uint64_t l
);
146 /* Return true if 't' is a valid object type. */
147 int checkType(unsigned char t
) {
148 /* In case a new object type is added, update the following
149 * condition as necessary. */
151 (t
>= REDIS_HASH_ZIPMAP
&& t
<= REDIS_HASH_ZIPLIST
) ||
153 t
>= REDIS_EXPIRETIME_MS
;
156 /* when number of bytes to read is negative, do a peek */
157 int readBytes(void *target
, long num
) {
158 char peek
= (num
< 0) ? 1 : 0;
159 num
= (num
< 0) ? -num
: num
;
161 pos p
= positions
[level
];
162 if (p
.offset
+ num
> p
.size
) {
165 memcpy(target
, (void*)((size_t)p
.data
+ p
.offset
), num
);
166 if (!peek
) positions
[level
].offset
+= num
;
171 int processHeader() {
172 char buf
[10] = "_________";
175 if (!readBytes(buf
, 9)) {
176 ERROR("Cannot read header\n");
179 /* expect the first 5 bytes to equal REDIS */
180 if (memcmp(buf
,"REDIS",5) != 0) {
181 ERROR("Wrong signature in header\n");
184 dump_version
= (int)strtol(buf
+ 5, NULL
, 10);
185 if (dump_version
< 1 || dump_version
> 6) {
186 ERROR("Unknown RDB format version: %d\n", dump_version
);
191 int loadType(entry
*e
) {
192 uint32_t offset
= CURR_OFFSET
;
194 /* this byte needs to qualify as type */
196 if (readBytes(&t
, 1)) {
201 SHIFT_ERROR(offset
, "Unknown type (0x%02x)", t
);
204 SHIFT_ERROR(offset
, "Could not read type");
213 if (readBytes(&t
, -1) && (checkType(t
)))
218 /* discard time, just consume the bytes */
219 int processTime(int type
) {
220 uint32_t offset
= CURR_OFFSET
;
222 int timelen
= (type
== REDIS_EXPIRETIME_MS
) ? 8 : 4;
224 if (readBytes(t
,timelen
)) {
227 SHIFT_ERROR(offset
, "Could not read time");
234 uint32_t loadLength(int *isencoded
) {
235 unsigned char buf
[2];
239 if (isencoded
) *isencoded
= 0;
240 if (!readBytes(buf
, 1)) return REDIS_RDB_LENERR
;
241 type
= (buf
[0] & 0xC0) >> 6;
242 if (type
== REDIS_RDB_6BITLEN
) {
243 /* Read a 6 bit len */
244 return buf
[0] & 0x3F;
245 } else if (type
== REDIS_RDB_ENCVAL
) {
246 /* Read a 6 bit len encoding type */
247 if (isencoded
) *isencoded
= 1;
248 return buf
[0] & 0x3F;
249 } else if (type
== REDIS_RDB_14BITLEN
) {
250 /* Read a 14 bit len */
251 if (!readBytes(buf
+1,1)) return REDIS_RDB_LENERR
;
252 return ((buf
[0] & 0x3F) << 8) | buf
[1];
254 /* Read a 32 bit len */
255 if (!readBytes(&len
, 4)) return REDIS_RDB_LENERR
;
256 return (unsigned int)ntohl(len
);
260 char *loadIntegerObject(int enctype
) {
261 uint32_t offset
= CURR_OFFSET
;
262 unsigned char enc
[4];
265 if (enctype
== REDIS_RDB_ENC_INT8
) {
267 if (!readBytes(enc
, 1)) return NULL
;
270 } else if (enctype
== REDIS_RDB_ENC_INT16
) {
272 if (!readBytes(enc
, 2)) return NULL
;
273 v
= enc
[0]|(enc
[1]<<8);
275 } else if (enctype
== REDIS_RDB_ENC_INT32
) {
277 if (!readBytes(enc
, 4)) return NULL
;
278 v
= enc
[0]|(enc
[1]<<8)|(enc
[2]<<16)|(enc
[3]<<24);
281 SHIFT_ERROR(offset
, "Unknown integer encoding (0x%02x)", enctype
);
285 /* convert val into string */
287 buf
= malloc(sizeof(char) * 128);
288 sprintf(buf
, "%lld", val
);
292 char* loadLzfStringObject() {
293 unsigned int slen
, clen
;
296 if ((clen
= loadLength(NULL
)) == REDIS_RDB_LENERR
) return NULL
;
297 if ((slen
= loadLength(NULL
)) == REDIS_RDB_LENERR
) return NULL
;
300 if (!readBytes(c
, clen
)) {
306 if (lzf_decompress(c
,clen
,s
,slen
) == 0) {
315 /* returns NULL when not processable, char* when valid */
316 char* loadStringObject() {
317 uint32_t offset
= CURR_OFFSET
;
321 len
= loadLength(&isencoded
);
324 case REDIS_RDB_ENC_INT8
:
325 case REDIS_RDB_ENC_INT16
:
326 case REDIS_RDB_ENC_INT32
:
327 return loadIntegerObject(len
);
328 case REDIS_RDB_ENC_LZF
:
329 return loadLzfStringObject();
331 /* unknown encoding */
332 SHIFT_ERROR(offset
, "Unknown string encoding (0x%02x)", len
);
337 if (len
== REDIS_RDB_LENERR
) return NULL
;
339 char *buf
= malloc(sizeof(char) * (len
+1));
341 if (!readBytes(buf
, len
)) {
348 int processStringObject(char** store
) {
349 unsigned long offset
= CURR_OFFSET
;
350 char *key
= loadStringObject();
352 SHIFT_ERROR(offset
, "Error reading string object");
365 double* loadDoubleValue() {
370 if (!readBytes(&len
,1)) return NULL
;
372 val
= malloc(sizeof(double));
374 case 255: *val
= R_NegInf
; return val
;
375 case 254: *val
= R_PosInf
; return val
;
376 case 253: *val
= R_Nan
; return val
;
378 if (!readBytes(buf
, len
)) {
383 sscanf(buf
, "%lg", val
);
388 int processDoubleValue(double** store
) {
389 unsigned long offset
= CURR_OFFSET
;
390 double *val
= loadDoubleValue();
392 SHIFT_ERROR(offset
, "Error reading double value");
405 int loadPair(entry
*e
) {
406 uint32_t offset
= CURR_OFFSET
;
411 if (processStringObject(&key
)) {
414 SHIFT_ERROR(offset
, "Error reading entry key");
419 if (e
->type
== REDIS_LIST
||
420 e
->type
== REDIS_SET
||
421 e
->type
== REDIS_ZSET
||
422 e
->type
== REDIS_HASH
) {
423 if ((length
= loadLength(NULL
)) == REDIS_RDB_LENERR
) {
424 SHIFT_ERROR(offset
, "Error reading %s length", types
[e
->type
]);
431 case REDIS_HASH_ZIPMAP
:
432 case REDIS_LIST_ZIPLIST
:
433 case REDIS_SET_INTSET
:
434 case REDIS_ZSET_ZIPLIST
:
435 case REDIS_HASH_ZIPLIST
:
436 if (!processStringObject(NULL
)) {
437 SHIFT_ERROR(offset
, "Error reading entry value");
443 for (i
= 0; i
< length
; i
++) {
444 offset
= CURR_OFFSET
;
445 if (!processStringObject(NULL
)) {
446 SHIFT_ERROR(offset
, "Error reading element at index %d (length: %d)", i
, length
);
452 for (i
= 0; i
< length
; i
++) {
453 offset
= CURR_OFFSET
;
454 if (!processStringObject(NULL
)) {
455 SHIFT_ERROR(offset
, "Error reading element key at index %d (length: %d)", i
, length
);
458 offset
= CURR_OFFSET
;
459 if (!processDoubleValue(NULL
)) {
460 SHIFT_ERROR(offset
, "Error reading element value at index %d (length: %d)", i
, length
);
466 for (i
= 0; i
< length
; i
++) {
467 offset
= CURR_OFFSET
;
468 if (!processStringObject(NULL
)) {
469 SHIFT_ERROR(offset
, "Error reading element key at index %d (length: %d)", i
, length
);
472 offset
= CURR_OFFSET
;
473 if (!processStringObject(NULL
)) {
474 SHIFT_ERROR(offset
, "Error reading element value at index %d (length: %d)", i
, length
);
480 SHIFT_ERROR(offset
, "Type not implemented");
483 /* because we're done, we assume success */
489 entry e
= { NULL
, -1, 0 };
490 uint32_t length
, offset
[4];
492 /* reset error container */
495 offset
[0] = CURR_OFFSET
;
500 offset
[1] = CURR_OFFSET
;
501 if (e
.type
== REDIS_SELECTDB
) {
502 if ((length
= loadLength(NULL
)) == REDIS_RDB_LENERR
) {
503 SHIFT_ERROR(offset
[1], "Error reading database number");
507 SHIFT_ERROR(offset
[1], "Database number out of range (%d)", length
);
510 } else if (e
.type
== REDIS_EOF
) {
511 if (positions
[level
].offset
< positions
[level
].size
) {
512 SHIFT_ERROR(offset
[0], "Unexpected EOF");
518 /* optionally consume expire */
519 if (e
.type
== REDIS_EXPIRETIME
||
520 e
.type
== REDIS_EXPIRETIME_MS
) {
521 if (!processTime(e
.type
)) return e
;
522 if (!loadType(&e
)) return e
;
525 offset
[1] = CURR_OFFSET
;
527 SHIFT_ERROR(offset
[1], "Error for type %s", types
[e
.type
]);
532 /* all entries are followed by a valid type:
533 * e.g. a new entry, SELECTDB, EXPIRE, EOF */
534 offset
[2] = CURR_OFFSET
;
535 if (peekType() == -1) {
536 SHIFT_ERROR(offset
[2], "Followed by invalid type");
537 SHIFT_ERROR(offset
[0], "Error for type %s", types
[e
.type
]);
546 void printCentered(int indent
, int width
, char* body
) {
547 char head
[256], tail
[256];
548 memset(head
, '\0', 256);
549 memset(tail
, '\0', 256);
551 memset(head
, '=', indent
);
552 memset(tail
, '=', width
- 2 - indent
- strlen(body
));
553 printf("%s %s %s\n", head
, body
, tail
);
556 void printValid(uint64_t ops
, uint64_t bytes
) {
558 sprintf(body
, "Processed %llu valid opcodes (in %llu bytes)",
559 (unsigned long long) ops
, (unsigned long long) bytes
);
560 printCentered(4, 80, body
);
563 void printSkipped(uint64_t bytes
, uint64_t offset
) {
565 sprintf(body
, "Skipped %llu bytes (resuming at 0x%08llx)",
566 (unsigned long long) bytes
, (unsigned long long) offset
);
567 printCentered(4, 80, body
);
570 void printErrorStack(entry
*e
) {
575 sprintf(body
, "Error trace");
576 } else if (e
->type
>= 253) {
577 sprintf(body
, "Error trace (%s)", types
[e
->type
]);
578 } else if (!e
->key
) {
579 sprintf(body
, "Error trace (%s: (unknown))", types
[e
->type
]);
582 strncpy(tmp
, e
->key
, 40);
584 /* display truncation at the last 3 chars */
585 if (strlen(e
->key
) > 40) {
586 memset(&tmp
[37], '.', 3);
589 /* display unprintable characters as ? */
590 for (i
= 0; i
< strlen(tmp
); i
++) {
591 if (tmp
[i
] <= 32) tmp
[i
] = '?';
593 sprintf(body
, "Error trace (%s: %s)", types
[e
->type
], tmp
);
596 printCentered(4, 80, body
);
598 /* display error stack */
599 for (i
= 0; i
< errors
.level
; i
++) {
600 printf("0x%08lx - %s\n",
601 (unsigned long) errors
.offset
[i
], errors
.error
[i
]);
606 uint64_t num_errors
= 0, num_valid_ops
= 0, num_valid_bytes
= 0;
608 int dump_version
= processHeader();
610 /* Exclude the final checksum for RDB >= 5. Will be checked at the end. */
611 if (dump_version
>= 5) {
612 if (positions
[0].size
< 8) {
613 printf("RDB version >= 5 but no room for checksum.\n");
616 positions
[0].size
-= 8;;
620 while(positions
[0].offset
< positions
[0].size
) {
621 positions
[1] = positions
[0];
624 if (!entry
.success
) {
625 printValid(num_valid_ops
, num_valid_bytes
);
626 printErrorStack(&entry
);
631 /* search for next valid entry */
632 uint64_t offset
= positions
[0].offset
+ 1;
635 while (!entry
.success
&& offset
< positions
[0].size
) {
636 positions
[1].offset
= offset
;
638 /* find 3 consecutive valid entries */
639 for (i
= 0; i
< 3; i
++) {
641 if (!entry
.success
) break;
643 /* check if we found 3 consecutive valid entries */
649 /* print how many bytes we have skipped to find a new valid opcode */
650 if (offset
< positions
[0].size
) {
651 printSkipped(offset
- positions
[0].offset
, offset
);
654 positions
[0].offset
= offset
;
657 num_valid_bytes
+= positions
[1].offset
- positions
[0].offset
;
659 /* advance position */
660 positions
[0] = positions
[1];
665 /* because there is another potential error,
666 * print how many valid ops we have processed */
667 printValid(num_valid_ops
, num_valid_bytes
);
670 if (entry
.type
!= REDIS_EOF
) {
671 /* last byte should be EOF, add error */
673 SHIFT_ERROR(positions
[0].offset
, "Expected EOF, got %s", types
[entry
.type
]);
675 /* this is an EOF error so reset type */
677 printErrorStack(&entry
);
682 /* Verify checksum */
683 if (dump_version
>= 5) {
684 uint64_t crc
= crc64(0,positions
[0].data
,positions
[0].size
);
686 unsigned char *p
= (unsigned char*)positions
[0].data
+positions
[0].size
;
687 crc2
= ((uint64_t)p
[0] << 0) |
688 ((uint64_t)p
[1] << 8) |
689 ((uint64_t)p
[2] << 16) |
690 ((uint64_t)p
[3] << 24) |
691 ((uint64_t)p
[4] << 32) |
692 ((uint64_t)p
[5] << 40) |
693 ((uint64_t)p
[6] << 48) |
694 ((uint64_t)p
[7] << 56);
696 SHIFT_ERROR(positions
[0].offset
, "RDB CRC64 does not match.");
698 printf("CRC64 checksum is OK\n");
702 /* print summary on errors */
705 printf("Total unprocessable opcodes: %llu\n",
706 (unsigned long long) num_errors
);
710 int main(int argc
, char **argv
) {
711 /* expect the first argument to be the dump file */
713 printf("Usage: %s <dump.rdb>\n", argv
[0]);
722 fd
= open(argv
[1], O_RDONLY
);
724 ERROR("Cannot open file: %s\n", argv
[1]);
726 if (fstat(fd
, &stat
) == -1) {
727 ERROR("Cannot stat: %s\n", argv
[1]);
732 if (sizeof(size_t) == sizeof(int32_t) && size
>= INT_MAX
) {
733 ERROR("Cannot check dump files >2GB on a 32-bit platform\n");
736 data
= mmap(NULL
, size
, PROT_READ
, MAP_SHARED
, fd
, 0);
737 if (data
== MAP_FAILED
) {
738 ERROR("Cannot mmap: %s\n", argv
[1]);
741 /* Initialize static vars */
742 positions
[0].data
= data
;
743 positions
[0].size
= size
;
744 positions
[0].offset
= 0;
748 sprintf(types
[REDIS_STRING
], "STRING");
749 sprintf(types
[REDIS_LIST
], "LIST");
750 sprintf(types
[REDIS_SET
], "SET");
751 sprintf(types
[REDIS_ZSET
], "ZSET");
752 sprintf(types
[REDIS_HASH
], "HASH");
754 /* Object types only used for dumping to disk */
755 sprintf(types
[REDIS_EXPIRETIME
], "EXPIRETIME");
756 sprintf(types
[REDIS_SELECTDB
], "SELECTDB");
757 sprintf(types
[REDIS_EOF
], "EOF");
759 /* Double constants initialization */
761 R_PosInf
= 1.0/R_Zero
;
762 R_NegInf
= -1.0/R_Zero
;
763 R_Nan
= R_Zero
/R_Zero
;