redis调试起步

redis调试起步

1.下载redis源码并安装

wget http://download.redis.io/releases/redis-5.0.7.tar.gz
tar xzf redis-5.0.7.tar.gz
mv redis-5.0.7 redis
make MALLOC=libc CFLAGS="-g -O0"
make install

2.使用gdb调试

gdb redis-server
进入命令行界面:

(gdb) start
Temporary breakpoint 1 at 0x394c4: file server.c, line 4040.
Starting program: /usr/local/bin/redis-server 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe018) at server.c:4040
4040	int main(int argc, char **argv) {

使用start命令启动调试,看到程序停止在程序入口server.c:4040处.

具体的启动流程不再看了,重点关注执行命令的流程:
使用c命令,使程序执行,运行起来后,看看当前程序的堆栈:

(gdb) bt
#0  0x00007ffff7344bb7 in epoll_wait (epfd=5, events=0x555555928580, maxevents=4192, timeout=100) at ../sysdeps/unix/sysv/linux/epoll_wait.c:30
#1  0x000055555557f242 in aeApiPoll (eventLoop=0x5555559201f0, tvp=0x7fffffffde70) at ae_epoll.c:112
#2  0x000055555557ff06 in aeProcessEvents (eventLoop=0x5555559201f0, flags=11) at ae.c:411
#3  0x00005555555801fd in aeMain (eventLoop=0x5555559201f0) at ae.c:501
#4  0x000055555558dcd4 in main (argc=1, argv=0x7fffffffe018) at server.c:4234

可以看到当前程序的#3在aeMain函数上,这是一个epoll时间轮询函数,当接收到客户端请求或者设定的轮询时间达到时候会唤醒执行:
打一个断点:

(gdb) br ae.c:501
Breakpoint 2 at 0x5555555801ec: file ae.c, line 501.
(gdb) info br
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x00005555555801ec in aeMain at ae.c:501

然后用客户端连接一下:redis-cli:
执行一个简单的命令:

127.0.0.1:6379> set foo bar 

程序进入断点:

│442                 if (!invert && fe->mask & mask & AE_READABLE) {                                                                                                                             │
>│443                     fe->rfileProc(eventLoop,fd,fe->clientData,mask);   //事件处理函数                                                                                                                     │
│444                     fired++;                                                                                                                                                                │
│445                 } 

看一下客户端传过来的值(进行了封装):

(gdb) s
readQueryFromClient (el=0x5555559201f0, fd=8, privdata=0x55555594a2e0, mask=1) at networking.c:1519
(gdb) s
(gdb) p *c
$1 = {id = 3, fd = 8, db = 0x555555934a10, name = 0x0, querybuf = 0x555555949491 "", qb_pos = 0, pending_querybuf = 0x5555559414e3 "", querybuf_peak = 0, argc = 0, argv = 0x555555941980, 
  cmd = 0x0, lastcmd = 0x5555558a34a0 <redisCommandTable+13440>, reqtype = 0, multibulklen = 0, bulklen = -1, reply = 0x555555949270, reply_bytes = 0, sentlen = 0, ctime = 1576494900,
  lastinteraction = 1576494911, obuf_soft_limit_reached_time = 0, flags = 0, authenticated = 0, replstate = 0, repl_put_online_on_ack = 0, repldbfd = 0, repldboff = 0, repldbsize = 0,
  replpreamble = 0x0, read_reploff = 0, reploff = 0, repl_ack_off = 0, repl_ack_time = 0, psync_initial_offset = 0, replid = '\000' <repeats 40 times>, slave_listening_port = 0,
  slave_ip = '\000' <repeats 45 times>, slave_capa = 0, mstate = {commands = 0x0, count = 0, cmd_flags = 0, minreplicas = 0, minreplicas_timeout = 0}, btype = 0, bpop = {timeout = 0,
    keys = 0x5555559492b0, target = 0x0, xread_count = 0, xread_group = 0x0, xread_consumer = 0x0, xread_retry_time = 0, xread_retry_ttl = 0, xread_group_noack = 0, numreplicas = 0, 
    reploffset = 0, module_blocked_handle = 0x0}, woff = 0, watched_keys = 0x555555949320, pubsub_channels = 0x555555949360, pubsub_patterns = 0x5555559493d0, peerid = 0x0,
  client_list_node = 0x5555559419d0, bufpos = 0, buf = "*200\r\n*6\r\n$4\r\nincr\r\n:2\r\n", '\000' <repeats 9532 times>...}

从fd文件流中读取发送的消息:

1545        nread = read(fd, c->querybuf+qblen, readlen);

读取完后查看一下redis客户端发送过来的内容:

(gdb) p c->querybuf
$4 = (sds) 0x55555594e505 "*3\r\n$3\r\nset\r\n$3\r\nfoo\r\n$3\r\nbar\r\n"

解析客户端命令:

1285    int processMultibulkBuffer(client *c)

客户端的参数存储在argv里,参数的个数argc

(gdb) p (char*)(server.current_client.argv[0].ptr)
$12 = 0x555555949543 "set"
(gdb) p (char*)(server.current_client.argv[1].ptr)
$13 = 0x555555949523 "foo"
(gdb) p (char*)(server.current_client.argv[2].ptr)
$14 = 0x555555949503 "bar"
(gdb) p server.current_client.argc
$15 = 3

命令解析:

2590        c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);  

解析为set命令的结构:

(gdb) p *c->cmd
$17 = {name = 0x555555661df9 "set", proc = 0x5555555b4823 <setCommand>, arity = -3, sflags = 0x555555661dfd "wm", flags = 5, getkeys_proc = 0x0, firstkey = 1, lastkey = 1, keystep = 1,
  microseconds = 0, calls = 0}

接下来是大量判断server当前状态以及配置是否允许执行命令
最终执行命令:

│2763            call(c,CMD_CALL_FULL); 

处理每次执行命令后的基于时间的流程:

469             processed += processTimeEvents(eventLoop); 

看看客户端的执行返回:

127.0.0.1:6379> set foo bar
OK
(1477.89s)

好了,redis基本的命令执行过程使用gdb观察完毕.

要在 VS Code 中调试 Redis,可以使用以下步骤: 1. 安装 Redis 扩展:打开 VS Code,点击左侧的扩展图标,搜索并安装 Redis 扩展。该扩展提供了 Redis调试功能。 2. 配置调试器:在 VS Code 中打开要调试Redis 配置文件。在侧边栏的调试视图中,点击齿轮图标打开 `launch.json` 文件。 3. 添加调试配置:在 `launch.json` 文件中,添加一个新的调试配置。可以根据你的需要选择不同的启动配置,例如使用默认配置启动 Redis 或者指定自定义的配置文件。 下面是一个示例的 `launch.json` 文件配置: ```json { "version": "0.2.0", "configurations": [ { "type": "redis", "request": "launch", "name": "Launch Redis", "program": "redis-server", "args": ["--port", "6379"], "cwd": "${workspaceRoot}" } ] } ``` 在上述配置中,指定了 Redis 服务器的启动命令为 `redis-server`,并传递了参数 `--port 6379` 来指定端口号。`${workspaceRoot}` 表示当前工作区的根目录。 4. 启动调试:保存 `launch.json` 文件后,点击调试视图中的播放按钮,或者使用快捷键 F5 启动 Redis 调试会话。VS Code 将会启动 Redis 服务器,并连接到调试器。 5. 调试 Redis:一旦 Redis 服务器启动并连接到调试器,你可以在调试控制台中输入 Redis 命令,以及使用调试器提供的其他功能,如断点、查看变量等。 请注意,确保你已经正确安装了 Redis 并配置了正确的路径和参数。此外,调试 Redis 时要小心,避免在生产环境中进行调试操作,以免造成数据丢失或其他损坏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值