面向祖传代码 Debug,我挽回了一位准备跑路的程序员
交流群的风格突然骤变,没有了往日的灌水扯淡,居然聊起了技术。
看了大家的全部的聊天记录,发现问题并没解决。群里难得这么多人聊技术,抱着问答不断,必有回响的原则,主动勾搭一起看看是什么问题。
大概了解其问题是这样,apache 子进程一直异常退出
$ sudo tail -f /var/log/httpd/error_log
[Sun Jun 13 19:32:25.660349 2021] [core:notice] [pid 23340] AH00052: child pid 20605 exit signal Bus error (7)
[Sun Jun 13 19:32:25.660413 2021] [core:notice] [pid 23340] AH00052: child pid 20606 exit signal Bus error (7)
[Sun Jun 13 19:35:39.862368 2021] [core:notice] [pid 23340] AH00052: child pid 21332 exit signal Bus error (7)
[Sun Jun 13 19:35:50.872363 2021] [core:notice] [pid 23340] AH00052: child pid 21369 exit signal Bus error (7)
[Sun Jun 13 19:39:43.079650 2021] [core:notice] [pid 23340] AH00052: child pid 18595 exit signal Bus error (7)
[Sun Jun 13 19:42:08.210353 2021] [core:notice] [pid 23340] AH00052: child pid 21348 exit signal Bus error (7)
[Sun Jun 13 19:42:16.221076 2021] [core:notice] [pid 23340] AH00052: child pid 21331 exit signal Bus error (7)
[Sun Jun 13 19:42:16.221177 2021] [core:notice] [pid 23340] AH00052: child pid 23156 exit signal Bus error (7)
[Sun Jun 13 19:44:11.329344 2021] [core:notice] [pid 23340] AH00052: child pid 27824 exit signal Bus error (7)
现场复现
了解到其架构是 apache + php
,群友也是临时接受的祖传代码,项目也不是很清楚。可以使用strace
来看看,背后到底是什么问题
$ ps -ef|grep apache
首先看到主进程 id 是 23340,这里需要跟着其子进程,所以我执行了如下命令
$ sudo strace -o strace.log -s 1024 $(pidof "/usr/sbin/httpd" -o 23340|sed 's/\([0-9]*\)/-p \1/g')
strace: Process 26212 attached
strace: Process 26211 attached
strace: Process 26191 attached
strace: Process 25940 attached
...
参数大概解释下
code | 释义 |
---|---|
-o strace.log | 输出 strace 日志到 strace.log |
-s 1024 | 当系统调用的某个参数是字符串时,最多输出指定长度的内容,默认是32个字节 |
pidof “/usr/sbin/httpd” -o 23340 | 排除主进程 23340 之外的 httpd 子进程 |
然后新开一个窗口监控日志
$ sudo tail -f strace.log |grep SIGBUS -B 500
28741 open("/xxxx/www/data/cache/xxx.php", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 15
28741 write(15, "<?php defined('DYMall') or exit('Access Invalid!'); return array ( ) ?>", 71) = 71
28741 close(15)
...
28741 open("/xxxx/www/data/cache/xxx.php", O_RDONLY) = 15
28741 fstat(15, {st_mode=S_IFREG|0644, st_size=71, ...}) = 0
28741 fstat(15, {st_mode=S_IFREG|0644, st_size=71, ...}) = 0
28741 fstat(15, {st_mode=S_IFREG|0644, st_size=71, ...}) = 0
28741 mmap(NULL, 71, PROT_READ, MAP_SHARED, 15, 0) = 0x7f584aee8000
28741 --- SIGBUS {si_signo=SIGBUS, si_code=BUS_ADRERR, si_addr=0x7f584aee8000} ---
28741 chdir("/etc/httpd") = 0
28741 rt_sigaction(SIGBUS, {SIG_DFL, [], SA_RESTORER|SA_INTERRUPT, 0x7f58499bc5e0}, {SIG_DFL, [], SA_RESTORER|SA_RESETHAND, 0x7f58499bc5e0}, 8) = 0
28741 kill(28741, SIGBUS) = 0
28741 rt_sigreturn({mask=[]}) = 140017190993928
28741 --- SIGBUS {si_signo=SIGBUS, si_code=SI_USER, si_pid=28741, si_uid=48} ---
28741 +++ killed by SIGBUS +++
SIGBUS 在用户态最为常见的场景,也最容易触发,通常来说根本原因都是进程 mmap 了一个文件后,另外的进程把这个文件截断了,导致 mmap 出来的某些内存页超出文件的实际大小,访问那些超出的内存页就会触发 SIGBUS
验证文件大小变化
监控文件的大小来验证文件是否一直在变化
$ while :; do cat /xxxx/www/data/cache/xxx.php|wc -L ;done
$ while :; do cat /xxxx/www/data/cache/xxx.php|wc -L ;done|grep -v 71
发现文件确实在少数情况下会变为 0 个字节,根据文件的路径,初步估计应该是缓存文件被重写了
确认文件写操作来源
$ sudo yum -y install audit auditd-libs
监控topic_goodsclass.php
文件的写入操作
$ sudo auditctl -w /xxxx/www/data/cache/xxx.php -p w
结果类似于
time->Mon Jun 14 21:21:39 2021
type=PROCTITLE msg=audit(1623676899.778:1883303): proctitle=2F7573722F7362696E2F6874747064002D44464F524547524F554E44
type=PATH msg=audit(1623676899.778:1883303): item=1 name="/xxxx/www/data/cache/xxx.php" inode=30151674 dev=fd:11 mode=0100644 ouid=48 ogid=48 rdev=00:00 objtype=NORMAL
type=PATH msg=audit(1623676899.778:1883303): item=0 name="/xxxx/www/data/cache/" inode=30151667 dev=fd:11 mode=040755 ouid=48 ogid=48 rdev=00:00 objtype=PARENT
type=CWD msg=audit(1623676899.778:1883303): cwd="/xxxx/www"
type=SYSCALL msg=audit(1623676899.778:1883303): arch=c000003e syscall=2 success=yes exit=16 a0=55b452b93078 a1=241 a2=1b6 a3=2a items=2 ppid=31318 pid=18269 auid=4294967295 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=4294967295 comm="httpd" exe="/usr/sbin/httpd" key=(null)
收集一段时间,做下统计
$ sudo ausearch -f /xxxx/www/data/cache/xxx.php |grep "exe="|awk '{print $12,$26}'|sort|uniq -c44200 ppid=31318 exe="/usr/sbin/httpd"
发现都是来自于/usr/sbin/httpd
,并没有CLI
模式的写入(担心有定时任务来生成缓存),清理掉监控
sudo auditctl -D
多进程文件读写冲突的解决方案
需要注意,在对文件进行fopen($filename, "w+")
的时候,就已经将文件清空了,所以加锁需要在fopen
目标文件之前,所以我的方式是增加一个文件一一对应的锁文件。如下:
aa.php
function addFileLock($filename, $lock)
{$fp = fopen($filename . ".lock", "w+");flock($fp, $lock);return $fp;
}function releaseLock($fp)
{flock($fp, LOCK_UN);
}$filename = "cc.log";// mock 数据
file_put_contents($filename, "100");$fp = fopen($filename, "r");if ($lockfp = addFileLock($filename, LOCK_SH)) {$n = 10;while ($n > 0) {sleep(1);printf("%d %d\n", time(), --$n);}echo fgets($fp);releaseLock($lockfp);
}fclose($fp);
bb.php
function setFileLock($filename, $lock)
{$fp = fopen($filename . ".lock", "w+");flock($fp, $lock);return $fp;
}function releaseFileLock($fp)
{flock($fp, LOCK_UN);fclose($fp);
}$filename = "cc.log";if ($lockfp = setFileLock($filename, LOCK_EX)) {$fp = fopen($filename, "w+");echo time();fwrite($fp, 200);releaseFileLock($lockfp);
}fclose($fp);
所以先执行 php aa.php
再执行 php bb.php
,就能看到效果。
实际代码定位
根据系统调用里面的字符串关键字,搜到了相关的缓存操作代码
28741 write(15, "<?php defined('DYMall') or exit('Access Invalid!'); return array ( ) ?>", 71) = 71
改完之后发现线上还是有问题,本来我已经怀疑人生了,还是通过系统调用发现读取的时候并没有走我增加的共享锁
原来缓存的读取是走的别的逻辑,从上面的截图可以看到缓存的写入已经走了修改之后的代码(加锁逻辑),然后根据个人经验加搜索该缓存写入方法,按图索骥找到了另外一个缓存文件的读取逻辑,然后再次修改,线上问题没有再复现。
问题解决,跑路哥给我发来红包,我心满意足的接受了。
总结
- 首先通过
strace
复现SIGBUS
的场景 - 预计是文件在多进程模型下,并发读写导致的问题
- 通过
auditctl
监控统计文件的写操作来源均为httpd
没有其他进程来修改 - 通过读加共享锁、写加排它锁,线上还是有问题
- 再次根据
strace
发现读的地方另有他处,根据经验,补上,问题修复
不足的地方:最后定位过于依靠个人经验,不够系统化。
小想法:可以弄一个扩展使用 zend_set_user_opcode_handler
来监控自定义函数和方法和系统方法和函数调用链,然后在每个请求初始化阶段写入日志,比如
write(fd, "{unique code}", xxx);
write(fd, "request uri", xxx);
然后的方法调用在 zend_set_user_opcode_handler
里面打印,这样就能方便排查祖传代码的一些业务逻辑问题了。
如果有其他更快更简单的方案,大家留言告诉我。
面向祖传代码 Debug,我挽回了一位准备跑路的程序员相关推荐
- 基于小波变换的图像边缘检测(matlab祖传代码注释)
基于小波变换的图像边缘提取应用展示 上图为针对png格式无背景原图的边缘检测,对比各种边缘检测算子,小波变化的优势体现并不明显. 上图为针对含背景图片的边缘检测,小波变化的优势这里体现的比较明显. m ...
- 入职第一天,我接手了号称【屎山】的祖传代码,这还能卷吗???
公司各种各样的祖传代码都是令新人虎躯一震的代码,因为有时候你根本不知道它是干嘛的,甚至觉得它毫无用处,关键是 还绝对不能动,碰一段改半年,别问我怎么知道的.最讽刺的是,你可能为了修改代码,也在里面拉了 ...
- 为什么祖传代码被称为「屎山」?这个回答简直太形象了
经常听说祖传代码会被人称之为「屎山」,不同人可能有不同的体会,最近看到一个回答,简直是把这个阐述得"活灵活现",大家来感受下吧. " 阅读本文大概需要 3 分钟. &qu ...
- 为什么祖传代码被称为“屎山”?
任何设计人员,你几年之后再来看自己现在的作品,你就会觉得简直就是狗屎,拿出来真tm丢人. 如果你没有这种感觉,那说明你这行干不久了. 说到祖传代码不得不提之前在知乎上看到的两位网友的经历: 一 我刚入 ...
- 祖传代码如何优化性能?
Hollis的新书限时折扣中,一本深入讲解Java基础的干货笔记! 为了新朋友能快速进入场景,再描述一遍这个项目的背景,这个项目是一个自研的Dubbo注册中心,上一张架构图 Consumer 和 Pr ...
- C#串口数据读取及处理解决方案--祖传代码修改记
文章目录 原方案 代码 基本思路 串口通信基本原理 采集数据流程 问题 重构 问题复盘 重构过程 ReadTo函数 多线程 代码 原方案 欢迎大家访问我的个人网站 www.joezhouman.com ...
- 祖传代码成「屎山」了,千万别动
上面这个公号「涩郎」,是我的一个备用号,为了防止万一哪天大号失联,平时一周我也会发三篇左右的我的思考,读书笔记,认知感悟等文章,带领大家一起探索精神与财务自由之路. 大家好,我是校长. 我看有人问了这 ...
- 删除vue打包大小限制_如何优化 Vue 祖传代码
目录 前言 为什么要优化 从哪里开始下手 现在开始 1.代码压缩 2.删除一些废弃的页面 3.使用 cdn 优化 4.修改路由引入方式 结果 前言 "这页面加载也太慢了!",一个宁 ...
- 某程序员动了公司祖传代码屎山,半年没改完,惭愧后交辞职报告
前段时间,有这样的一个话题,非常的火热,那就是关于程序员的,新入职程序员吐槽老员工写的代码就像是"一坨屎"!这样的言论瞬间就引起了程序员们的讨论. 有程序员认为,别看现在像是一坨屎 ...
最新文章
- freemarker中js里面取字符串,换行导致报错的解决办法
- 广州科目三路考经历与注意事项分享
- 七彩影视双端新版本源码
- sql azure 语法_将SQL工作负载迁移到Microsoft Azure:规划迁移
- 说明Android应用调用全屏方式
- 局域网电脑屏幕桌面监控实现方法
- Computer Networking——network layer QA
- opengl 五角星画法 源代码
- Snowy小诺 前端关闭Eslint校验 yarn
- 鲁大师从服务器获取信息失败怎么办,云服务器 鲁大师
- java poi替换word_利用POI 技术动态替换word模板内容
- Git runner安装
- 推荐一个软件分享资源站
- cad转换器高版本转低版本怎么转?
- 商铺wifi短信验证如何实现?商铺无线wifi短信认证方案
- 最适合freshman的Java习题集(一)
- 温湿度控制系统c语言,基于单片机的温湿度控制系统设计
- python爬 歌曲 视频
- Android性能优化之工具篇 — — 开发者选项
- 利用神经网络进行分类,神经网络学什么