2012-11-08 12:25:23 -05:00
/*
* Copyright ( c ) 2009 - 2012 , Salvatore Sanfilippo < antirez at gmail dot com >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* * Redistributions of source code must retain the above copyright notice ,
* this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
*/
2015-07-26 09:14:57 -04:00
# include "server.h"
2010-06-21 18:07:48 -04:00
# include "sha1.h" /* SHA1 is used for DEBUG DIGEST */
2012-11-29 08:20:08 -05:00
# include "crc64.h"
2010-06-21 18:07:48 -04:00
2010-07-01 15:13:38 -04:00
# include <arpa/inet.h>
2012-01-20 06:20:45 -05:00
# include <signal.h>
2016-09-26 18:29:47 -04:00
# include <dlfcn.h>
2012-01-20 06:20:45 -05:00
# ifdef HAVE_BACKTRACE
# include <execinfo.h>
# include <ucontext.h>
2012-04-26 10:04:53 -04:00
# include <fcntl.h>
2012-11-22 16:52:39 -05:00
# include "bio.h"
2016-07-04 16:28:32 -04:00
# include <unistd.h>
2012-01-20 06:20:45 -05:00
# endif /* HAVE_BACKTRACE */
2010-07-01 15:13:38 -04:00
2014-05-12 10:56:43 -04:00
# ifdef __CYGWIN__
# ifndef SA_ONSTACK
# define SA_ONSTACK 0x08000000
# endif
# endif
2010-06-21 18:07:48 -04:00
/* ================================= Debugging ============================== */
/* Compute the sha1 of string at 's' with 'len' bytes long.
2013-01-16 12:00:20 -05:00
* The SHA1 is then xored against the string pointed by digest .
2010-06-21 18:07:48 -04:00
* Since xor is commutative , this operation is used in order to
* " add " digests relative to unordered elements .
*
* So digest ( a , b , c , d ) will be the same of digest ( b , a , c , d ) */
void xorDigest ( unsigned char * digest , void * ptr , size_t len ) {
SHA1_CTX ctx ;
unsigned char hash [ 20 ] , * s = ptr ;
int j ;
SHA1Init ( & ctx ) ;
SHA1Update ( & ctx , s , len ) ;
SHA1Final ( hash , & ctx ) ;
for ( j = 0 ; j < 20 ; j + + )
digest [ j ] ^ = hash [ j ] ;
}
void xorObjectDigest ( unsigned char * digest , robj * o ) {
o = getDecodedObject ( o ) ;
xorDigest ( digest , o - > ptr , sdslen ( o - > ptr ) ) ;
decrRefCount ( o ) ;
}
/* This function instead of just computing the SHA1 and xoring it
2013-01-16 12:00:20 -05:00
* against digest , also perform the digest of " digest " itself and
2010-06-21 18:07:48 -04:00
* replace the old value with the new one .
*
* So the final digest will be :
*
* digest = SHA1 ( digest xor SHA1 ( data ) )
*
* This function is used every time we want to preserve the order so
* that digest ( a , b , c , d ) will be different than digest ( b , c , d , a )
*
* Also note that mixdigest ( " foo " ) followed by mixdigest ( " bar " )
* will lead to a different digest compared to " fo " , " obar " .
*/
void mixDigest ( unsigned char * digest , void * ptr , size_t len ) {
SHA1_CTX ctx ;
char * s = ptr ;
xorDigest ( digest , s , len ) ;
SHA1Init ( & ctx ) ;
SHA1Update ( & ctx , digest , 20 ) ;
SHA1Final ( digest , & ctx ) ;
}
void mixObjectDigest ( unsigned char * digest , robj * o ) {
o = getDecodedObject ( o ) ;
mixDigest ( digest , o - > ptr , sdslen ( o - > ptr ) ) ;
decrRefCount ( o ) ;
}
/* Compute the dataset digest. Since keys, sets elements, hashes elements
* are not ordered , we use a trick : every aggregate digest is the xor
* of the digests of their elements . This way the order will not change
* the result . For list instead we use a feedback entering the output digest
* as input in order to ensure that a different ordered list will result in
* a different digest . */
void computeDatasetDigest ( unsigned char * final ) {
unsigned char digest [ 20 ] ;
char buf [ 128 ] ;
dictIterator * di = NULL ;
dictEntry * de ;
int j ;
uint32_t aux ;
memset ( final , 0 , 20 ) ; /* Start with a clean result */
for ( j = 0 ; j < server . dbnum ; j + + ) {
redisDb * db = server . db + j ;
if ( dictSize ( db - > dict ) = = 0 ) continue ;
2016-12-24 10:27:58 -05:00
di = dictGetSafeIterator ( db - > dict ) ;
2010-06-21 18:07:48 -04:00
/* hash the DB id, so the same dataset moved in a different
* DB will lead to a different digest */
aux = htonl ( j ) ;
mixDigest ( final , & aux , sizeof ( aux ) ) ;
/* Iterate this DB writing every entry */
while ( ( de = dictNext ( di ) ) ! = NULL ) {
sds key ;
robj * keyobj , * o ;
2011-11-11 19:04:27 -05:00
long long expiretime ;
2010-06-21 18:07:48 -04:00
memset ( digest , 0 , 20 ) ; /* This key-val digest */
2011-11-08 11:07:55 -05:00
key = dictGetKey ( de ) ;
2010-06-21 18:07:48 -04:00
keyobj = createStringObject ( key , sdslen ( key ) ) ;
mixDigest ( digest , key , sdslen ( key ) ) ;
2011-11-08 11:07:55 -05:00
o = dictGetVal ( de ) ;
2010-06-21 18:07:48 -04:00
aux = htonl ( o - > type ) ;
mixDigest ( digest , & aux , sizeof ( aux ) ) ;
expiretime = getExpire ( db , keyobj ) ;
/* Save the key and associated value */
2015-07-26 09:28:00 -04:00
if ( o - > type = = OBJ_STRING ) {
2010-06-21 18:07:48 -04:00
mixObjectDigest ( digest , o ) ;
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_LIST ) {
2015-07-27 03:41:48 -04:00
listTypeIterator * li = listTypeInitIterator ( o , 0 , LIST_TAIL ) ;
2010-06-21 18:07:48 -04:00
listTypeEntry entry ;
while ( listTypeNext ( li , & entry ) ) {
robj * eleobj = listTypeGet ( & entry ) ;
mixObjectDigest ( digest , eleobj ) ;
decrRefCount ( eleobj ) ;
}
listTypeReleaseIterator ( li ) ;
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_SET ) {
2010-08-21 05:25:13 -04:00
setTypeIterator * si = setTypeInitIterator ( o ) ;
2015-07-31 12:01:23 -04:00
sds sdsele ;
while ( ( sdsele = setTypeNextObject ( si ) ) ! = NULL ) {
2015-08-04 03:20:55 -04:00
xorDigest ( digest , sdsele , sdslen ( sdsele ) ) ;
2015-09-04 04:22:24 -04:00
sdsfree ( sdsele ) ;
2010-06-21 18:07:48 -04:00
}
2010-07-02 14:42:20 -04:00
setTypeReleaseIterator ( si ) ;
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_ZSET ) {
2011-03-14 08:30:06 -04:00
unsigned char eledigest [ 20 ] ;
2015-07-26 09:28:00 -04:00
if ( o - > encoding = = OBJ_ENCODING_ZIPLIST ) {
2011-03-14 08:30:06 -04:00
unsigned char * zl = o - > ptr ;
unsigned char * eptr , * sptr ;
unsigned char * vstr ;
unsigned int vlen ;
long long vll ;
double score ;
eptr = ziplistIndex ( zl , 0 ) ;
2015-07-26 09:29:53 -04:00
serverAssert ( eptr ! = NULL ) ;
2011-03-14 08:30:06 -04:00
sptr = ziplistNext ( zl , eptr ) ;
2015-07-26 09:29:53 -04:00
serverAssert ( sptr ! = NULL ) ;
2011-03-14 08:30:06 -04:00
while ( eptr ! = NULL ) {
2015-07-26 09:29:53 -04:00
serverAssert ( ziplistGet ( eptr , & vstr , & vlen , & vll ) ) ;
2011-03-14 08:30:06 -04:00
score = zzlGetScore ( sptr ) ;
memset ( eledigest , 0 , 20 ) ;
if ( vstr ! = NULL ) {
mixDigest ( eledigest , vstr , vlen ) ;
} else {
ll2string ( buf , sizeof ( buf ) , vll ) ;
mixDigest ( eledigest , buf , strlen ( buf ) ) ;
}
snprintf ( buf , sizeof ( buf ) , " %.17g " , score ) ;
mixDigest ( eledigest , buf , strlen ( buf ) ) ;
xorDigest ( digest , eledigest , 20 ) ;
zzlNext ( zl , & eptr , & sptr ) ;
}
2015-07-26 09:28:00 -04:00
} else if ( o - > encoding = = OBJ_ENCODING_SKIPLIST ) {
2011-03-14 08:30:06 -04:00
zset * zs = o - > ptr ;
dictIterator * di = dictGetIterator ( zs - > dict ) ;
dictEntry * de ;
while ( ( de = dictNext ( di ) ) ! = NULL ) {
2015-08-04 03:20:55 -04:00
sds sdsele = dictGetKey ( de ) ;
2011-11-08 11:07:55 -05:00
double * score = dictGetVal ( de ) ;
2011-03-14 08:30:06 -04:00
snprintf ( buf , sizeof ( buf ) , " %.17g " , * score ) ;
memset ( eledigest , 0 , 20 ) ;
2015-08-04 03:20:55 -04:00
mixDigest ( eledigest , sdsele , sdslen ( sdsele ) ) ;
2011-03-14 08:30:06 -04:00
mixDigest ( eledigest , buf , strlen ( buf ) ) ;
xorDigest ( digest , eledigest , 20 ) ;
}
dictReleaseIterator ( di ) ;
} else {
2015-07-27 03:41:48 -04:00
serverPanic ( " Unknown sorted set encoding " ) ;
2010-06-21 18:07:48 -04:00
}
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_HASH ) {
2015-09-23 03:33:23 -04:00
hashTypeIterator * hi = hashTypeInitIterator ( o ) ;
2015-07-26 17:17:55 -04:00
while ( hashTypeNext ( hi ) ! = C_ERR ) {
2010-06-21 18:07:48 -04:00
unsigned char eledigest [ 20 ] ;
2015-09-23 03:33:23 -04:00
sds sdsele ;
2010-06-21 18:07:48 -04:00
memset ( eledigest , 0 , 20 ) ;
2015-09-23 03:33:23 -04:00
sdsele = hashTypeCurrentObjectNewSds ( hi , OBJ_HASH_KEY ) ;
mixDigest ( eledigest , sdsele , sdslen ( sdsele ) ) ;
sdsfree ( sdsele ) ;
sdsele = hashTypeCurrentObjectNewSds ( hi , OBJ_HASH_VALUE ) ;
mixDigest ( eledigest , sdsele , sdslen ( sdsele ) ) ;
sdsfree ( sdsele ) ;
2010-06-21 18:07:48 -04:00
xorDigest ( digest , eledigest , 20 ) ;
}
hashTypeReleaseIterator ( hi ) ;
2017-12-01 09:04:05 -05:00
} else if ( o - > type = = OBJ_STREAM ) {
streamIterator si ;
streamIteratorStart ( & si , o - > ptr , NULL , NULL , 0 ) ;
streamID id ;
int64_t numfields ;
while ( streamIteratorGetID ( & si , & id , & numfields ) ) {
sds itemid = sdscatfmt ( sdsempty ( ) , " %U.%U " , id . ms , id . seq ) ;
mixDigest ( digest , itemid , sdslen ( itemid ) ) ;
sdsfree ( itemid ) ;
while ( numfields - - ) {
unsigned char * field , * value ;
int64_t field_len , value_len ;
streamIteratorGetField ( & si , & field , & value ,
& field_len , & value_len ) ;
mixDigest ( digest , field , field_len ) ;
mixDigest ( digest , value , value_len ) ;
}
}
streamIteratorStop ( & si ) ;
2017-07-06 04:29:19 -04:00
} else if ( o - > type = = OBJ_MODULE ) {
RedisModuleDigest md ;
moduleValue * mv = o - > ptr ;
moduleType * mt = mv - > type ;
moduleInitDigestContext ( md ) ;
if ( mt - > digest ) {
mt - > digest ( & md , mv - > value ) ;
xorDigest ( digest , md . x , sizeof ( md . x ) ) ;
}
2010-06-21 18:07:48 -04:00
} else {
2015-07-27 03:41:48 -04:00
serverPanic ( " Unknown object type " ) ;
2010-06-21 18:07:48 -04:00
}
/* If the key has an expire, add it to the mix */
if ( expiretime ! = - 1 ) xorDigest ( digest , " !!expire!! " , 10 ) ;
/* We can finally xor the key-val digest to the final digest */
xorDigest ( final , digest , 20 ) ;
decrRefCount ( keyobj ) ;
}
dictReleaseIterator ( di ) ;
}
}
2015-07-26 09:20:46 -04:00
void debugCommand ( client * c ) {
2017-11-27 10:57:44 -05:00
if ( c - > argc = = 2 & & ! strcasecmp ( c - > argv [ 1 ] - > ptr , " help " ) ) {
const char * help [ ] = {
2017-12-06 06:05:11 -05:00
" assert -- Crash by assertion failed. " ,
" change-repl-id -- Change the replication IDs of the instance. Dangerous, should be used only for testing the replication subsystem. " ,
2018-05-25 04:49:23 -04:00
" crash-and-recover <milliseconds> -- Hard crash and restart after <milliseconds> delay. " ,
2017-12-06 06:05:11 -05:00
" digest -- Outputs an hex signature representing the current DB content. " ,
" htstats <dbid> -- Return hash table statistics of the specified Redis database. " ,
" loadaof -- Flush the AOF buffers on disk and reload the AOF in memory. " ,
" lua-always-replicate-commands (0|1) -- Setting it to 1 makes Lua replication defaulting to replicating single commands, without the script having to enable effects replication. " ,
" object <key> -- Show low level info about key and associated value. " ,
" panic -- Crash the server simulating a panic. " ,
" populate <count> [prefix] [size] -- Create <count> string keys named key:<num>. If a prefix is specified is used instead of the 'key' prefix. " ,
" reload -- Save the RDB on disk and reload it back in memory. " ,
" restart -- Graceful restart: save config, db, restart. " ,
" sdslen <key> -- Show low level SDS string info representing key and value. " ,
" segfault -- Crash the server with sigsegv. " ,
" set-active-expire (0|1) -- Setting it to 0 disables expiring keys in background when they are not accessed (otherwise the Redis behavior). Setting it to 1 reenables back the default. " ,
" sleep <seconds> -- Stop the server for <seconds>. Decimals allowed. " ,
" structsize -- Return the size of different Redis core C structures. " ,
" ziplist <key> -- Show low level info about the ziplist encoding. " ,
" error <string> -- Return a Redis protocol error with <string> as message. Useful for clients unit tests to simulate Redis errors. " ,
NULL
2017-11-27 10:57:44 -05:00
} ;
addReplyHelp ( c , help ) ;
2016-05-04 06:45:55 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " segfault " ) ) {
2010-06-21 18:07:48 -04:00
* ( ( char * ) - 1 ) = ' x ' ;
2017-01-18 11:05:10 -05:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " panic " ) ) {
serverPanic ( " DEBUG PANIC called at Unix time %ld " , time ( NULL ) ) ;
2015-10-13 05:08:24 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " restart " ) | |
! strcasecmp ( c - > argv [ 1 ] - > ptr , " crash-and-recover " ) )
{
long long delay = 0 ;
if ( c - > argc > = 3 ) {
if ( getLongLongFromObjectOrReply ( c , c - > argv [ 2 ] , & delay , NULL )
! = C_OK ) return ;
if ( delay < 0 ) delay = 0 ;
}
int flags = ! strcasecmp ( c - > argv [ 1 ] - > ptr , " restart " ) ?
( RESTART_SERVER_GRACEFULLY | RESTART_SERVER_CONFIG_REWRITE ) :
RESTART_SERVER_NONE ;
restartServer ( flags , delay ) ;
addReplyError ( c , " failed to restart the server. Check server logs. " ) ;
2012-08-24 06:55:37 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " oom " ) ) {
void * ptr = zmalloc ( ULONG_MAX ) ; /* Should trigger an out of memory. */
zfree ( ptr ) ;
addReply ( c , shared . ok ) ;
2011-10-04 11:22:29 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " assert " ) ) {
if ( c - > argc > = 3 ) c - > argv [ 2 ] = tryObjectEncoding ( c - > argv [ 2 ] ) ;
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( c , c - > argv [ 0 ] , 1 = = 2 ) ;
2010-06-21 18:07:48 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " reload " ) ) {
2017-09-20 01:47:42 -04:00
rdbSaveInfo rsi , * rsiptr ;
rsiptr = rdbPopulateSaveInfo ( & rsi ) ;
if ( rdbSave ( server . rdb_filename , rsiptr ) ! = C_OK ) {
2010-06-21 18:07:48 -04:00
addReply ( c , shared . err ) ;
return ;
}
2015-09-28 04:47:45 -04:00
emptyDb ( - 1 , EMPTYDB_NO_FLAGS , NULL ) ;
PSYNC2: different improvements to Redis replication.
The gist of the changes is that now, partial resynchronizations between
slaves and masters (without the need of a full resync with RDB transfer
and so forth), work in a number of cases when it was impossible
in the past. For instance:
1. When a slave is promoted to mastrer, the slaves of the old master can
partially resynchronize with the new master.
2. Chained slalves (slaves of slaves) can be moved to replicate to other
slaves or the master itsef, without requiring a full resync.
3. The master itself, after being turned into a slave, is able to
partially resynchronize with the new master, when it joins replication
again.
In order to obtain this, the following main changes were operated:
* Slaves also take a replication backlog, not just masters.
* Same stream replication for all the slaves and sub slaves. The
replication stream is identical from the top level master to its slaves
and is also the same from the slaves to their sub-slaves and so forth.
This means that if a slave is later promoted to master, it has the
same replication backlong, and can partially resynchronize with its
slaves (that were previously slaves of the old master).
* A given replication history is no longer identified by the `runid` of
a Redis node. There is instead a `replication ID` which changes every
time the instance has a new history no longer coherent with the past
one. So, for example, slaves publish the same replication history of
their master, however when they are turned into masters, they publish
a new replication ID, but still remember the old ID, so that they are
able to partially resynchronize with slaves of the old master (up to a
given offset).
* The replication protocol was slightly modified so that a new extended
+CONTINUE reply from the master is able to inform the slave of a
replication ID change.
* REPLCONF CAPA is used in order to notify masters that a slave is able
to understand the new +CONTINUE reply.
* The RDB file was extended with an auxiliary field that is able to
select a given DB after loading in the slave, so that the slave can
continue receiving the replication stream from the point it was
disconnected without requiring the master to insert "SELECT" statements.
This is useful in order to guarantee the "same stream" property, because
the slave must be able to accumulate an identical backlog.
* Slave pings to sub-slaves are now sent in a special form, when the
top-level master is disconnected, in order to don't interfer with the
replication stream. We just use out of band "\n" bytes as in other parts
of the Redis protocol.
An old design document is available here:
https://gist.github.com/antirez/ae068f95c0d084891305
However the implementation is not identical to the description because
during the work to implement it, different changes were needed in order
to make things working well.
2016-11-09 05:31:06 -05:00
if ( rdbLoad ( server . rdb_filename , NULL ) ! = C_OK ) {
2011-10-14 08:30:41 -04:00
addReplyError ( c , " Error trying to load the RDB dump " ) ;
2010-06-21 18:07:48 -04:00
return ;
}
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " DB reloaded by DEBUG RELOAD " ) ;
2010-06-21 18:07:48 -04:00
addReply ( c , shared . ok ) ;
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " loadaof " ) ) {
2015-10-30 06:57:36 -04:00
if ( server . aof_state = = AOF_ON ) flushAppendOnlyFile ( 1 ) ;
2015-09-28 04:47:45 -04:00
emptyDb ( - 1 , EMPTYDB_NO_FLAGS , NULL ) ;
2015-07-26 17:17:55 -04:00
if ( loadAppendOnlyFile ( server . aof_filename ) ! = C_OK ) {
2010-06-21 18:07:48 -04:00
addReply ( c , shared . err ) ;
return ;
}
2011-12-20 11:52:57 -05:00
server . dirty = 0 ; /* Prevent AOF / replication */
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Append Only File loaded by DEBUG LOADAOF " ) ;
2010-06-21 18:07:48 -04:00
addReply ( c , shared . ok ) ;
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " object " ) & & c - > argc = = 3 ) {
2011-01-07 20:06:01 -05:00
dictEntry * de ;
2010-06-21 18:07:48 -04:00
robj * val ;
2010-12-29 10:58:57 -05:00
char * strenc ;
2010-06-21 18:07:48 -04:00
2011-01-07 20:06:01 -05:00
if ( ( de = dictFind ( c - > db - > dict , c - > argv [ 2 ] - > ptr ) ) = = NULL ) {
2010-06-21 18:07:48 -04:00
addReply ( c , shared . nokeyerr ) ;
return ;
}
2011-11-08 11:07:55 -05:00
val = dictGetVal ( de ) ;
2010-12-29 10:58:57 -05:00
strenc = strEncoding ( val - > encoding ) ;
2011-01-01 15:35:56 -05:00
2017-12-02 22:51:35 -05:00
char extra [ 138 ] = { 0 } ;
2015-07-26 09:28:00 -04:00
if ( val - > encoding = = OBJ_ENCODING_QUICKLIST ) {
2014-12-10 22:54:19 -05:00
char * nextra = extra ;
int remaining = sizeof ( extra ) ;
quicklist * ql = val - > ptr ;
2014-12-19 10:41:52 -05:00
/* Add number of quicklist nodes */
2017-12-02 22:51:35 -05:00
int used = snprintf ( nextra , remaining , " ql_nodes:%lu " , ql - > len ) ;
2014-12-10 22:54:19 -05:00
nextra + = used ;
remaining - = used ;
2014-12-19 10:41:52 -05:00
/* Add average quicklist fill factor */
double avg = ( double ) ql - > count / ql - > len ;
used = snprintf ( nextra , remaining , " ql_avg_node:%.2f " , avg ) ;
nextra + = used ;
remaining - = used ;
/* Add quicklist fill level / max ziplist size */
used = snprintf ( nextra , remaining , " ql_ziplist_max:%d " , ql - > fill ) ;
nextra + = used ;
remaining - = used ;
/* Add isCompressed? */
int compressed = ql - > compress ! = 0 ;
used = snprintf ( nextra , remaining , " ql_compressed:%d " , compressed ) ;
nextra + = used ;
remaining - = used ;
/* Add total uncompressed size */
unsigned long sz = 0 ;
for ( quicklistNode * node = ql - > head ; node ; node = node - > next ) {
sz + = node - > sz ;
}
used = snprintf ( nextra , remaining , " ql_uncompressed_size:%lu " , sz ) ;
nextra + = used ;
remaining - = used ;
2014-12-10 22:54:19 -05:00
}
2010-12-29 10:58:57 -05:00
addReplyStatusFormat ( c ,
" Value at:%p refcount:%d "
2015-01-18 15:54:30 -05:00
" encoding:%s serializedlength:%zu "
2014-12-10 22:54:19 -05:00
" lru:%d lru_seconds_idle:%llu%s " ,
2010-12-29 10:58:57 -05:00
( void * ) val , val - > refcount ,
2015-01-18 15:54:30 -05:00
strenc , rdbSavedObjectLen ( val ) ,
2014-12-10 22:54:19 -05:00
val - > lru , estimateObjectIdleTime ( val ) / 1000 , extra ) ;
2013-08-27 05:52:12 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " sdslen " ) & & c - > argc = = 3 ) {
dictEntry * de ;
robj * val ;
sds key ;
if ( ( de = dictFind ( c - > db - > dict , c - > argv [ 2 ] - > ptr ) ) = = NULL ) {
addReply ( c , shared . nokeyerr ) ;
return ;
}
val = dictGetVal ( de ) ;
key = dictGetKey ( de ) ;
2015-07-26 09:28:00 -04:00
if ( val - > type ! = OBJ_STRING | | ! sdsEncodedObject ( val ) ) {
2013-08-27 05:52:12 -04:00
addReplyError ( c , " Not an sds encoded string. " ) ;
} else {
addReplyStatusFormat ( c ,
2016-05-18 01:08:43 -04:00
" key_sds_len:%lld, key_sds_avail:%lld, key_zmalloc: %lld, "
" val_sds_len:%lld, val_sds_avail:%lld, val_zmalloc: %lld " ,
2013-08-27 05:52:12 -04:00
( long long ) sdslen ( key ) ,
( long long ) sdsavail ( key ) ,
2016-05-18 01:08:43 -04:00
( long long ) sdsZmallocSize ( key ) ,
2013-08-27 05:52:12 -04:00
( long long ) sdslen ( val - > ptr ) ,
2016-05-18 01:08:43 -04:00
( long long ) sdsavail ( val - > ptr ) ,
( long long ) getStringObjectSdsUsedMemory ( val ) ) ;
2013-08-27 05:52:12 -04:00
}
2016-12-16 03:02:50 -05:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " ziplist " ) & & c - > argc = = 3 ) {
robj * o ;
if ( ( o = objectCommandLookupOrReply ( c , c - > argv [ 2 ] , shared . nokeyerr ) )
= = NULL ) return ;
if ( o - > encoding ! = OBJ_ENCODING_ZIPLIST ) {
addReplyError ( c , " Not an sds encoded string. " ) ;
} else {
ziplistRepr ( o - > ptr ) ;
addReplyStatus ( c , " Ziplist structure printed on stdout " ) ;
}
2014-09-25 11:01:56 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " populate " ) & &
2016-12-29 20:37:52 -05:00
c - > argc > = 3 & & c - > argc < = 5 ) {
2010-06-21 18:07:48 -04:00
long keys , j ;
robj * key , * val ;
char buf [ 128 ] ;
2015-07-26 17:17:55 -04:00
if ( getLongFromObjectOrReply ( c , c - > argv [ 2 ] , & keys , NULL ) ! = C_OK )
2010-06-21 18:07:48 -04:00
return ;
2014-05-09 09:02:29 -04:00
dictExpand ( c - > db - > dict , keys ) ;
2010-06-21 18:07:48 -04:00
for ( j = 0 ; j < keys ; j + + ) {
2016-12-29 20:37:52 -05:00
long valsize = 0 ;
2014-09-25 11:01:56 -04:00
snprintf ( buf , sizeof ( buf ) , " %s:%lu " ,
2014-10-09 05:17:27 -04:00
( c - > argc = = 3 ) ? " key " : ( char * ) c - > argv [ 3 ] - > ptr , j ) ;
2010-06-21 18:07:48 -04:00
key = createStringObject ( buf , strlen ( buf ) ) ;
2016-12-29 20:37:52 -05:00
if ( c - > argc = = 5 )
if ( getLongFromObjectOrReply ( c , c - > argv [ 4 ] , & valsize , NULL ) ! = C_OK )
return ;
Better read-only behavior for expired keys in slaves.
Slaves key expire is orchestrated by the master. Sometimes the master
will send the synthesized DEL to expire keys on the slave with a non
trivial delay (when the key is not accessed, only the incremental expiry
algorithm will expire it in background).
During that time, a key is logically expired, but slaves still return
the key if you GET (or whatever) it. This is a bad behavior.
However we can't simply trust the slave view of the key, since we need
the master to be able to send write commands to update the slave data
set, and DELs should only happen when the key is expired in the master
in order to ensure consistency.
However 99.99% of the issues with this behavior is when a client which
is not a master sends a read only command. In this case we are safe and
can consider the key as non existing.
This commit does a few changes in order to make this sane:
1. lookupKeyRead() is modified in order to return NULL if the above
conditions are met.
2. Calls to lookupKeyRead() in commands actually writing to the data set
are repliaced with calls to lookupKeyWrite().
There are redundand checks, so for example, if in "2" something was
overlooked, we should be still safe, since anyway, when the master
writes the behavior is to don't care about what expireIfneeded()
returns.
This commit is related to #1768, #1770, #2131.
2014-12-10 10:10:21 -05:00
if ( lookupKeyWrite ( c - > db , key ) ! = NULL ) {
2010-06-21 18:07:48 -04:00
decrRefCount ( key ) ;
continue ;
}
snprintf ( buf , sizeof ( buf ) , " value:%lu " , j ) ;
2016-12-29 20:37:52 -05:00
if ( valsize = = 0 )
val = createStringObject ( buf , strlen ( buf ) ) ;
else {
2017-01-02 02:42:32 -05:00
int buflen = strlen ( buf ) ;
2016-12-29 20:37:52 -05:00
val = createStringObject ( NULL , valsize ) ;
2017-01-02 02:42:32 -05:00
memcpy ( val - > ptr , buf , valsize < = buflen ? valsize : buflen ) ;
2016-12-29 20:37:52 -05:00
}
2010-06-21 18:07:48 -04:00
dbAdd ( c - > db , key , val ) ;
2016-04-25 09:47:42 -04:00
signalModifiedKey ( c - > db , key ) ;
2010-06-21 18:07:48 -04:00
decrRefCount ( key ) ;
}
addReply ( c , shared . ok ) ;
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " digest " ) & & c - > argc = = 2 ) {
unsigned char digest [ 20 ] ;
2010-09-02 13:52:24 -04:00
sds d = sdsempty ( ) ;
2010-06-21 18:07:48 -04:00
int j ;
computeDatasetDigest ( digest ) ;
for ( j = 0 ; j < 20 ; j + + )
d = sdscatprintf ( d , " %02x " , digest [ j ] ) ;
2010-09-02 13:52:24 -04:00
addReplyStatus ( c , d ) ;
sdsfree ( d ) ;
2011-06-30 07:31:44 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " sleep " ) & & c - > argc = = 3 ) {
double dtime = strtod ( c - > argv [ 2 ] - > ptr , NULL ) ;
long long utime = dtime * 1000000 ;
2012-07-19 08:37:34 -04:00
struct timespec tv ;
2011-06-30 07:31:44 -04:00
2012-07-19 08:37:34 -04:00
tv . tv_sec = utime / 1000000 ;
tv . tv_nsec = ( utime % 1000000 ) * 1000 ;
nanosleep ( & tv , NULL ) ;
2011-06-30 07:31:44 -04:00
addReply ( c , shared . ok ) ;
2013-03-27 12:55:02 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " set-active-expire " ) & &
c - > argc = = 3 )
{
server . active_expire_enabled = atoi ( c - > argv [ 2 ] - > ptr ) ;
addReply ( c , shared . ok ) ;
2015-10-30 05:13:04 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " lua-always-replicate-commands " ) & &
c - > argc = = 3 )
{
server . lua_always_replicate_commands = atoi ( c - > argv [ 2 ] - > ptr ) ;
addReply ( c , shared . ok ) ;
2014-03-10 18:01:55 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " error " ) & & c - > argc = = 3 ) {
sds errstr = sdsnewlen ( " - " , 1 ) ;
errstr = sdscatsds ( errstr , c - > argv [ 2 ] - > ptr ) ;
errstr = sdsmapchars ( errstr , " \n \r " , " " , 2 ) ; /* no newlines in errors. */
errstr = sdscatlen ( errstr , " \r \n " , 2 ) ;
addReplySds ( c , errstr ) ;
2015-01-23 12:10:14 -05:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " structsize " ) & & c - > argc = = 2 ) {
sds sizes = sdsempty ( ) ;
2015-07-14 10:04:00 -04:00
sizes = sdscatprintf ( sizes , " bits:%d " , ( sizeof ( void * ) = = 8 ) ? 64 : 32 ) ;
sizes = sdscatprintf ( sizes , " robj:%d " , ( int ) sizeof ( robj ) ) ;
sizes = sdscatprintf ( sizes , " dictentry:%d " , ( int ) sizeof ( dictEntry ) ) ;
2015-07-16 03:14:39 -04:00
sizes = sdscatprintf ( sizes , " sdshdr5:%d " , ( int ) sizeof ( struct sdshdr5 ) ) ;
2015-07-14 10:04:00 -04:00
sizes = sdscatprintf ( sizes , " sdshdr8:%d " , ( int ) sizeof ( struct sdshdr8 ) ) ;
sizes = sdscatprintf ( sizes , " sdshdr16:%d " , ( int ) sizeof ( struct sdshdr16 ) ) ;
sizes = sdscatprintf ( sizes , " sdshdr32:%d " , ( int ) sizeof ( struct sdshdr32 ) ) ;
sizes = sdscatprintf ( sizes , " sdshdr64:%d " , ( int ) sizeof ( struct sdshdr64 ) ) ;
2015-01-23 12:10:14 -05:00
addReplyBulkSds ( c , sizes ) ;
2015-07-14 11:15:37 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " htstats " ) & & c - > argc = = 3 ) {
long dbid ;
sds stats = sdsempty ( ) ;
char buf [ 4096 ] ;
2015-07-26 17:17:55 -04:00
if ( getLongFromObjectOrReply ( c , c - > argv [ 2 ] , & dbid , NULL ) ! = C_OK )
2015-07-14 11:15:37 -04:00
return ;
if ( dbid < 0 | | dbid > = server . dbnum ) {
addReplyError ( c , " Out of range database " ) ;
return ;
}
stats = sdscatprintf ( stats , " [Dictionary HT] \n " ) ;
dictGetStats ( buf , sizeof ( buf ) , server . db [ dbid ] . dict ) ;
stats = sdscat ( stats , buf ) ;
stats = sdscatprintf ( stats , " [Expires HT] \n " ) ;
dictGetStats ( buf , sizeof ( buf ) , server . db [ dbid ] . expires ) ;
stats = sdscat ( stats , buf ) ;
addReplyBulkSds ( c , stats ) ;
2017-12-04 04:24:52 -05:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " change-repl-id " ) & & c - > argc = = 2 ) {
serverLog ( LL_WARNING , " Changing replication IDs after receiving DEBUG change-repl-id " ) ;
changeReplicationId ( ) ;
clearReplicationId2 ( ) ;
addReply ( c , shared . ok ) ;
2010-06-21 18:07:48 -04:00
} else {
2017-12-06 06:05:11 -05:00
addReplyErrorFormat ( c , " Unknown subcommand or wrong number of arguments for '%s'. Try DEBUG HELP " ,
2013-03-27 12:55:02 -04:00
( char * ) c - > argv [ 1 ] - > ptr ) ;
2017-11-27 10:57:44 -05:00
return ;
2010-06-21 18:07:48 -04:00
}
}
2012-01-20 06:20:45 -05:00
/* =========================== Crash handling ============================== */
2016-06-20 16:08:06 -04:00
void _serverAssert ( const char * estr , const char * file , int line ) {
2011-11-24 09:47:26 -05:00
bugReportStart ( ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " === ASSERTION FAILED === " ) ;
serverLog ( LL_WARNING , " ==> %s:%d '%s' is not true " , file , line , estr ) ;
2010-06-21 18:07:48 -04:00
# ifdef HAVE_BACKTRACE
2011-11-24 09:47:26 -05:00
server . assert_failed = estr ;
server . assert_file = file ;
server . assert_line = line ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " (forcing SIGSEGV to print the bug report.) " ) ;
2010-06-21 18:07:48 -04:00
# endif
2012-02-21 13:05:46 -05:00
* ( ( char * ) - 1 ) = ' x ' ;
2010-06-21 18:07:48 -04:00
}
2016-06-20 16:08:06 -04:00
void _serverAssertPrintClientInfo ( const client * c ) {
2011-10-04 12:05:26 -04:00
int j ;
2011-10-04 11:22:29 -04:00
2011-11-24 09:47:26 -05:00
bugReportStart ( ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " === ASSERTION FAILED CLIENT CONTEXT === " ) ;
serverLog ( LL_WARNING , " client->flags = %d " , c - > flags ) ;
serverLog ( LL_WARNING , " client->fd = %d " , c - > fd ) ;
serverLog ( LL_WARNING , " client->argc = %d " , c - > argc ) ;
2011-10-04 12:05:26 -04:00
for ( j = 0 ; j < c - > argc ; j + + ) {
char buf [ 128 ] ;
char * arg ;
2015-07-26 09:28:00 -04:00
if ( c - > argv [ j ] - > type = = OBJ_STRING & & sdsEncodedObject ( c - > argv [ j ] ) ) {
2011-10-04 12:05:26 -04:00
arg = ( char * ) c - > argv [ j ] - > ptr ;
} else {
2015-11-28 03:05:41 -05:00
snprintf ( buf , sizeof ( buf ) , " Object type: %u, encoding: %u " ,
2011-10-04 12:05:26 -04:00
c - > argv [ j ] - > type , c - > argv [ j ] - > encoding ) ;
arg = buf ;
2011-10-04 11:22:29 -04:00
}
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " client->argv[%d] = \" %s \" (refcount: %d) " ,
2011-10-04 12:05:26 -04:00
j , arg , c - > argv [ j ] - > refcount ) ;
}
}
2016-06-20 16:08:06 -04:00
void serverLogObjectDebugInfo ( const robj * o ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Object type: %d " , o - > type ) ;
serverLog ( LL_WARNING , " Object encoding: %d " , o - > encoding ) ;
serverLog ( LL_WARNING , " Object refcount: %d " , o - > refcount ) ;
2015-07-26 09:28:00 -04:00
if ( o - > type = = OBJ_STRING & & sdsEncodedObject ( o ) ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Object raw string len: %zu " , sdslen ( o - > ptr ) ) ;
2013-06-04 09:53:53 -04:00
if ( sdslen ( o - > ptr ) < 4096 ) {
sds repr = sdscatrepr ( sdsempty ( ) , o - > ptr , sdslen ( o - > ptr ) ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Object raw string content: %s " , repr ) ;
2013-06-04 09:53:53 -04:00
sdsfree ( repr ) ;
}
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_LIST ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " List length: %d " , ( int ) listTypeLength ( o ) ) ;
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_SET ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Set size: %d " , ( int ) setTypeSize ( o ) ) ;
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_HASH ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Hash size: %d " , ( int ) hashTypeLength ( o ) ) ;
2015-07-26 09:28:00 -04:00
} else if ( o - > type = = OBJ_ZSET ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Sorted set size: %d " , ( int ) zsetLength ( o ) ) ;
2015-07-26 09:28:00 -04:00
if ( o - > encoding = = OBJ_ENCODING_SKIPLIST )
2016-06-20 16:08:06 -04:00
serverLog ( LL_WARNING , " Skiplist level: %d " , ( int ) ( ( const zset * ) o - > ptr ) - > zsl - > level ) ;
2011-10-04 11:22:29 -04:00
}
}
2016-06-20 16:08:06 -04:00
void _serverAssertPrintObject ( const robj * o ) {
2012-01-12 10:02:57 -05:00
bugReportStart ( ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " === ASSERTION FAILED OBJECT CONTEXT === " ) ;
2015-07-26 09:17:43 -04:00
serverLogObjectDebugInfo ( o ) ;
2012-01-12 10:02:57 -05:00
}
2016-06-20 16:08:06 -04:00
void _serverAssertWithInfo ( const client * c , const robj * o , const char * estr , const char * file , int line ) {
2015-07-26 09:29:53 -04:00
if ( c ) _serverAssertPrintClientInfo ( c ) ;
if ( o ) _serverAssertPrintObject ( o ) ;
_serverAssert ( estr , file , line ) ;
2011-10-04 11:22:29 -04:00
}
2017-01-18 11:05:10 -05:00
void _serverPanic ( const char * file , int line , const char * msg , . . . ) {
va_list ap ;
va_start ( ap , msg ) ;
char fmtmsg [ 256 ] ;
vsnprintf ( fmtmsg , sizeof ( fmtmsg ) , msg , ap ) ;
va_end ( ap ) ;
2011-11-24 09:47:26 -05:00
bugReportStart ( ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " ------------------------------------------------ " ) ;
serverLog ( LL_WARNING , " !!! Software Failure. Press left mouse button to continue " ) ;
2017-01-18 11:05:10 -05:00
serverLog ( LL_WARNING , " Guru Meditation: %s #%s:%d " , fmtmsg , file , line ) ;
2010-06-21 18:07:48 -04:00
# ifdef HAVE_BACKTRACE
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " (forcing SIGSEGV in order to print the stack trace) " ) ;
2012-02-21 13:05:46 -05:00
# endif
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " ------------------------------------------------ " ) ;
2010-06-21 18:07:48 -04:00
* ( ( char * ) - 1 ) = ' x ' ;
}
2012-01-20 06:20:45 -05:00
2012-02-08 16:24:59 -05:00
void bugReportStart ( void ) {
if ( server . bug_report_start = = 0 ) {
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW ,
" \n \n === REDIS BUG REPORT START: Cut & paste starting from here === \n " ) ;
2012-02-08 16:24:59 -05:00
server . bug_report_start = 1 ;
}
}
2012-01-20 06:20:45 -05:00
# ifdef HAVE_BACKTRACE
static void * getMcontextEip ( ucontext_t * uc ) {
2012-04-24 05:07:15 -04:00
# if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
/* OSX < 10.6 */
# if defined(__x86_64__)
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext - > __ss . __rip ;
2012-04-24 05:07:15 -04:00
# elif defined(__i386__)
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext - > __ss . __eip ;
2012-04-24 05:07:15 -04:00
# else
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext - > __ss . __srr0 ;
2012-04-24 05:07:15 -04:00
# endif
2012-01-20 06:20:45 -05:00
# elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
2012-04-24 05:07:15 -04:00
/* OSX >= 10.6 */
# if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext - > __ss . __rip ;
2012-04-24 05:07:15 -04:00
# else
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext - > __ss . __eip ;
2012-04-24 05:07:15 -04:00
# endif
# elif defined(__linux__)
/* Linux */
# if defined(__i386__)
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext . gregs [ 14 ] ; /* Linux 32 */
2012-04-24 05:07:15 -04:00
# elif defined(__X86_64__) || defined(__x86_64__)
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext . gregs [ 16 ] ; /* Linux 64 */
2012-04-24 05:07:15 -04:00
# elif defined(__ia64__) /* Linux IA64 */
2012-01-20 06:20:45 -05:00
return ( void * ) uc - > uc_mcontext . sc_ip ;
2016-07-04 16:28:32 -04:00
# elif defined(__arm__) /* Linux ARM */
return ( void * ) uc - > uc_mcontext . arm_pc ;
2017-07-03 03:18:32 -04:00
# elif defined(__aarch64__) /* Linux AArch64 */
return ( void * ) uc - > uc_mcontext . pc ;
2012-04-24 05:07:15 -04:00
# endif
2012-01-20 06:20:45 -05:00
# else
return NULL ;
# endif
}
void logStackContent ( void * * sp ) {
int i ;
for ( i = 15 ; i > = 0 ; i - - ) {
2013-02-27 06:27:15 -05:00
unsigned long addr = ( unsigned long ) sp + i ;
unsigned long val = ( unsigned long ) sp [ i ] ;
2012-01-20 10:40:43 -05:00
if ( sizeof ( long ) = = 4 )
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " (%08lx) -> %08lx " , addr , val ) ;
2012-01-20 10:40:43 -05:00
else
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " (%016lx) -> %016lx " , addr , val ) ;
2012-01-20 06:20:45 -05:00
}
}
void logRegisters ( ucontext_t * uc ) {
2015-12-16 03:13:41 -05:00
serverLog ( LL_WARNING | LL_RAW , " \n ------ REGISTERS ------ \n " ) ;
2012-04-24 05:07:15 -04:00
/* OSX */
2012-01-20 06:20:45 -05:00
# if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
2012-04-24 05:07:15 -04:00
/* OSX AMD64 */
# if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2012-01-20 06:20:45 -05:00
" \n "
2012-01-20 10:40:43 -05:00
" RAX:%016lx RBX:%016lx \n RCX:%016lx RDX:%016lx \n "
" RDI:%016lx RSI:%016lx \n RBP:%016lx RSP:%016lx \n "
" R8 :%016lx R9 :%016lx \n R10:%016lx R11:%016lx \n "
" R12:%016lx R13:%016lx \n R14:%016lx R15:%016lx \n "
" RIP:%016lx EFL:%016lx \n CS :%016lx FS:%016lx GS:%016lx " ,
2013-02-27 06:27:15 -05:00
( unsigned long ) uc - > uc_mcontext - > __ss . __rax ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rbx ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rcx ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rdx ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rdi ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rsi ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rbp ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rsp ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r8 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r9 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r10 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r11 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r12 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r13 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r14 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __r15 ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rip ,
( unsigned long ) uc - > uc_mcontext - > __ss . __rflags ,
( unsigned long ) uc - > uc_mcontext - > __ss . __cs ,
( unsigned long ) uc - > uc_mcontext - > __ss . __fs ,
( unsigned long ) uc - > uc_mcontext - > __ss . __gs
2012-01-20 06:20:45 -05:00
) ;
logStackContent ( ( void * * ) uc - > uc_mcontext - > __ss . __rsp ) ;
2012-04-24 05:07:15 -04:00
# else
/* OSX x86 */
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2012-01-20 06:20:45 -05:00
" \n "
2012-01-20 10:40:43 -05:00
" EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx \n "
" EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx \n "
" SS:%08lx EFL:%08lx EIP:%08lx CS :%08lx \n "
" DS:%08lx ES:%08lx FS :%08lx GS :%08lx " ,
2013-02-27 06:27:15 -05:00
( unsigned long ) uc - > uc_mcontext - > __ss . __eax ,
( unsigned long ) uc - > uc_mcontext - > __ss . __ebx ,
( unsigned long ) uc - > uc_mcontext - > __ss . __ecx ,
( unsigned long ) uc - > uc_mcontext - > __ss . __edx ,
( unsigned long ) uc - > uc_mcontext - > __ss . __edi ,
( unsigned long ) uc - > uc_mcontext - > __ss . __esi ,
( unsigned long ) uc - > uc_mcontext - > __ss . __ebp ,
( unsigned long ) uc - > uc_mcontext - > __ss . __esp ,
( unsigned long ) uc - > uc_mcontext - > __ss . __ss ,
( unsigned long ) uc - > uc_mcontext - > __ss . __eflags ,
( unsigned long ) uc - > uc_mcontext - > __ss . __eip ,
( unsigned long ) uc - > uc_mcontext - > __ss . __cs ,
( unsigned long ) uc - > uc_mcontext - > __ss . __ds ,
( unsigned long ) uc - > uc_mcontext - > __ss . __es ,
( unsigned long ) uc - > uc_mcontext - > __ss . __fs ,
( unsigned long ) uc - > uc_mcontext - > __ss . __gs
2012-01-20 06:20:45 -05:00
) ;
logStackContent ( ( void * * ) uc - > uc_mcontext - > __ss . __esp ) ;
2012-04-24 05:07:15 -04:00
# endif
/* Linux */
# elif defined(__linux__)
/* Linux x86 */
# if defined(__i386__)
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2012-01-20 08:37:50 -05:00
" \n "
2012-01-20 10:40:43 -05:00
" EAX:%08lx EBX:%08lx ECX:%08lx EDX:%08lx \n "
" EDI:%08lx ESI:%08lx EBP:%08lx ESP:%08lx \n "
" SS :%08lx EFL:%08lx EIP:%08lx CS:%08lx \n "
" DS :%08lx ES :%08lx FS :%08lx GS:%08lx " ,
2013-02-27 06:27:15 -05:00
( unsigned long ) uc - > uc_mcontext . gregs [ 11 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 8 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 10 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 9 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 4 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 5 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 6 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 7 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 18 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 17 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 14 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 15 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 3 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 2 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 1 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 0 ]
2012-01-20 08:37:50 -05:00
) ;
logStackContent ( ( void * * ) uc - > uc_mcontext . gregs [ 7 ] ) ;
2012-04-24 05:07:15 -04:00
# elif defined(__X86_64__) || defined(__x86_64__)
/* Linux AMD64 */
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2012-01-20 06:54:15 -05:00
" \n "
2012-01-20 10:40:43 -05:00
" RAX:%016lx RBX:%016lx \n RCX:%016lx RDX:%016lx \n "
" RDI:%016lx RSI:%016lx \n RBP:%016lx RSP:%016lx \n "
" R8 :%016lx R9 :%016lx \n R10:%016lx R11:%016lx \n "
" R12:%016lx R13:%016lx \n R14:%016lx R15:%016lx \n "
" RIP:%016lx EFL:%016lx \n CSGSFS:%016lx " ,
2013-02-27 06:27:15 -05:00
( unsigned long ) uc - > uc_mcontext . gregs [ 13 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 11 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 14 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 12 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 8 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 9 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 10 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 15 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 0 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 1 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 2 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 3 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 4 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 5 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 6 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 7 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 16 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 17 ] ,
( unsigned long ) uc - > uc_mcontext . gregs [ 18 ]
2012-01-20 06:54:15 -05:00
) ;
logStackContent ( ( void * * ) uc - > uc_mcontext . gregs [ 15 ] ) ;
2012-04-24 05:07:15 -04:00
# endif
2012-01-20 06:20:45 -05:00
# else
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2012-01-20 06:20:45 -05:00
" Dumping of registers not supported for this OS/arch " ) ;
# endif
}
2015-12-16 11:41:20 -05:00
/* Return a file descriptor to write directly to the Redis log with the
* write ( 2 ) syscall , that can be used in critical sections of the code
* where the rest of Redis can ' t be trusted ( for example during the memory
* test ) or when an API call requires a raw fd .
*
* Close it with closeDirectLogFiledes ( ) . */
int openDirectLogFiledes ( void ) {
int log_to_stdout = server . logfile [ 0 ] = = ' \0 ' ;
int fd = log_to_stdout ?
STDOUT_FILENO :
open ( server . logfile , O_APPEND | O_CREAT | O_WRONLY , 0644 ) ;
return fd ;
}
/* Used to close what closeDirectLogFiledes() returns. */
void closeDirectLogFiledes ( int fd ) {
int log_to_stdout = server . logfile [ 0 ] = = ' \0 ' ;
if ( ! log_to_stdout ) close ( fd ) ;
}
2012-04-26 10:04:53 -04:00
/* Logs the stack trace using the backtrace() call. This function is designed
* to be called from signal handlers safely . */
void logStackTrace ( ucontext_t * uc ) {
2015-12-16 03:13:41 -05:00
void * trace [ 101 ] ;
2015-12-16 11:41:20 -05:00
int trace_size = 0 , fd = openDirectLogFiledes ( ) ;
2012-04-26 10:04:53 -04:00
2015-12-16 11:41:20 -05:00
if ( fd = = - 1 ) return ; /* If we can't log there is anything to do. */
2012-01-20 06:20:45 -05:00
/* Generate the stack trace */
2015-12-16 03:13:41 -05:00
trace_size = backtrace ( trace + 1 , 100 ) ;
if ( getMcontextEip ( uc ) ! = NULL ) {
char * msg1 = " EIP: \n " ;
char * msg2 = " \n Backtrace: \n " ;
2015-12-16 06:36:29 -05:00
if ( write ( fd , msg1 , strlen ( msg1 ) ) = = - 1 ) { /* Avoid warning. */ } ;
2015-12-16 03:13:41 -05:00
trace [ 0 ] = getMcontextEip ( uc ) ;
backtrace_symbols_fd ( trace , 1 , fd ) ;
2015-12-16 06:36:29 -05:00
if ( write ( fd , msg2 , strlen ( msg2 ) ) = = - 1 ) { /* Avoid warning. */ } ;
2015-12-16 03:13:41 -05:00
}
2012-04-26 10:04:53 -04:00
/* Write symbols to log file */
2015-12-16 03:13:41 -05:00
backtrace_symbols_fd ( trace + 1 , trace_size , fd ) ;
2012-04-26 10:04:53 -04:00
/* Cleanup */
2015-12-16 11:41:20 -05:00
closeDirectLogFiledes ( fd ) ;
2012-03-27 04:33:45 -04:00
}
/* Log information about the "current" client, that is, the client that is
* currently being served by Redis . May be NULL if Redis is not serving a
* client right now . */
void logCurrentClient ( void ) {
if ( server . current_client = = NULL ) return ;
2015-07-26 09:20:46 -04:00
client * cc = server . current_client ;
2012-03-27 04:33:45 -04:00
sds client ;
int j ;
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW , " \n ------ CURRENT CLIENT INFO ------ \n " ) ;
2014-04-28 11:36:57 -04:00
client = catClientInfoString ( sdsempty ( ) , cc ) ;
2015-12-16 03:13:41 -05:00
serverLog ( LL_WARNING | LL_RAW , " %s \n " , client ) ;
2012-03-27 04:33:45 -04:00
sdsfree ( client ) ;
for ( j = 0 ; j < cc - > argc ; j + + ) {
robj * decoded ;
decoded = getDecodedObject ( cc - > argv [ j ] ) ;
2015-12-16 03:13:41 -05:00
serverLog ( LL_WARNING | LL_RAW , " argv[%d]: '%s' \n " , j ,
( char * ) decoded - > ptr ) ;
2012-03-27 04:33:45 -04:00
decrRefCount ( decoded ) ;
}
/* Check if the first argument, usually a key, is found inside the
* selected DB , and if so print info about the associated object . */
if ( cc - > argc > = 1 ) {
robj * val , * key ;
dictEntry * de ;
key = getDecodedObject ( cc - > argv [ 1 ] ) ;
de = dictFind ( cc - > db - > dict , key - > ptr ) ;
if ( de ) {
val = dictGetVal ( de ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " key '%s' found in DB containing the following object: " , ( char * ) key - > ptr ) ;
2015-07-26 09:17:43 -04:00
serverLogObjectDebugInfo ( val ) ;
2012-03-27 04:33:45 -04:00
}
decrRefCount ( key ) ;
}
}
2012-11-21 07:19:38 -05:00
# if defined(HAVE_PROC_MAPS)
2015-12-16 11:41:20 -05:00
2012-11-25 10:21:21 -05:00
# define MEMTEST_MAX_REGIONS 128
2012-11-21 07:19:38 -05:00
2015-12-16 11:41:20 -05:00
/* A non destructive memory test executed during segfauls. */
2012-11-21 07:19:38 -05:00
int memtest_test_linux_anonymous_maps ( void ) {
2015-12-16 11:41:20 -05:00
FILE * fp ;
2012-11-21 07:19:38 -05:00
char line [ 1024 ] ;
2015-12-16 11:41:20 -05:00
char logbuf [ 1024 ] ;
2012-11-21 07:19:38 -05:00
size_t start_addr , end_addr , size ;
2012-11-25 10:21:21 -05:00
size_t start_vect [ MEMTEST_MAX_REGIONS ] ;
size_t size_vect [ MEMTEST_MAX_REGIONS ] ;
int regions = 0 , j ;
2012-11-21 07:19:38 -05:00
2015-12-16 11:41:20 -05:00
int fd = openDirectLogFiledes ( ) ;
if ( ! fd ) return 0 ;
fp = fopen ( " /proc/self/maps " , " r " ) ;
if ( ! fp ) return 0 ;
2012-11-21 07:19:38 -05:00
while ( fgets ( line , sizeof ( line ) , fp ) ! = NULL ) {
char * start , * end , * p = line ;
start = p ;
p = strchr ( p , ' - ' ) ;
if ( ! p ) continue ;
* p + + = ' \0 ' ;
end = p ;
p = strchr ( p , ' ' ) ;
if ( ! p ) continue ;
* p + + = ' \0 ' ;
if ( strstr ( p , " stack " ) | |
strstr ( p , " vdso " ) | |
strstr ( p , " vsyscall " ) ) continue ;
if ( ! strstr ( p , " 00:00 " ) ) continue ;
if ( ! strstr ( p , " rw " ) ) continue ;
start_addr = strtoul ( start , NULL , 16 ) ;
end_addr = strtoul ( end , NULL , 16 ) ;
size = end_addr - start_addr ;
2012-11-25 10:21:21 -05:00
start_vect [ regions ] = start_addr ;
size_vect [ regions ] = size ;
2015-12-16 11:41:20 -05:00
snprintf ( logbuf , sizeof ( logbuf ) ,
" *** Preparing to test memory region %lx (%lu bytes) \n " ,
( unsigned long ) start_vect [ regions ] ,
( unsigned long ) size_vect [ regions ] ) ;
if ( write ( fd , logbuf , strlen ( logbuf ) ) = = - 1 ) { /* Nothing to do. */ }
2012-11-25 10:21:21 -05:00
regions + + ;
2012-11-21 07:19:38 -05:00
}
2012-11-25 10:21:21 -05:00
2015-12-16 11:41:20 -05:00
int errors = 0 ;
2012-11-25 10:21:21 -05:00
for ( j = 0 ; j < regions ; j + + ) {
2015-12-16 11:41:20 -05:00
if ( write ( fd , " . " , 1 ) = = - 1 ) { /* Nothing to do. */ }
errors + = memtest_preserving_test ( ( void * ) start_vect [ j ] , size_vect [ j ] , 1 ) ;
if ( write ( fd , errors ? " E " : " O " , 1 ) = = - 1 ) { /* Nothing to do. */ }
2012-11-25 10:21:21 -05:00
}
2015-12-16 11:41:20 -05:00
if ( write ( fd , " \n " , 1 ) = = - 1 ) { /* Nothing to do. */ }
2012-11-25 10:21:21 -05:00
/* NOTE: It is very important to close the file descriptor only now
* because closing it before may result into unmapping of some memory
* region that we are testing . */
2012-11-21 07:19:38 -05:00
fclose ( fp ) ;
2015-12-16 11:41:20 -05:00
closeDirectLogFiledes ( fd ) ;
return errors ;
2012-11-21 07:19:38 -05:00
}
# endif
2016-09-09 04:59:29 -04:00
/* Scans the (assumed) x86 code starting at addr, for a max of `len`
* bytes , searching for E8 ( callq ) opcodes , and dumping the symbols
* and the call offset if they appear to be valid . */
void dumpX86Calls ( void * addr , size_t len ) {
size_t j ;
unsigned char * p = addr ;
Dl_info info ;
/* Hash table to best-effort avoid printing the same symbol
* multiple times . */
unsigned long ht [ 256 ] = { 0 } ;
if ( len < 5 ) return ;
for ( j = 0 ; j < len - 4 ; j + + ) {
if ( p [ j ] ! = 0xE8 ) continue ; /* Not an E8 CALL opcode. */
unsigned long target = ( unsigned long ) addr + j + 5 ;
target + = * ( ( int32_t * ) ( p + j + 1 ) ) ;
if ( dladdr ( ( void * ) target , & info ) ! = 0 & & info . dli_sname ! = NULL ) {
if ( ht [ target & 0xff ] ! = target ) {
printf ( " Function at 0x%lx is %s \n " , target , info . dli_sname ) ;
ht [ target & 0xff ] = target ;
}
j + = 4 ; /* Skip the 32 bit immediate. */
}
}
}
2012-03-27 04:33:45 -04:00
void sigsegvHandler ( int sig , siginfo_t * info , void * secret ) {
ucontext_t * uc = ( ucontext_t * ) secret ;
2015-12-16 03:13:41 -05:00
void * eip = getMcontextEip ( uc ) ;
2012-04-26 10:04:53 -04:00
sds infostring , clients ;
2012-03-27 04:33:45 -04:00
struct sigaction act ;
2015-07-27 03:41:48 -04:00
UNUSED ( info ) ;
2012-03-27 04:33:45 -04:00
bugReportStart ( ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2015-12-16 03:13:41 -05:00
" Redis %s crashed by signal: %d " , REDIS_VERSION , sig ) ;
if ( eip ! = NULL ) {
2015-12-15 12:00:29 -05:00
serverLog ( LL_WARNING ,
2018-01-09 03:13:02 -05:00
" Crashed running the instruction at: %p " , eip ) ;
2015-12-16 03:13:41 -05:00
}
if ( sig = = SIGSEGV | | sig = = SIGBUS ) {
serverLog ( LL_WARNING ,
" Accessing address: %p " , ( void * ) info - > si_addr ) ;
2015-12-15 12:00:29 -05:00
}
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2015-12-16 03:13:41 -05:00
" Failed assertion: %s (%s:%d) " , server . assert_failed ,
2012-03-27 04:33:45 -04:00
server . assert_file , server . assert_line ) ;
/* Log the stack trace */
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW , " \n ------ STACK TRACE ------ \n " ) ;
2012-04-26 10:04:53 -04:00
logStackTrace ( uc ) ;
2012-01-20 06:20:45 -05:00
/* Log INFO and CLIENT LIST */
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW , " \n ------ INFO OUTPUT ------ \n " ) ;
2012-01-20 06:20:45 -05:00
infostring = genRedisInfoString ( " all " ) ;
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW , infostring ) ;
serverLogRaw ( LL_WARNING | LL_RAW , " \n ------ CLIENT LIST OUTPUT ------ \n " ) ;
2012-01-20 06:20:45 -05:00
clients = getAllClientsInfoString ( ) ;
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW , clients ) ;
2012-03-27 04:33:45 -04:00
sdsfree ( infostring ) ;
sdsfree ( clients ) ;
2012-01-20 06:20:45 -05:00
2012-03-27 04:33:45 -04:00
/* Log the current client */
logCurrentClient ( ) ;
2012-01-20 06:20:45 -05:00
/* Log dump of processor registers */
logRegisters ( uc ) ;
2012-11-21 07:19:38 -05:00
# if defined(HAVE_PROC_MAPS)
/* Test memory */
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW , " \n ------ FAST MEMORY TEST ------ \n " ) ;
2012-11-22 04:08:44 -05:00
bioKillThreads ( ) ;
2012-11-21 07:19:38 -05:00
if ( memtest_test_linux_anonymous_maps ( ) ) {
2015-12-16 11:41:20 -05:00
serverLogRaw ( LL_WARNING | LL_RAW ,
2016-09-09 04:59:29 -04:00
" !!! MEMORY ERROR DETECTED! Check your memory ASAP !!! \n " ) ;
2012-11-21 07:19:38 -05:00
} else {
2015-12-16 11:41:20 -05:00
serverLogRaw ( LL_WARNING | LL_RAW ,
2016-09-09 04:59:29 -04:00
" Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible. \n " ) ;
2012-11-21 07:19:38 -05:00
}
# endif
2016-09-09 04:59:29 -04:00
if ( eip ! = NULL ) {
Dl_info info ;
if ( dladdr ( eip , & info ) ! = 0 ) {
serverLog ( LL_WARNING | LL_RAW ,
" \n ------ DUMPING CODE AROUND EIP ------ \n "
" Symbol: %s (base: %p) \n "
" Module: %s (base %p) \n "
" $ xxd -r -p /tmp/dump.hex /tmp/dump.bin \n "
" $ objdump --adjust-vma=%p -D -b binary -m i386:x86-64 /tmp/dump.bin \n "
" ------ \n " ,
info . dli_sname , info . dli_saddr , info . dli_fname , info . dli_fbase ,
info . dli_saddr ) ;
size_t len = ( long ) eip - ( long ) info . dli_saddr ;
unsigned long sz = sysconf ( _SC_PAGESIZE ) ;
if ( len < 1 < < 13 ) { /* we don't have functions over 8k (verified) */
/* Find the address of the next page, which is our "safety"
* limit when dumping . Then try to dump just 128 bytes more
* than EIP if there is room , or stop sooner . */
unsigned long next = ( ( unsigned long ) eip + sz ) & ~ ( sz - 1 ) ;
unsigned long end = ( unsigned long ) eip + 128 ;
if ( end > next ) end = next ;
len = end - ( unsigned long ) info . dli_saddr ;
serverLogHexDump ( LL_WARNING , " dump of function " ,
info . dli_saddr , len ) ;
dumpX86Calls ( info . dli_saddr , len ) ;
}
}
}
2015-12-16 03:13:41 -05:00
serverLogRaw ( LL_WARNING | LL_RAW ,
2012-01-20 06:20:45 -05:00
" \n === REDIS BUG REPORT END. Make sure to include from START to END. === \n \n "
2014-08-28 10:36:32 -04:00
" Please report the crash by opening an issue on github: \n \n "
2012-03-18 06:35:35 -04:00
" http://github.com/antirez/redis/issues \n \n "
2014-05-28 03:46:01 -04:00
" Suspect RAM error? Use redis-server --test-memory to verify it. \n \n "
2012-01-20 06:20:45 -05:00
) ;
2016-09-09 04:59:29 -04:00
2012-01-20 06:20:45 -05:00
/* free(messages); Don't call free() with possibly corrupted memory. */
2015-01-12 09:58:23 -05:00
if ( server . daemonize & & server . supervised = = 0 ) unlink ( server . pidfile ) ;
2012-01-20 06:20:45 -05:00
/* Make sure we exit with the right signal at the end. So for instance
* the core will be dumped if enabled . */
sigemptyset ( & act . sa_mask ) ;
act . sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND ;
act . sa_handler = SIG_DFL ;
sigaction ( sig , & act , NULL ) ;
kill ( getpid ( ) , sig ) ;
}
# endif /* HAVE_BACKTRACE */
2012-03-27 05:47:51 -04:00
2012-06-11 17:44:34 -04:00
/* ==================== Logging functions for debugging ===================== */
2015-07-26 09:17:43 -04:00
void serverLogHexDump ( int level , char * descr , void * value , size_t len ) {
2012-06-11 17:44:34 -04:00
char buf [ 65 ] , * b ;
unsigned char * v = value ;
char charset [ ] = " 0123456789abcdef " ;
2016-09-09 04:59:29 -04:00
serverLog ( level , " %s (hexdump of %zu bytes): " , descr , len ) ;
2012-06-11 17:44:34 -04:00
b = buf ;
while ( len ) {
b [ 0 ] = charset [ ( * v ) > > 4 ] ;
b [ 1 ] = charset [ ( * v ) & 0xf ] ;
b [ 2 ] = ' \0 ' ;
b + = 2 ;
len - - ;
v + + ;
if ( b - buf = = 64 | | len = = 0 ) {
2015-07-27 03:41:48 -04:00
serverLogRaw ( level | LL_RAW , buf ) ;
2012-06-11 17:44:34 -04:00
b = buf ;
}
}
2015-07-27 03:41:48 -04:00
serverLogRaw ( level | LL_RAW , " \n " ) ;
2012-06-11 17:44:34 -04:00
}
2012-03-27 05:47:51 -04:00
/* =========================== Software Watchdog ============================ */
# include <sys/time.h>
void watchdogSignalHandler ( int sig , siginfo_t * info , void * secret ) {
2012-04-24 05:07:15 -04:00
# ifdef HAVE_BACKTRACE
2012-03-27 05:47:51 -04:00
ucontext_t * uc = ( ucontext_t * ) secret ;
2012-04-24 05:07:15 -04:00
# endif
2015-07-27 03:41:48 -04:00
UNUSED ( info ) ;
UNUSED ( sig ) ;
2012-03-27 05:47:51 -04:00
2015-07-27 03:41:48 -04:00
serverLogFromHandler ( LL_WARNING , " \n --- WATCHDOG TIMER EXPIRED --- " ) ;
2012-03-27 05:47:51 -04:00
# ifdef HAVE_BACKTRACE
2012-04-26 10:04:53 -04:00
logStackTrace ( uc ) ;
2012-03-27 09:24:33 -04:00
# else
2015-07-27 03:41:48 -04:00
serverLogFromHandler ( LL_WARNING , " Sorry: no support for backtrace(). " ) ;
2012-03-27 05:47:51 -04:00
# endif
2015-07-27 03:41:48 -04:00
serverLogFromHandler ( LL_WARNING , " -------- \n " ) ;
2012-03-27 05:47:51 -04:00
}
/* Schedule a SIGALRM delivery after the specified period in milliseconds.
* If a timer is already scheduled , this function will re - schedule it to the
* specified time . If period is 0 the current timer is disabled . */
void watchdogScheduleSignal ( int period ) {
struct itimerval it ;
/* Will stop the timer if period is 0. */
it . it_value . tv_sec = period / 1000 ;
2012-03-27 06:11:37 -04:00
it . it_value . tv_usec = ( period % 1000 ) * 1000 ;
2012-03-27 05:47:51 -04:00
/* Don't automatically restart. */
it . it_interval . tv_sec = 0 ;
it . it_interval . tv_usec = 0 ;
setitimer ( ITIMER_REAL , & it , NULL ) ;
}
2012-11-27 14:41:33 -05:00
/* Enable the software watchdog with the specified period in milliseconds. */
2012-03-27 05:47:51 -04:00
void enableWatchdog ( int period ) {
2012-05-13 15:52:35 -04:00
int min_period ;
2012-03-27 05:47:51 -04:00
if ( server . watchdog_period = = 0 ) {
struct sigaction act ;
/* Watchdog was actually disabled, so we have to setup the signal
* handler . */
sigemptyset ( & act . sa_mask ) ;
2014-05-22 15:34:02 -04:00
act . sa_flags = SA_ONSTACK | SA_SIGINFO ;
2012-03-27 05:47:51 -04:00
act . sa_sigaction = watchdogSignalHandler ;
sigaction ( SIGALRM , & act , NULL ) ;
}
2012-05-13 15:52:35 -04:00
/* If the configured period is smaller than twice the timer period, it is
* too short for the software watchdog to work reliably . Fix it now
* if needed . */
2012-12-14 11:10:40 -05:00
min_period = ( 1000 / server . hz ) * 2 ;
2012-05-13 15:52:35 -04:00
if ( period < min_period ) period = min_period ;
2012-03-27 05:47:51 -04:00
watchdogScheduleSignal ( period ) ; /* Adjust the current timer. */
server . watchdog_period = period ;
}
/* Disable the software watchdog. */
void disableWatchdog ( void ) {
struct sigaction act ;
if ( server . watchdog_period = = 0 ) return ; /* Already disabled. */
watchdogScheduleSignal ( 0 ) ; /* Stop the current timer. */
/* Set the signal handler to SIG_IGN, this will also remove pending
* signals from the queue . */
sigemptyset ( & act . sa_mask ) ;
act . sa_flags = 0 ;
act . sa_handler = SIG_IGN ;
sigaction ( SIGALRM , & act , NULL ) ;
server . watchdog_period = 0 ;
}