一个生产环境C++工程段错误的排查
Posted 李迟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个生产环境C++工程段错误的排查相关的知识,希望对你有一定的参考价值。
最近遇到一个C++工程段错误的问题,因日志记录有回溯栈的信息,排查比较容易,解决也不难,但过程还是值得记录的。
起因
年后第一天上班的头几分钟,运维同事发消息反馈,某一机子的一个程序每天凌晨2点准时段错误重启。随后下载并查询日志,发现是一个不怎么使用的服务出问题,当时移交时,这个服务已明确不使用了,但我不敢删除,刚好这个服务开了一个端口,恰好接收不合法数据时又不判断指针结束,从而出现了段错误。自去年部门调整接手代码至今,已发生多次疑难杂症,比如内存泄漏,比如sqlite3数据库文件被无故写入日志数据,比如这次段错误。本命年估计不太顺利。
排查及解决
代码定位
出现问题的代码示例如下:
int split_cmd(char *src, const char *separeter)
char *ptrsrc = src + 1;
int idx = 0;
while (1)
if (*ptrsrc != ',')
ptrsrc++;
continue;
else
*ptrsrc = 0;
//tmpp[idx]=ptrsrc;
if (2 == ++idx)
break;
if (*ptrsrc == 0)
printf("error cmd\\n");
return -1;
问题如下:
- 入参
src
为指针,但没有判断是否合法,而且代码中将指针直接+1
,这是非法危险的。 separeter
本意是指定分割的字符,但并没有使用之。- 结束条件
if (*ptrsrc != ',')
过简单,段错误即发现在此处。 - 循环使用
while(1)
,而不是通过长度遍历。
重现问题
工程是已成型的服务,在网上找了几个 socket 调试助手,但无法连接成功,后使用 linux 的 nc 命令,发送命令如下:
printf "bad cmd" | nc 127.0.0.1 1314
重现问题。出错代码如下:
if (*ptrsrc != ',')
ptrsrc++;
continue;
解决方法
根据上述列出的问题就能快速找到解决方法,这段代码是解析通过 socket 接收的数据,数据是有长度的,但代码并没有沿用,因为不敢对代码大改,因此只改了判断条件。如下:
int RemoteMonitor::SplitCMDbuf(char *src, const char *separeter)
if (src == NULL) return -1;
...
while (1)
if (*ptrsrc != '\\0' && *ptrsrc != ',')
ptrsrc++;
continue;
...
另外打印了远程机器IP和数据(十六进制格式),这样能知道是谁发了什么数据。示例代码如下:
client_len = sizeof(client_addr);
if ((client_sockfd = accept(serverfd, (struct sockaddr *)&client_addr, &client_len)) < 0)
printf("accept error");
close(serverfd);
continue;
char * client_ip = inet_ntoa(client_addr.sin_addr);
// int client_port = ntohs(client_addr.sin_port);
printf( recv from client %s\\n", client_ip);
因开年第一天上班,因此会议比较多,但还是在晚上升级好版本运行。翌日早上观察日志,结果在2点接收到非法指令,但没有出现段错误,分析得知,是一台机器发现了 http 的 GET 请求,将信息反馈至运维同事,后未有消息。
小结
写代码这么久,对出错处理十分小心。函数体第一件事就是判断参数的合法,包括指针是否为空,数值是否在合理范围,等。
nc 命令在某些场合比较方便,比如本次调试,直接在同一机器上执行即可,不需要额外的工具。
对于 socket 的连接,个人觉得加上远程 IP 地址(或端口)会比较好,起码知道连接到哪台服务器,被哪个客户端连接。本例中就新加了客户端的 IP 的打印。
以上是关于一个生产环境C++工程段错误的排查的主要内容,如果未能解决你的问题,请参考以下文章