一群人的战斗

article/2025/8/20 14:41:23

图片

一、Bug 来了

一个平静的周日午后,正悠闲地在公园里遛娃。突然来了一条消息,打开企业微信仔细看了下,竟大吃一惊:客户成功在群内反馈了 Android A/B Testing  SDK 的一个 crash,需要紧急解决。

得知问题后我立刻和客户成功进行了语音沟通:了解到客户的应用今天晚上会上线,担心该 crash 带到线上之后会发生井喷,需要我们这边尽快予以修复。得知这个情况之后,一场轰轰烈烈的 Bug 对抗之旅就此拉开序幕。

二、紧急动员

17 点 09 分

刚刚和客户成功语音沟通完毕,需要立即动员相关的同学处理此问题。

在介绍如何动员之前,先和大家简单说明下 SDK 的发版流程:

1. 研发修复 Bug 并完成自测;

2. 提供给组员进行 Code Review 并同步影响范围;

3. QA 进行测试,如果测试通过则进行发布。

因此,这里要求研发和 QA 同学的深度参与。如果放在工作日,可能会比较简单。但是此时恰逢周日,并且事发突然,客户给予的时间紧迫。

基于此背景,开始兵分两路:

1. 第一时间找到 A/B Testing SDK 的项目经理,请他来帮助协调版本发布的相关人员;

2. 与此同时,我开始进行问题的定位分析。

三、独立分析

17 点 52 分

飞奔到家里,打开了客户提供的 crash 堆栈。该堆栈是客户从自己的分析平台里提取出来的,本地无法复现,堆栈如下:

main(1) java.util.UnknownFormatConversionException Conversion = '_' 还原失败(未找到符号表)(404_1_0_2_0_0_0_0_9_0) 解析原始1 java.util.Formatter$FormatSpecifier.conversion(Formatter.java:2782)2 java.util.Formatter$FormatSpecifier.<init>(Formatter.java:2812)3 java.util.Formatter$FormatSpecifierParser.<init>(Formatter.java:2625)4 java.util.Formatter.parse(Formatter.java:2558)5 java.util.Formatter.format(Formatter.java:2505)6 java.util.Formatter.format(Formatter.java:2459)7 java.lang.String.format(String.java:2870)8 com.sensorsdata.abtest.a.c.a(SABErrorDispatcher.java:29)9 com.sensorsdata.abtest.a.d$3.a(SensorsABTestApiRequestHelper.java:236)10 com.sensorsdata.abtest.a.d$2.onFailure(SensorsABTestApiRequestHelper.java:185)11 com.sensorsdata.analytics.android.sdk.network.HttpCallback$1.run(HttpCallback.java:46)12 android.os.Handler.handleCallback(Handler.java:900)13 android.os.Handler.dispatchMessage(Handler.java:103)14 android.os.Looper.loop(Looper.java:219)15 android.app.ActivityThread.main(ActivityThread.java:8387)16 java.lang.reflect.Method.invoke(Native Method)17 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)18 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1055)

结合上下文分析发现:接口请求失败时,SDK 会打印错误日志。在打印的过程中调用 format 方法导致 UnknownFormatConversionException 异常。

通过上述分析,问题排查范围缩小到:调用 format 方法导致的 crash,并将初步排查结论同步给客户和客户成功。

正在进一步定位为什么会导致 crash 时,突然发现我已经被拉进「紧急处理 Android A/B Testing SDK 崩溃问题」企业微信群。

四、抽丝剥茧

18 点 04 分

进入到专项问题讨论群后,技术顾问向客户确认了更多的信息,例如 crash 的机型、次数、版本等,进一步完善了问题的周边信息。

此时,群内各位大佬纷纷出谋划策,尝试复现 crash。

思路一:format 方法的第一个参数存在特殊字符 '% '

String.format("where name like % %s","Zhang san");

format 方法传入特殊字符,尝试复现,crash 堆栈如下:​​​​​​​

Exception in thread "main" java.util.IllegalFormatFlagsException: Flags = ' '    at java.util.Formatter$FormatSpecifier.checkText(Formatter.java:3037)    at java.util.Formatter$FormatSpecifier.<init>(Formatter.java:2733)    at java.util.Formatter.parse(Formatter.java:2560)    at java.util.Formatter.format(Formatter.java:2501)    at java.util.Formatter.format(Formatter.java:2455)    at java.lang.String.format(String.java:2940)    at com.zxwei.cf.lib.MyClass.main(MyClass.java:22)

可见,这个堆栈和实际的 crash 堆栈不一致。

思路二:format 方法的第一个参数为 null

String.format(null,"Zhang san");

format 方法传入 null,此时 crash 堆栈如下:​​​​​​​

Exception in thread "main" java.lang.NullPointerException    at java.util.regex.Matcher.getTextLength(Matcher.java:1283)    at java.util.regex.Matcher.reset(Matcher.java:309)    at java.util.regex.Matcher.<init>(Matcher.java:229)    at java.util.regex.Pattern.matcher(Pattern.java:1093)    at java.util.Formatter.parse(Formatter.java:2547)    at java.util.Formatter.format(Formatter.java:2501)    at java.util.Formatter.format(Formatter.java:2455)    at java.lang.String.format(String.java:2940)    at com.zxwei.cf.lib.MyClass.main(MyClass.java:24)

比对堆栈信息,发现和实际的 crash 堆栈还是不一致。

思路三:format 方法的第一个参数存在特殊字符  '%_'

String.format("where name like %_","Zhang san");

通过这种方式的模拟,堆栈如下:​​​​​​​

Exception in thread "main" java.util.UnknownFormatConversionException: Conversion = '_'    at java.util.Formatter.checkText(Formatter.java:2579)    at java.util.Formatter.parse(Formatter.java:2565)    at java.util.Formatter.format(Formatter.java:2501)    at java.util.Formatter.format(Formatter.java:2455)    at java.lang.String.format(String.java:2940)    at com.zxwei.cf.lib.MyClass.main(MyClass.java:22)

和实际的 crash 堆栈是一样的,但实际项目中 format 方法的第一个参数是不变的,不可能会出现特殊字符。

思路四:从源码分析产生 UnknownFormatConversionException 异常的原因​​​​​​​

private char conversion(String s) {            c = s.charAt(0);            if (!dt) {                if (!Conversion.isValid(c))                    throw new UnknownFormatConversionException(String.valueOf(c));                if (Character.isUpperCase(c))                    f.add(Flags.UPPERCASE);                c = Character.toLowerCase(c);                if (Conversion.isText(c))                    index = -2;            }            return c;        }

从源码来看,确实是存在非法字符才会抛出异常,但为什么就是复现不了呢?

五、妥协还是坚持

18 点 41 分

时间在一点点的流逝,但是问题一直都没有复现。就在大家都一筹莫展之际,我突然灵光一现:由于该问题的出现只是发生在日志打印期间,是否可以绕过 format 方法,通过字符串直接打印日志?这样相当于直接绕过问题,也能进行解决。

很多时候我们都会面临类似的选择:绕过去还是坚持到底?此时,书记给了一个建议:目前是偶现,不着急修复,还是尽量找到根本原因。

不轻易放过一个问题,将问题跟进到底,我们一起选择了坚持。

六、拨开迷雾

19 点 09 分

这时候项目经理给了一个很关键的假设:连续多次调用 format 方法,并且将引用传递到 format 方法的第一个参数,这样会不会有问题呢?

图片

沿着这个思路,结合项目中的实际代码,确实存在特殊字符且连续调用的场景。基于此假设,我们成功复现出了问题并予以修复。

这一刻让我感到了一个人的力量是有限的,团队的力量是无穷的。

随后 QA 立即远程投入问题验证以及版本发布,在大家的共同努力下于 20 点 40 分顺利交付给客户。

客户拿到新版本后立即集成、自测,并于当天晚上成功上线。

至此,这个问题算是解决完毕。

七、总结

问题发生在周日,从发现到解决,前后总共 4 个小时不到。

在这短短的 4 个小时内,涉及到项目经理、测试、研发、技术顾问共 7 人。感谢大家的通力合作,使得问题能够在如此短的时间内予以解决。

最后,我想和大家分享的是:

1. 于自己而言,遇到问题,要有一颗坚守的心。放弃很容易,但有了一次放弃就会有第二次、第三次。寻求真相的过程必然坎坷,当得到结果的那一刻一切都值得,但求无愧于心;

2. 于团队而言,遇到问题,不要一个人去战斗,在必要的时候可以寻求帮助,总会有一群人给我们出谋划策。大家有着同样的目标,相互配合往往可以碰撞出不一样的火花。如果没有大家的帮助,由于个人思路的局限性一定会耗费大量的时间,更别提后续的测试、发版。这次经历也让我更加体会到团队合作的重要性。

文章来源:公众号-神策技术社区


http://chatgpt.dhexx.cn/article/uPhR8aaa.shtml

相关文章

一个“后浪”的狂欢,一群中年人的孤单!

点击“技术领导力”关注∆ 每天早上8:30推送 作者| Mr.K 编辑| Emma 来源| 技术领导力(ID&#xff1a;jishulingdaoli) “孤单&#xff0c;是一个人的狂欢&#xff0c;狂欢&#xff0c;是一群人的孤单。” 《叶子》&#xff0c;阿桑&#xff0c;词/曲&#xff1a;陈晓娟 01 …

计算机术语hook的理解

Hooks就像一些外来的钩子&#xff0c;在源代码之间钩取&#xff08;窃听&#xff09;一些信息&#xff0c;当它捕捉到自己感兴趣的事发生&#xff0c;就拦截下来&#xff0c;让自己的代码执行一下&#xff0c;处理一下这个信息&#xff0c;然后再放出去继续之前的进程。这样就可…

计算机mips是什么,在计算机术语中,什么叫MIPS

2006-08-18 在计算机术语中,什么叫VGA 显卡所处理的信息最终都要输出到显示器上&#xff0c;显卡的输出接口就是电脑与显示器之间的桥梁&#xff0c;它负责向显示器输出相应的图像信号。CRT显示器因为设计制造上的原因&#xff0c;只能接受模拟信号输入&#xff0c;这就需要显卡…

堆 (计算机术语)

2019独角兽企业重金招聘Python工程师标准>>> 堆&#xff08;英语&#xff1a;heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质&#xff1a; 堆中某个节点的值总是不大于或不小于其父节点的值&#xff1b…

计算机术语翻译(Term.)及缩写整理(Abbr.)

Table of Contents &#x1f52e; 计算机术语翻译&#xff08;Term.&#xff09;及缩写整理&#xff08;Abbr.&#xff09;&#x1f5e1;️ DOI, URI, URL, URN&#x1f5e1;️ prompt&#x1f5e1;️ as-is, to-be&#x1f5e1;️ WYSIWYG&#x1f5e1;️ socket&#x1f5e1;…

计算机术语宏是什么意思,宏(计算机术语)

什幺是宏 所谓宏&#xff0c;就是一些命令组织在一起&#xff0c;作为一个单独命令完成一个特定任务。Microsoft Word中对宏定义为&#xff1a;“宏就是能组织到一起作为一独立的命令使用的一系列word命令&#xff0c;它能使日常工作变得更容易”。Word使用宏语言Visual Basic将…

计算机术语中 cam表示,计算机术语中,英文CAT是指_____。

计算机辅助翻译(英语&#xff1a;Computer-assisted Translation或Computer-aided Translation&#xff0c;缩写&#xff1a;CAT)。 亦称计算机辅助翻译系统&#xff0c;透过人工智能搜索及比对技术以及运用参考资料库和翻译记忆程序&#xff0c;纪录翻译人员所完成之译文&…

栈(计算机术语)

1.栈的概念 栈&#xff08;stack&#xff09;又名堆栈&#xff0c;作为一种数据结构&#xff0c;是一种只能在一端进行插入和删除操作的特殊线性表。 它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶&#xff0c;相对地&#xff0c;…

计算机术语翻译在线,常用计算机专业词汇术语翻译

原标题:常用计算机专业词汇术语翻译 ESP: Encapsulating Security Payload,压缩安全有效载荷 Execute Buffers,执行缓冲区 Extended Burst Transactions,增强式突发处理 Extended Stereo(扩展式立体声) FADD(Floationg PointAddition,浮点加) FAT(File AllocationTables,…

台湾 计算机术语,快取,陣列,程式,这些台湾的计算机术语,你知道几个?|冷知识...

原标题&#xff1a;快取&#xff0c;陣列&#xff0c;程式&#xff0c;这些台湾的计算机术语&#xff0c;你知道几个&#xff1f;|冷知识 作者 | 楼下小黑哥 来源 | 程序通事 今天就不写技术文了&#xff0c;写点轻松的&#xff0c;带大家涨点知识。 最近闲来无聊的时候&#x…

计算机术语中bit的中文含义是,在计算机术语中bit的中文含义是

在计算机术语中,bit的中文含义是位。在计算机中的二进制数系统中,位,简记为b,也称为比特,每个0或1就是一个位(bit)。计算机中的CPU位数指的是CPU一次能处理的最大位数。 二进制数系统中,位简记为b,也称为比特,每个二进制数字0或1就是一个位(bit)。位是数据存储的最小单位…

课程设计:基于SQL Server的银行ATM 存取款机系统设计与实现

目录 前言一、项目背景1、项目任务2、项目技能目标3、需求概述4、开发环境5 、问题分析(1) 银行存取款业务介绍(2) 客户信息(3) 银行卡账户信息(4) 银行卡交易信息(5) 银行卡手工账户和存取款单据信息 二、项目实训内容1、实训一&#xff1a;制定《数据库设计与编程规范》2、实…

面向对象的银行ATM系统分析

1.系统概况 1.1目标系统介绍 随着市场经济的活跃&#xff0c;银行电子化建设迅速发展&#xff0c;ATM交易在银行支付渠道中越来越显现出其重要性&#xff0c;以ATM交易为代表的自助交易正逐步成为现代商业银行为客户服务的主流渠道&#xff0c;也就是我们所常说的自动化与无人…

银行ATM活动图文档

银行ATM问题陈述、词汇表、领域类图链接&#xff1a; http://blog.csdn.net/yingyingbaibai/article/details/70216506 银行ATM用况图文档链接&#xff1a; http://blog.csdn.net/yingyingbaibai/article/details/70216792 银行ATM分析类类图文档链接&#xff1a; http://…

MySQL项目:银行ATM存取款机系统

1、数据库设计 绘制E-R 绘制数据库模型 数据库模型图我们可以创建好表和约束之后&#xff0c;点击对象 选中需要绘制模型图的表 数据库默认是第一个&#xff0c;我们可以选择第三个 代码段在最下面 可以去看看这三个图案有什么不同&#xff0c;这样比我们一个一个去添加字段…

银行ATM机系统

模拟Atm机的一些简单功能 UserInfo类 public class UserInfo {private int id; //idprivate String name; //姓名private String password; //密码private String idCard; //卡号private String bank; //银行名称private double money; //余额Overridepu…

Java实现ATM银行模拟系统(含完整代码)

目录 引言 功能简介 登录和注册 1、注册 2、登录 具体功能实现 1、存款功能 2、取款功能 3、转账功能 4、查询功能 5、注销账号 6、修改密码 7、退出 完整代码 引言 &#x1f3e7;&#x1f3e7;&#x1f3e7;本文主要介绍了如何通过Java实现ATM银行模拟系统&…

ATM系统

ATM系统 1、简介 ATM系统包含两个菜单栏&#xff0c;一个是登录、注册菜单栏&#xff0c;一个时是功能菜单栏。 ATM系统共有四个功能&#xff1a;   充值功能&#xff1a;用户登录成功后可以为本账户充值&#xff08;存款&#xff09;。   转账功能&#xff1a;用户登录后可…

银行卡在哪个银行都能取款吗?

银行卡要想取款&#xff0c;分两种情况。 第一种情况是在银行窗口取款&#xff0c;你是哪个银行的银行卡&#xff0c;就得在哪个银行取款&#xff0c;在不同的银行窗口是办理不了的&#xff0c;比如你的卡是工商银行的&#xff0c;你去建设银行窗口是取不了现金的。 第二种情况…

ATM取款机系统

模拟银行实现ATM机取款系统 该系统使用( ( (SQLServer) ) )数据库 功能介绍&#xff1a; 开户&#xff08;到银行填写开户申请单&#xff09;取钱存钱查询余额转账 根据需求设计相对应的数据库概念模型 流程分步详解 1 创建数据库Bank_db --创建数据库 CREATE DATABASE B…