腾讯实习生面试2016两道面试题目?修改
谢谢大神们高质量的回答,满满干货,excited
-----------------------------------------------------------------
回答里有提到保密协议,实习生面试前没有听说这个,并且网上也有很多相关的面经,但为了省去一些不必要的问题,匿了。
------------------------------------------------------------------------
在参加腾讯实习生2016面试时,有两道问题自己没有想清楚也思考的不是很明白,面试结束时后悔没有问正解是什么,回来后在搜索引擎上也没有搜到相关的问题。
第一道:"在多线程环境下,有大量并发, 有一个百万次出现一次bug, 如何调试这个bug。" 这种比较大的工程没有接触过,我回答的是,首先要试图重现这个bug(没有经验),出现bug时,保留当时的一些状态信息,然后进行调试,依次确定与这个调用有关的模块(加桩和驱动),面试官说这个bug很难重现,我说那么这个bug的概率比较小,如果没有造成太大的影响能不能忽略,然后面试官说那10W次出现一次呢。。。
第二道:这道题目的题意不是太理解,,是面试官给我的试卷上面的。"有一个类指针,指向类实例化的对象,在程序的运行过程中,这个类指针指向的对象崩溃了,这个类指针的虚函数表被破坏了,如何定位这个问题?" 我把如何定位这个问题理解的是如何定位这个bug所在,,然后我回答,,知道虚函数表被破坏了,那么问题不是在这里吗,可以依次确定是不是调用了应该调用的虚函数,,然后面试官说虚函数表里面的内容没有问题,,我有点蒙了,,不知道为什么是这样,我回答说那是指向虚函数表的指针被破坏了,,面试官说你没有理解题意,C++基础比较差,然后面试GG。
所以,我想问下这两道题目该怎么回答, 还有第二道题目的题意该怎么理解。。谢谢谢谢!!! 修改
-----------------------------------------------------------------
回答里有提到保密协议,实习生面试前没有听说这个,并且网上也有很多相关的面经,但为了省去一些不必要的问题,匿了。
------------------------------------------------------------------------
在参加腾讯实习生2016面试时,有两道问题自己没有想清楚也思考的不是很明白,面试结束时后悔没有问正解是什么,回来后在搜索引擎上也没有搜到相关的问题。
第一道:"在多线程环境下,有大量并发, 有一个百万次出现一次bug, 如何调试这个bug。" 这种比较大的工程没有接触过,我回答的是,首先要试图重现这个bug(没有经验),出现bug时,保留当时的一些状态信息,然后进行调试,依次确定与这个调用有关的模块(加桩和驱动),面试官说这个bug很难重现,我说那么这个bug的概率比较小,如果没有造成太大的影响能不能忽略,然后面试官说那10W次出现一次呢。。。
第二道:这道题目的题意不是太理解,,是面试官给我的试卷上面的。"有一个类指针,指向类实例化的对象,在程序的运行过程中,这个类指针指向的对象崩溃了,这个类指针的虚函数表被破坏了,如何定位这个问题?" 我把如何定位这个问题理解的是如何定位这个bug所在,,然后我回答,,知道虚函数表被破坏了,那么问题不是在这里吗,可以依次确定是不是调用了应该调用的虚函数,,然后面试官说虚函数表里面的内容没有问题,,我有点蒙了,,不知道为什么是这样,我回答说那是指向虚函数表的指针被破坏了,,面试官说你没有理解题意,C++基础比较差,然后面试GG。
所以,我想问下这两道题目该怎么回答, 还有第二道题目的题意该怎么理解。。谢谢谢谢!!! 修改
举报
5 条评论
•
邀请回答
按投票排序
按时间排序
51 个回答
其实这两个问题拿来问有一定经验的中高级程序员是可以的,问应届的实习毕业生,就确实有点强人所难了。
因为这些都是假设性问题,所以一般都只能说排查问题的思路:
问题一:
1:其实100万分之一的几率并不算很低。一个繁忙一点的服务,每秒一万个请求是很正常的(如果业务简单,还能更高)。所以百万分之一的复现概率,相当于在压力测试下几分钟就能出一次了。所以在道理上,如果有服务以这个几率出现bug,让它上线本身就是测试部门的失职。
2:反过来说,既然几分钟就能复现,那就在测试环境里压就好了,想怎么改就怎么改,想怎么打log就怎么打log。
3:假定测试环境的压测真的没出来,真的线上出现这样的问题(例如说压测的用例没有覆盖到)。首先考虑的是回滚服务到旧版,以确保正常业务不出问题。然后在线上环境提取某台机,用类似tcpcopy这样的软件导一份数据到测试环境中,来测试这个有问题的版本。这时候,既然是测试环境,你想怎么办都行(不影响线上服务)。
4:到了真正去分析bug了,那首先确定bug的类型。例如说如果能core dump的话相对好办一些,因为可以从core dump大概能看出或者猜出一些东西来。而如果并不core dump,而只是导致数据错乱的话,那就要做数据出入口的校验等。
5:在各顶级模块的入口出口打log,定位发生bug时所在的模块,然后逐步收敛。
6:确定这些模块以及关联影响的模块,单独提取这些模块出来,进行测试或者code review,最终确定问题。
问题二:
1:这类bug,一般都涉及越界操作。
2:一般这类bug,大多数都是可以通过静态code review解决的。既然知道是那个类,盯着用到这些类的实例的地方就行(包括这些类实例的前后几个对象,因为有可能是它们越界)。
3:一些工具,例如valgrind、purify之类的,也有可能有帮助(不一定肯定有用,但一般还是挺有用的)。
因为这些都是假设性问题,所以一般都只能说排查问题的思路:
问题一:
1:其实100万分之一的几率并不算很低。一个繁忙一点的服务,每秒一万个请求是很正常的(如果业务简单,还能更高)。所以百万分之一的复现概率,相当于在压力测试下几分钟就能出一次了。所以在道理上,如果有服务以这个几率出现bug,让它上线本身就是测试部门的失职。
2:反过来说,既然几分钟就能复现,那就在测试环境里压就好了,想怎么改就怎么改,想怎么打log就怎么打log。
3:假定测试环境的压测真的没出来,真的线上出现这样的问题(例如说压测的用例没有覆盖到)。首先考虑的是回滚服务到旧版,以确保正常业务不出问题。然后在线上环境提取某台机,用类似tcpcopy这样的软件导一份数据到测试环境中,来测试这个有问题的版本。这时候,既然是测试环境,你想怎么办都行(不影响线上服务)。
4:到了真正去分析bug了,那首先确定bug的类型。例如说如果能core dump的话相对好办一些,因为可以从core dump大概能看出或者猜出一些东西来。而如果并不core dump,而只是导致数据错乱的话,那就要做数据出入口的校验等。
5:在各顶级模块的入口出口打log,定位发生bug时所在的模块,然后逐步收敛。
6:确定这些模块以及关联影响的模块,单独提取这些模块出来,进行测试或者code review,最终确定问题。
问题二:
1:这类bug,一般都涉及越界操作。
2:一般这类bug,大多数都是可以通过静态code review解决的。既然知道是那个类,盯着用到这些类的实例的地方就行(包括这些类实例的前后几个对象,因为有可能是它们越界)。
3:一些工具,例如valgrind、purify之类的,也有可能有帮助(不一定肯定有用,但一般还是挺有用的)。
class 的虚函数表被破坏是不可能的,因为这个 vtable 是放在 rodata 区,是只读的。修改 vtable 会导致 segment fault,这样拿 coredump 一看就知道哪条语句在干坏事。
class object 里的 vptr 被修改倒是有可能。
class object 里的 vptr 被修改倒是有可能。
第一个问题
@洋耗子的回答已经很详实了,另外补充如果bug一定在第100w或者10w次前后的时机出现,那么可以考虑的方向:小块资源泄漏、计数器溢出
第二个问题,如果虚函数表被破坏了,可能会比较简单,可以尝试用mprotect把虚函数表的区域设置为只读来排查问题;如果虚函数表完好,那么基本就是虚表指针的问题,可能有几种原因:已经析构或正在析构的对象被其他线程调用,多继承体系中非法的cast,对象内存被越界写。越界写就比难查了,也可以配合mprotect(参考这篇文章 走下神坛的内存调试器--定位多线程内存越界问题实践总结),或者在那些怀疑的class或struct增加magic number字段标记,看看能否通过被越界写的内容特征来找出真凶。
第二个问题,如果虚函数表被破坏了,可能会比较简单,可以尝试用mprotect把虚函数表的区域设置为只读来排查问题;如果虚函数表完好,那么基本就是虚表指针的问题,可能有几种原因:已经析构或正在析构的对象被其他线程调用,多继承体系中非法的cast,对象内存被越界写。越界写就比难查了,也可以配合mprotect(参考这篇文章 走下神坛的内存调试器--定位多线程内存越界问题实践总结),或者在那些怀疑的class或struct增加magic number字段标记,看看能否通过被越界写的内容特征来找出真凶。
我来跑个题。
说到虚表。。有一次我定位一个LLVM miscompile,现象是崩溃。发现是虚函数调用时函数指针地址奇怪。然后当然是虚表指针错了。然后gdb watch虚表指针的地址看看哪个小贱人改了虚表指针,然后发现不知道哪个a[i]++中i正好越界,把虚表指针自增了1(其实看看虚表指针没对齐就该猜到了)。为啥会越界呢,源代码看着没问题,只是在双重循环里。emit IR一看,妈蛋给vectorize错了。然后去读vectorizer的代码,读到最后:
âš™ D15906 [Vectorization] Actually return from error case in isStridedPtr
*。
自那以后看到指针第一件事是看对不对齐。
------------------------- 跑题结束,继续跑题 -------------------------
这东西怎么训练。。不好训练。有太多的东西,都是“人活得久一点,自然就身经百战见得多啦”。
你可能说一个实习生怎么可能那么老,对,这个我兹瓷你,考察实习生这些“经验”而不是“技能”我觉得确实不妥,但是我觉得不妥又有什么用,别人照考不误。
那只能设法让自己身经百战了/w\。不要按知识点去一个个死抠,be a <del>man</del> hacker(大雾)。对于C++岗位来说,把汇编弄弄明白;修修GCC或者LLVM的bug;参加参加GSoC。到时候随便飘点calling convention,vectorization,devirtualization,LTO,搞好气场震慑欺软怕硬的傻逼很重要。
虚表更是不在话下,你可以反问面试官“你看你对虚表这么热情啊,你知不知道multiple inheritance虚表怎么实现的?static_cast此种情况又是怎么实现的?virtual inheritance又是怎么实现的?知道multiple dispatch么?怎么在C++里用模拟实现unbounded multiple dispatch或者bounded multiple dispatch(std::experimental::variant<>::visit)?不知道不要紧,我给您讲讲”。其实这些虚表实现细节只要老实巴交地读点wiki page就行了。
说到虚表。。有一次我定位一个LLVM miscompile,现象是崩溃。发现是虚函数调用时函数指针地址奇怪。然后当然是虚表指针错了。然后gdb watch虚表指针的地址看看哪个小贱人改了虚表指针,然后发现不知道哪个a[i]++中i正好越界,把虚表指针自增了1(其实看看虚表指针没对齐就该猜到了)。为啥会越界呢,源代码看着没问题,只是在双重循环里。emit IR一看,妈蛋给vectorize错了。然后去读vectorizer的代码,读到最后:
âš™ D15906 [Vectorization] Actually return from error case in isStridedPtr
*。
自那以后看到指针第一件事是看对不对齐。
------------------------- 跑题结束,继续跑题 -------------------------
这东西怎么训练。。不好训练。有太多的东西,都是“人活得久一点,自然就身经百战见得多啦”。
你可能说一个实习生怎么可能那么老,对,这个我兹瓷你,考察实习生这些“经验”而不是“技能”我觉得确实不妥,但是我觉得不妥又有什么用,别人照考不误。
那只能设法让自己身经百战了/w\。不要按知识点去一个个死抠,be a <del>man</del> hacker(大雾)。对于C++岗位来说,把汇编弄弄明白;修修GCC或者LLVM的bug;参加参加GSoC。到时候随便飘点calling convention,vectorization,devirtualization,LTO,搞好气场震慑欺软怕硬的傻逼很重要。
虚表更是不在话下,你可以反问面试官“你看你对虚表这么热情啊,你知不知道multiple inheritance虚表怎么实现的?static_cast此种情况又是怎么实现的?virtual inheritance又是怎么实现的?知道multiple dispatch么?怎么在C++里用模拟实现unbounded multiple dispatch或者bounded multiple dispatch(std::experimental::variant<>::visit)?不知道不要紧,我给您讲讲”。其实这些虚表实现细节只要老实巴交地读点wiki page就行了。
我觉得这不是特别合适的面试问题。
比方说我直接回答“两个问题我都想在测试环境下用valgrind跑一发”,可能也是一个思路,但是面试官该怎么继续?假设我valgrind出来就找到错了?……那这个问题我就莫名棋妙地面完了?不太对吧?还是说,为了考察我的知识的全面,你强行告诉我,不,这个问题用valgrind查不出来?
可能我继续说,你故障点附近的代码有多长,我静态查错行不行?面试官又该怎么回答?告诉我又查不出来?感情我自己静态查错查不查得出来还得面试官说了算?
或者我说我想看crash现场的stack trace,面试官您给我现场制备一个?
就是……
感觉这问题……
特别纸上谈兵啊……
虽然我特别讨厌别人引Linus那句名言,但是如果面试官非要问你怎么查错,那答案还真是talk is cheap, show me the code啊……
比方说我直接回答“两个问题我都想在测试环境下用valgrind跑一发”,可能也是一个思路,但是面试官该怎么继续?假设我valgrind出来就找到错了?……那这个问题我就莫名棋妙地面完了?不太对吧?还是说,为了考察我的知识的全面,你强行告诉我,不,这个问题用valgrind查不出来?
可能我继续说,你故障点附近的代码有多长,我静态查错行不行?面试官又该怎么回答?告诉我又查不出来?感情我自己静态查错查不查得出来还得面试官说了算?
或者我说我想看crash现场的stack trace,面试官您给我现场制备一个?
就是……
感觉这问题……
特别纸上谈兵啊……
虽然我特别讨厌别人引Linus那句名言,但是如果面试官非要问你怎么查错,那答案还真是talk is cheap, show me the code啊……
第一个问题:
引发bug的可能性有很多,形形色色的debug方法也有很多,它们各有各的优势,并不存在通用的最优解,我目前用过的调试方法有下面几种:
1. 人肉调试:
对于某些bug,直接根据程序的异常表现,就可以知道问题代码的具体位置,心里逆推演一下相关代码,就可以找到问题产生的原因。
例:刚给客户端加了个多线程模块,F5运行,等了30s。。。咦?客户端界面怎么还没显示出来?任务管理器一看,客户端进程CPU占用为0:八成是刚写的代码死锁了,直接Review代码吧。【Problem Solved】
2. 中断调试
依赖于IDE的调试方法(写C++一般用的都是VS吧),在可能出问题的代码位置打个断点,或者等程序自己出异常中断,或者手动加判断中断。程序中断后,追溯函数调用堆栈,找到产生异常数据的代码。这是最方便的定位bug的方法,但前提是能够在开发环境重现bug。
例:策划突然跑过来说:“新做的技能怎么没伤害啊,是不是代码里的伤害计算公式写错了?balabalabala”。。。计算技能伤害的代码位置打个断点,一看数据,有个乘积因子加载以后的数值是0,“卧槽,你自己回去查下技能表是不是漏填了数据。” 【Problem Solved】
3. Log调试
在经常出错、或极有可能出错的代码位置打印log,从而定位问题的原因。如果bug产生的代码没有被log覆盖到,可以通过临时log排查可能导致出错的问题模块。
例:测试:“刚发布的测试客户端怎么XX界面打不开啊,程序看下呗!”程序猿:“log文件发过来”。看完log:“界面里有个资源文件找不到,是不是美术没上传到SVN?”【Problem Solved】
4. Dump调试
利用Windows API,在程序运行不正常时中断,将此时的程序的内存镜像输出到一个dump文件里,然后利用WinDbg获取中断时的函数调用堆栈,从而定位出bug的代码,使用的前提是bug不会导致程序闪退,否则无法保存dump文件。
例:客服:“刚才有外网玩家反映切换地图的时候程序报错了。”程序猿:“有dump文件吗?”。分析完客服收集的dump文件:“new内存的时候失败了,加个内存池吧。”【Problem Solved】
5. 工具调试
除了上面几种通用的调试方法以外,对于某些特定的问题,可以使用特定的工具进行调试。
例:PIX可以用来调试着色器;LeakDiag可以用来调试内存泄漏;Vtune可以用来调试性能。
Ps:由于这些工具通常会对程序性能产生比较明显的影响,大型程序(比如游戏引擎)通常会直接在代码层面集成相应的模块,并通过log将结果打印出来。
上面的五种方法基本是按照使用的困难程度升序排列的,对于具体的bug,在可以解决问题的情况下采用难度最低的debug方法才是最优解。而题目没有给出bug的具体表现,所以这是一个开放性问题。
不过根据问题的四个关键词:
Ps:产生bug的原因有很多,问题中并没给出bug的具体表现,根本没有办法判断bug产生的具体原因。题主和部分答主将答题思路往“临界区”与“多线程同步”之类的方向靠,我觉得有点答非所问了,毕竟面试官的问题不是“造成bug的原因”,而是“如何debug”。
================================================================
第二个问题:
这个问题中,面试官明确提到了虚函数表被破坏,而虚函数表是放在程序的只读数据段的,根本无法修改,所以只有可能是虚函数表指针被修改了。因此,我觉得对象崩溃的可能原因有下面几个:
1. 访问对象时,对象的构造函数尚未执行完毕。
例:对象是在某个函数内定义的静态对象,比如某个单例函数:
多线程调用时,第一个线程会等待instance的构造函数执行完毕后再返回instance的地址,但由于static的存在,instance的构造函数只会执行一次,如果instance的构造函数尚未执行完毕,有第二个线程使用了GetInstance()函数,此时便会访问一个尚未构造完成的对象。(Ps:据说新的C++标准修复了这个问题,但至少VS2013中这个问题是存在的。)
这个错误会造成程序中断,此时直接根据函数调用堆栈定位到出错的指针或对象即可。
2. 访问对象前,某处代码手动执行了对象的析构函数。
在多态的情况下,手动调用对象的析构函数,虽然对象的内存并不会被释放,但成员变量中的指针会变为野指针,此时访问它们自然会造成程序崩溃。此外,执行析构函数时,C++还会做一些额外的工作:当执行完派生类的析构函数后,C++会将对象的虚函数表指针从派生类的虚函数表指向基类的虚函数表,因此,虽然派生类的内存空间还没有被释放,但此时已经无法再访问派生类中定义的虚函数了。
这个错误会造成程序中断,此时直接根据函数调用堆栈定位到出错的指针或对象即可。
3. 内存越界访问
虚函数表指针是由编译器管理的,正常情况下开发者不会访问或修改它的值。因此,若虚函数表指针被破坏了,说明程序中发生了内存越界访问,造成了虚函数表指针被意外修改。
这个错误虽然会造成程序中断,但出错的位置往往不是错误发生的位置:
A. 如果对象不是通过new创建的
此时需要排查出错对象声明处的代码,观察在它附近声明的对象是否发生了内存越界访问。(尤其要关注数组对象,内存越界访问通常是由于数组下标越界导致的)。
B. 如果对象是通过new创建的
此时需要排查整个程序中所有的指针是否发生了越界访问(仍然优先关注数组)。
一种做法是重载new / delete操作符,分配内存时在返回的内存前后各自多分配一个int作为首尾标记,并将它们各自设定为一个特殊的值。每次释放内存时,检查首尾标记的值是否合法。如果不合法,说明该内存指针发生了越界访问。
引发bug的可能性有很多,形形色色的debug方法也有很多,它们各有各的优势,并不存在通用的最优解,我目前用过的调试方法有下面几种:
1. 人肉调试:
对于某些bug,直接根据程序的异常表现,就可以知道问题代码的具体位置,心里逆推演一下相关代码,就可以找到问题产生的原因。
例:刚给客户端加了个多线程模块,F5运行,等了30s。。。咦?客户端界面怎么还没显示出来?任务管理器一看,客户端进程CPU占用为0:八成是刚写的代码死锁了,直接Review代码吧。【Problem Solved】
2. 中断调试
依赖于IDE的调试方法(写C++一般用的都是VS吧),在可能出问题的代码位置打个断点,或者等程序自己出异常中断,或者手动加判断中断。程序中断后,追溯函数调用堆栈,找到产生异常数据的代码。这是最方便的定位bug的方法,但前提是能够在开发环境重现bug。
例:策划突然跑过来说:“新做的技能怎么没伤害啊,是不是代码里的伤害计算公式写错了?balabalabala”。。。计算技能伤害的代码位置打个断点,一看数据,有个乘积因子加载以后的数值是0,“卧槽,你自己回去查下技能表是不是漏填了数据。” 【Problem Solved】
3. Log调试
在经常出错、或极有可能出错的代码位置打印log,从而定位问题的原因。如果bug产生的代码没有被log覆盖到,可以通过临时log排查可能导致出错的问题模块。
例:测试:“刚发布的测试客户端怎么XX界面打不开啊,程序看下呗!”程序猿:“log文件发过来”。看完log:“界面里有个资源文件找不到,是不是美术没上传到SVN?”【Problem Solved】
4. Dump调试
利用Windows API,在程序运行不正常时中断,将此时的程序的内存镜像输出到一个dump文件里,然后利用WinDbg获取中断时的函数调用堆栈,从而定位出bug的代码,使用的前提是bug不会导致程序闪退,否则无法保存dump文件。
例:客服:“刚才有外网玩家反映切换地图的时候程序报错了。”程序猿:“有dump文件吗?”。分析完客服收集的dump文件:“new内存的时候失败了,加个内存池吧。”【Problem Solved】
5. 工具调试
除了上面几种通用的调试方法以外,对于某些特定的问题,可以使用特定的工具进行调试。
例:PIX可以用来调试着色器;LeakDiag可以用来调试内存泄漏;Vtune可以用来调试性能。
Ps:由于这些工具通常会对程序性能产生比较明显的影响,大型程序(比如游戏引擎)通常会直接在代码层面集成相应的模块,并通过log将结果打印出来。
上面的五种方法基本是按照使用的困难程度升序排列的,对于具体的bug,在可以解决问题的情况下采用难度最低的debug方法才是最优解。而题目没有给出bug的具体表现,所以这是一个开放性问题。
不过根据问题的四个关键词:
关键词一、 “多线程”可以看出面试官为这个bug设定的属性是:
关键词二、 “大量并发”
关键词三、 “一百万次出现一次”
关键词四、“很难重现”
- 很难定位
- 几乎不可能在开发环境中重现
Ps:产生bug的原因有很多,问题中并没给出bug的具体表现,根本没有办法判断bug产生的具体原因。题主和部分答主将答题思路往“临界区”与“多线程同步”之类的方向靠,我觉得有点答非所问了,毕竟面试官的问题不是“造成bug的原因”,而是“如何debug”。
================================================================
第二个问题:
这个问题中,面试官明确提到了虚函数表被破坏,而虚函数表是放在程序的只读数据段的,根本无法修改,所以只有可能是虚函数表指针被修改了。因此,我觉得对象崩溃的可能原因有下面几个:
1. 访问对象时,对象的构造函数尚未执行完毕。
例:对象是在某个函数内定义的静态对象,比如某个单例函数:
static Type* GetInstance()
{
static Type instance;
return &instance;
}
这个错误会造成程序中断,此时直接根据函数调用堆栈定位到出错的指针或对象即可。
2. 访问对象前,某处代码手动执行了对象的析构函数。
在多态的情况下,手动调用对象的析构函数,虽然对象的内存并不会被释放,但成员变量中的指针会变为野指针,此时访问它们自然会造成程序崩溃。此外,执行析构函数时,C++还会做一些额外的工作:当执行完派生类的析构函数后,C++会将对象的虚函数表指针从派生类的虚函数表指向基类的虚函数表,因此,虽然派生类的内存空间还没有被释放,但此时已经无法再访问派生类中定义的虚函数了。
这个错误会造成程序中断,此时直接根据函数调用堆栈定位到出错的指针或对象即可。
3. 内存越界访问
虚函数表指针是由编译器管理的,正常情况下开发者不会访问或修改它的值。因此,若虚函数表指针被破坏了,说明程序中发生了内存越界访问,造成了虚函数表指针被意外修改。
这个错误虽然会造成程序中断,但出错的位置往往不是错误发生的位置:
A. 如果对象不是通过new创建的
此时需要排查出错对象声明处的代码,观察在它附近声明的对象是否发生了内存越界访问。(尤其要关注数组对象,内存越界访问通常是由于数组下标越界导致的)。
B. 如果对象是通过new创建的
此时需要排查整个程序中所有的指针是否发生了越界访问(仍然优先关注数组)。
一种做法是重载new / delete操作符,分配内存时在返回的内存前后各自多分配一个int作为首尾标记,并将它们各自设定为一个特殊的值。每次释放内存时,检查首尾标记的值是否合法。如果不合法,说明该内存指针发生了越界访问。
第一题不太好帮你,应该是如何实施测试环境。
第二题如果你的叙述没有问题,那我觉得面试官有点不专业。
首先怀疑他对C++内存对象模型的理解,如果他确实懂这个,那就是他问题表述能力堪忧。他先说了虚函数表被破坏,然后又说虚函数表里面的内容没有问题,已经自相矛盾了,你的反驳是对的,他可能想说的是虚函数表指针被破坏了。
一个含有虚函数的类,全局只有一个对应的虚函数表,而这个类的每一个实例化对象都有自己的虚函数表指针,指向那个虚函数表。
他可能想考察你对于 use after free问题的排查能力,你可以首先考虑在析构函数打断点,看看该对象是何时被释放了。
如果他硬要说是程序crash导致内存 corruption,你可以说下数据断点来排查问题。
面试的时候如果出现面试官对问题表述不清楚,你应该多追问!千万不要一口就说应该是什么什么,像这个问题,你可以以请求的口吻去追问一下,刚才你说的是虚函数表指针被破坏了么?
你要是说,那应该是虚函数表指针被破坏了啊!如果面试官心理健康,并且确实技术实力强,他会很大方的承认表述错误,技术好的大部分人还是很实事求是的,但是如果对方不是这样的人,那面试官对自己之前的错误下不了台啊,他可能决定给你下马威,结束面试。
当然如果你低姿态已经用了,对方还是要坚持自己的错误,你也别想那么多了。开启撕逼教育模式。
拿过纸和笔,给对方上一课都无妨。
第二题如果你的叙述没有问题,那我觉得面试官有点不专业。
首先怀疑他对C++内存对象模型的理解,如果他确实懂这个,那就是他问题表述能力堪忧。他先说了虚函数表被破坏,然后又说虚函数表里面的内容没有问题,已经自相矛盾了,你的反驳是对的,他可能想说的是虚函数表指针被破坏了。
一个含有虚函数的类,全局只有一个对应的虚函数表,而这个类的每一个实例化对象都有自己的虚函数表指针,指向那个虚函数表。
他可能想考察你对于 use after free问题的排查能力,你可以首先考虑在析构函数打断点,看看该对象是何时被释放了。
如果他硬要说是程序crash导致内存 corruption,你可以说下数据断点来排查问题。
面试的时候如果出现面试官对问题表述不清楚,你应该多追问!千万不要一口就说应该是什么什么,像这个问题,你可以以请求的口吻去追问一下,刚才你说的是虚函数表指针被破坏了么?
你要是说,那应该是虚函数表指针被破坏了啊!如果面试官心理健康,并且确实技术实力强,他会很大方的承认表述错误,技术好的大部分人还是很实事求是的,但是如果对方不是这样的人,那面试官对自己之前的错误下不了台啊,他可能决定给你下马威,结束面试。
当然如果你低姿态已经用了,对方还是要坚持自己的错误,你也别想那么多了。开启撕逼教育模式。
拿过纸和笔,给对方上一课都无妨。
个人觉得:
1. 最好有容错机制+log定位问题。如果非想要完美解决,检测到下次发生的时候把内存和调用栈dump出来。然后回来慢慢看。(具体做法嘛,如果内存足够多,一个简单的搞法是fork进程然后abort掉生成coredump。哇哈哈就是这么暴力,因为代码调api来生成dump的话太耗cpu怕影响正常业务)
2. 这种情况无非是内存写越界或者不正确的类型转换导致的内存写坏。一种最常见的原因是对有虚函数的对象memset,但是内存写坏的原因太复杂,可能性太多了得具体情况具体分析。如果有重现方法,一种比较简单的做法是直接内存watch虚表指针的内存变化。那么写坏的那一刻,什么都知道了
补充:刚刚想到问题2,如果是内存写越界,很有可能不止写坏了一个虚表指针。如果这样的话可以debug模式编译然后valgrind+memcheck很容易找到出问题的调用栈。(当然这只是碰运气哒,也许能比较快一点找到问题)
前面有回答说mprotect,其实个人觉得这样不太好,因为mprotect会导致内存map的地址段被切割。然后这个地址段是有数量限制的,如果对象数量多的话容易出事儿。
1. 最好有容错机制+log定位问题。如果非想要完美解决,检测到下次发生的时候把内存和调用栈dump出来。然后回来慢慢看。(具体做法嘛,如果内存足够多,一个简单的搞法是fork进程然后abort掉生成coredump。哇哈哈就是这么暴力,因为代码调api来生成dump的话太耗cpu怕影响正常业务)
2. 这种情况无非是内存写越界或者不正确的类型转换导致的内存写坏。一种最常见的原因是对有虚函数的对象memset,但是内存写坏的原因太复杂,可能性太多了得具体情况具体分析。如果有重现方法,一种比较简单的做法是直接内存watch虚表指针的内存变化。那么写坏的那一刻,什么都知道了
补充:刚刚想到问题2,如果是内存写越界,很有可能不止写坏了一个虚表指针。如果这样的话可以debug模式编译然后valgrind+memcheck很容易找到出问题的调用栈。(当然这只是碰运气哒,也许能比较快一点找到问题)
前面有回答说mprotect,其实个人觉得这样不太好,因为mprotect会导致内存map的地址段被切割。然后这个地址段是有数量限制的,如果对象数量多的话容易出事儿。
并发和百万次有什么关系,言必称大数据,高并发,这是病。什么叫虚函数表破坏了,怎么得出来的结论,记得
@RednaxelaFX不久前说过一次defect fix,是出现了不对的行为,最后定位到虚函数表破坏了,这都是很难定位的问题,实习生问这些是出于什么考虑的呢?
如果是在现实中我遇到这些问题的话
1、已经知道bug是什么了,那就可以在程序里面自我审查,发现bug的时候冻结自己出dump,然后在集群里面把自己下线,发邮件给农民工来查。
2、C++写指针越界,可以造成任何问题,所以问题也可以不出在这个类本身。光题目本身给出的信息并不能制定任何针对性的方案。
1、已经知道bug是什么了,那就可以在程序里面自我审查,发现bug的时候冻结自己出dump,然后在集群里面把自己下线,发邮件给农民工来查。
2、C++写指针越界,可以造成任何问题,所以问题也可以不出在这个类本身。光题目本身给出的信息并不能制定任何针对性的方案。
先告诉他别扯淡……
问题一:
你以为腾讯的产品十万分之一概率出现的bug就能修得掉?!
别开玩笑了,《王者荣耀》里某个英雄技能的bug,10次就能出现一次的bug 连半年都没能修复。
你先问问他腾讯当初招人的时候有没有问过这个问题。
问题二:
【这个类指针的虚函数表被破坏了,如何定位这个问题?】
【然后面试官说虚函数表里面的内容没有问题】
对不起,我不知道是这位面试官的大学语文没学好还是我的大学语文还给马哲老师了。
"虚函数表里的内容没有问题" && "虚函数表被破坏了" ???
我的三观被破坏了。
问题一:
你以为腾讯的产品十万分之一概率出现的bug就能修得掉?!
别开玩笑了,《王者荣耀》里某个英雄技能的bug,10次就能出现一次的bug 连半年都没能修复。
你先问问他腾讯当初招人的时候有没有问过这个问题。
问题二:
【这个类指针的虚函数表被破坏了,如何定位这个问题?】
【然后面试官说虚函数表里面的内容没有问题】
对不起,我不知道是这位面试官的大学语文没学好还是我的大学语文还给马哲老师了。
"虚函数表里的内容没有问题" && "虚函数表被破坏了" ???
我的三观被破坏了。
先说明我不是面试官也不是他肚里的蛔虫也没有看过标准答案或招聘标准。这里的部分回答还是暴露了很多程序员明明思维欠缺还自以为是。
第一个问题,既然已知难重现,第一反应应该是捞问题场景的log而不是重现!起码反问一句有没log或coredump,提到log这三个字母估计就过了。不是考你各种事后补救措施什么的,这种bug在腾讯的话这个规模下就是每天发生的事情解这种bug是你可能将要频繁面对的最平凡的工作之一!生产环境没log也是不可能的。第一解决办法就是拼体力拼细致拼分析能力来分析log。跟我念10遍loglogloglogloglogloglogloglog。如果要对题主说句安慰的话就是毕业生没经验可以理解不见怪。
都以为亿级的服务是打开你的visualstudio按f5运行来提供的?试图重现是活在梦里!如果没log,老实摊手,给领导认错,添加log和上报代码观察解决!
第二个问题你是没理解清楚题意。虚函数表是静态初始化好只读的。显然这种情况他的意思是对象要么野指针要么对象被overrun了。有现场或coredump应该打印寄存器分析当前栈,打印指针指向的前后内存段看端倪。没现场再来跟我读一次loglogloglogloglogloglogloglog慢动作重播,然后读代码。
一个项目每天上百人协作提交,一次合流几十个文件被改,分分钟一个版本上n个特性。你解的bug也通常不是你写出来的,甚至跨模块引起的。svn回滚的人,提供可用性思路没错,但问题影响未足够大的话你的实践经验根本不适合大公司。
即使面试官对虚函数表的问题描述的确有不严谨,说面试官傻逼的,非要抬杠,回字的四种写法笔划要多精确的,我也不是谦虚,跟你合作你还是另请高明吧。
第一个问题,既然已知难重现,第一反应应该是捞问题场景的log而不是重现!起码反问一句有没log或coredump,提到log这三个字母估计就过了。不是考你各种事后补救措施什么的,这种bug在腾讯的话这个规模下就是每天发生的事情解这种bug是你可能将要频繁面对的最平凡的工作之一!生产环境没log也是不可能的。第一解决办法就是拼体力拼细致拼分析能力来分析log。跟我念10遍loglogloglogloglogloglogloglog。如果要对题主说句安慰的话就是毕业生没经验可以理解不见怪。
都以为亿级的服务是打开你的visualstudio按f5运行来提供的?试图重现是活在梦里!如果没log,老实摊手,给领导认错,添加log和上报代码观察解决!
第二个问题你是没理解清楚题意。虚函数表是静态初始化好只读的。显然这种情况他的意思是对象要么野指针要么对象被overrun了。有现场或coredump应该打印寄存器分析当前栈,打印指针指向的前后内存段看端倪。没现场再来跟我读一次loglogloglogloglogloglogloglog慢动作重播,然后读代码。
一个项目每天上百人协作提交,一次合流几十个文件被改,分分钟一个版本上n个特性。你解的bug也通常不是你写出来的,甚至跨模块引起的。svn回滚的人,提供可用性思路没错,但问题影响未足够大的话你的实践经验根本不适合大公司。
即使面试官对虚函数表的问题描述的确有不严谨,说面试官傻逼的,非要抬杠,回字的四种写法笔划要多精确的,我也不是谦虚,跟你合作你还是另请高明吧。
腾讯是打算招百万年薪的实习生么?
百万并发,有几个大学生能有机会接触到的……
其实哪怕是工作了的人,如果不是搞这个的……估计也不清楚怎么查
反正我是答不上来。
我唯一一次处理很难复现的bug,靠的是code review。
(当然,这个不是多线程的……)
我前东家处理这种问题的方案是查log,查不到就在预计相关的地方加log,等生产环境复现,同时组织专人review代码。
曾经遇到一个某sqlserver服务莫名挂掉的问题,好像最后也没解决,他们搞了个walkaround给它绕过去了。
第二题不会,我完全不会C++。
百万并发,有几个大学生能有机会接触到的……
其实哪怕是工作了的人,如果不是搞这个的……估计也不清楚怎么查
反正我是答不上来。
我唯一一次处理很难复现的bug,靠的是code review。
(当然,这个不是多线程的……)
我前东家处理这种问题的方案是查log,查不到就在预计相关的地方加log,等生产环境复现,同时组织专人review代码。
曾经遇到一个某sqlserver服务莫名挂掉的问题,好像最后也没解决,他们搞了个walkaround给它绕过去了。
第二题不会,我完全不会C++。
第一个问题,100万次并发能出现,如果出现时有coredump文件,那可以通过这个结合现象和日志信息(如果他说没有日志,你可以直接走了)解决,如果没有coredump只有日志,那就分析日志定位到具体的模块和代码,走读代码,特别是锁相关的,分析有什么问题。
第二个问题,他的意思应该是对象指向虚表的指针坏了,虚表本身肯定没问题,不然早就段错误了。所以考虑是踩内存了,如果是越界问题,用一些工具辅助检测,很好定位。如果不允许用工具,那同样,首先定位出问题的对象,走读代码看哪些地方有调用,或者间接调用。如果太灵异,每次出问题的地方都不定,这种问题很难解,没什么有效的办法。可以从代码扫描,编译warming全开的角度去发现可能的原因。
第二个问题,他的意思应该是对象指向虚表的指针坏了,虚表本身肯定没问题,不然早就段错误了。所以考虑是踩内存了,如果是越界问题,用一些工具辅助检测,很好定位。如果不允许用工具,那同样,首先定位出问题的对象,走读代码看哪些地方有调用,或者间接调用。如果太灵异,每次出问题的地方都不定,这种问题很难解,没什么有效的办法。可以从代码扫描,编译warming全开的角度去发现可能的原因。
说实话,第二题没看懂。我也认为只可能是虚函数表指针,或者是表本身被破坏。。。。看了其他大神们的回复,也都是这两种啊。面试官先说不是表本身被修改。然后你就问了一下那是指针被改了?对方就直接说你不懂,就给挂了。orz.....(如果你明白还有其他可能破坏虚函数表,麻烦你也告诉我一下。)
另外,第一题bug不好重现:一般是根据现有情报先分析,确定大致bug的范围;接着只能多打log,尽量做dump。多开几个程序跑,把bug跑出来。如果一个bug只出现一次,神都调试不出来。。
写log上有很多需要注意的。整个流程中的最先输入一定要log。按照流程划分再次log。如果只是线上才出现的bug,说不定跟输入有关系。私以为自己线下模拟高并发并不一定能重现,如果实在是时间充裕可以试一试。另外,如果log下来的引发bug的输入保存好了,可以用自己保存的数据来测试代码,说不定偶发bug就变成必然发生的情况了。如果发生概率提升了,基本就好调试很多。至于code review都是技术之外的手段了。如果发现bug有某种重复性出现的规律,可以自己写条件语句log,还有数据断点都可以使用,包括直接dump堆栈。会好调试很多。
另外,第一题bug不好重现:一般是根据现有情报先分析,确定大致bug的范围;接着只能多打log,尽量做dump。多开几个程序跑,把bug跑出来。如果一个bug只出现一次,神都调试不出来。。
写log上有很多需要注意的。整个流程中的最先输入一定要log。按照流程划分再次log。如果只是线上才出现的bug,说不定跟输入有关系。私以为自己线下模拟高并发并不一定能重现,如果实在是时间充裕可以试一试。另外,如果log下来的引发bug的输入保存好了,可以用自己保存的数据来测试代码,说不定偶发bug就变成必然发生的情况了。如果发生概率提升了,基本就好调试很多。至于code review都是技术之外的手段了。如果发现bug有某种重复性出现的规律,可以自己写条件语句log,还有数据断点都可以使用,包括直接dump堆栈。会好调试很多。
回答里说用测试环境,有测试环境当然好说了,如果没有呢?比如说一个在线交易系统,每100万比交易就有一笔交易价格是错误的,而且只有线上环境才出问题,也证实了对方的接口没问题(一样的接口其他公司没有这问题),怎么Debug?
我仔细看了下你的文字表达,1. 没看懂。2. 我遇到的一种情形是指针指错了,指针的值是其它object的,这样这个指针调用虚函数方法,将会进行虚表推断,则会指向完全不相关的“函数”执行,导致匪夷所思的错误。
注意“虚函数表”(编译决定了)和推断时候得到的“实际内容”也叫做“虚函数表”的措辞。这个没什么深奥的,但不要弄乱了。
其实没有必要问实习生,这么难的问题,实际中遇到的坑爹问题多得是,何必要拿来作为经验。
注意“虚函数表”(编译决定了)和推断时候得到的“实际内容”也叫做“虚函数表”的措辞。这个没什么深奥的,但不要弄乱了。
其实没有必要问实习生,这么难的问题,实际中遇到的坑爹问题多得是,何必要拿来作为经验。
确实你c++基础太差了,不过这问题问应届毕业生有点过分了。
第一个问题,首先你得定性问题的类型,100万是一个虚数,10万还是1万都无所谓。只是想告诉你这不是一个必然触发的bug,既然在多线程环境里,不是必然触发的问题,那必然是并发问题了。我想这是面试官想听到的第一个关键字。既然是并发问题,那把问题找出来的手段就太多了,不过既然是面试,我想面试官绝对不想听你说用某某工具就摆平问题的答案,考验你基础知识的时候到了,如果你围绕并发的本质和怎么提高并发bug的重现概率来讨论,并来一段静态代码分析的经验之谈,那应该能更好的展示自己。涉及你大学的知识包括了计算机语言和操作系统
第二个问题,这是一个典型的宕机现场分析的案例。题目的所有描述,都是客观的在说明当前发生的事情,也就是看得到的现象,而对于宕机现场的分析,并不是说我把宕机的原因说清楚就ok了。要知道在实际的工作场景中,最终的目的是改掉bug,不宕机。所以我们看到问题后,有两个步骤是必须要做的:分析引起问题的原因,提出修改问题的解决方案。而面试官给你出的问题,是第一个步骤,需要你去分析什么原因引起问题的,所谓的定位问题,并不是定位问题的现象,而是定位代码中哪一行代码引起的问题。我们看到的宕机现象是虚表被破坏,但明显的一个陷阱是他并没有说其他数据的完整性,所以当你分析这个问题的时候,必须把多种情况都说全,作为面试官才可能给你一个满分。如果单单只是虚表被破坏,而虚表后面的数据有一部分是完好的,被修改的只是靠前的部分,那应该是内存越界导致的问题,而你又要根据是堆还是栈上分配的空间来定位可能越界的操作来自于哪一段内存中的数据,根据这个数据追溯到内存越界的案发现场,也就是出问题的代码。如果数据完全被破坏,并且呈现000之类的连续重复字符,那应该考虑是否在哪出做了memset之类的操作。如果完全破坏,并呈现混乱数据,那应该是某个野指针的访问,这个查起来就比较复杂了,需要大量的代码静态分析的经验。这题目主要涉及到你大学知识中的计算机语言、数据结构、操作系统和编译原理。
其实我觉得面试官出的题目的难度略高了,不过作为一个优秀的大学毕业生而言,60分的回答应该不成问题的,而题主的回答很可惜的没有得分。不过不要灰心,你才大三,还有一年的时间可以好好努力的补足基础知识,你别看大学中学习的那些课程意义不大,但在实际的工作中,它们是你真正能依赖的本钱。努力攒把。
第一个问题,首先你得定性问题的类型,100万是一个虚数,10万还是1万都无所谓。只是想告诉你这不是一个必然触发的bug,既然在多线程环境里,不是必然触发的问题,那必然是并发问题了。我想这是面试官想听到的第一个关键字。既然是并发问题,那把问题找出来的手段就太多了,不过既然是面试,我想面试官绝对不想听你说用某某工具就摆平问题的答案,考验你基础知识的时候到了,如果你围绕并发的本质和怎么提高并发bug的重现概率来讨论,并来一段静态代码分析的经验之谈,那应该能更好的展示自己。涉及你大学的知识包括了计算机语言和操作系统
第二个问题,这是一个典型的宕机现场分析的案例。题目的所有描述,都是客观的在说明当前发生的事情,也就是看得到的现象,而对于宕机现场的分析,并不是说我把宕机的原因说清楚就ok了。要知道在实际的工作场景中,最终的目的是改掉bug,不宕机。所以我们看到问题后,有两个步骤是必须要做的:分析引起问题的原因,提出修改问题的解决方案。而面试官给你出的问题,是第一个步骤,需要你去分析什么原因引起问题的,所谓的定位问题,并不是定位问题的现象,而是定位代码中哪一行代码引起的问题。我们看到的宕机现象是虚表被破坏,但明显的一个陷阱是他并没有说其他数据的完整性,所以当你分析这个问题的时候,必须把多种情况都说全,作为面试官才可能给你一个满分。如果单单只是虚表被破坏,而虚表后面的数据有一部分是完好的,被修改的只是靠前的部分,那应该是内存越界导致的问题,而你又要根据是堆还是栈上分配的空间来定位可能越界的操作来自于哪一段内存中的数据,根据这个数据追溯到内存越界的案发现场,也就是出问题的代码。如果数据完全被破坏,并且呈现000之类的连续重复字符,那应该考虑是否在哪出做了memset之类的操作。如果完全破坏,并呈现混乱数据,那应该是某个野指针的访问,这个查起来就比较复杂了,需要大量的代码静态分析的经验。这题目主要涉及到你大学知识中的计算机语言、数据结构、操作系统和编译原理。
其实我觉得面试官出的题目的难度略高了,不过作为一个优秀的大学毕业生而言,60分的回答应该不成问题的,而题主的回答很可惜的没有得分。不过不要灰心,你才大三,还有一年的时间可以好好努力的补足基础知识,你别看大学中学习的那些课程意义不大,但在实际的工作中,它们是你真正能依赖的本钱。努力攒把。
第一题没经验,不清楚。不乱说了。说下第二题。这个bug是c++里很容易出现的一个bug,可能是写内存时越界了,破坏了虚函数表。这种问题难在难以定位,是哪里写内存写错了,因为写完后是看不出来错误的,只有在其它地方用到这块内存的时候,才会暴露出这个bug。所以很难调试解决,即便是编码经验十年的程序员,解决这类问题也要花不少时间排查。因为这个bug是一个经典且很有难度的问题,实际项目出现的概率不小,但是你并没有遇到过,因此面试官断言你编码的实际经验太少。
今天我在麦当劳听到俩人好像就在说第一个问题,其中一男的说万分之一的概率我不care,即使增加到万分之二,万分之五,虽然数据上是原来的两倍,五倍,但对他而言还是概率很低所以不考虑,然后听到另一个问他要到多少他才会care,然后我点的好了我去拿,之后回来就没再看到他俩。。。。。
不要打我虽然我确实不是在认真答题。。。
不要打我虽然我确实不是在认真答题。。。
1.多线程的bug。。。还是老老实实打log吧。。。我之前也是通过打log才找到多线程的bug的,不过情景没问题里面那么严峻。
2.看看这个帖子里面的测试用例,有教你打印虚函数表出来,虽然我没完全弄懂面试官究竟要问的啥。 C++ 虚函数表解析
感觉这类问题面试官主要是想和你多多伸展主题,和你深入交流的。只是回答会与否那是不怎么好的。
2.看看这个帖子里面的测试用例,有教你打印虚函数表出来,虽然我没完全弄懂面试官究竟要问的啥。 C++ 虚函数表解析
感觉这类问题面试官主要是想和你多多伸展主题,和你深入交流的。只是回答会与否那是不怎么好的。
陈子雅、Watson Yuan
赞同
我只能说,我在鹅厂这么久了,有很多业务不区分test and int 环境。。。经常找op,问说没有test环境,导致压测都要在int环境下进行。至于这两个问题,lz以应届生的身份回答已经很nice了面试官太2b 不要在意,我遇到很多面试官同事,只问理想,压根就不看技术的。鹅厂就是这么乱,业务太多。部门太杂
匿名用户
赞同
其实我不是码农,不过也能看出来这是比较难的问题。
只是看到有些回答质疑为什么问实习生这么难的问题
我想解释一下,其实问面试者问题的时候,一堆问题中必然会有一些明显超出你预期能力的问题。问你这些不是要为难你,首先是看你在面对自己解决不了的问题时的思路;其次就是根据你能解决问题的难度测试一下你的能力上限。
只是看到有些回答质疑为什么问实习生这么难的问题
我想解释一下,其实问面试者问题的时候,一堆问题中必然会有一些明显超出你预期能力的问题。问你这些不是要为难你,首先是看你在面对自己解决不了的问题时的思路;其次就是根据你能解决问题的难度测试一下你的能力上限。
第一个问题,一些条件并不是很严谨,但可以一步步和面试官推理和探讨。这个过程是加分的。
已知的是,运行一段时间(一定次数)后会有可能出现bug,而且程序有大量并发。
并发的确加大了查找问题的困难(如果确实是并发引起的),但是也不用一开始就把bug和并发联系在一起。通常的思路是避免去猜,应当有目的性的缩小排查范围,从最简单的环节开始。
第一条:不要慌。这个bug是能在期望的时间内重现的,有这一条你就有底:修正bug只是时间问题。最麻烦的情况是,bug不知道怎么什么时候能重现,你这情况已经比较好解决了。
程序是刚上线的吗? 是不是新版本才有这毛病,旧版本没有?如果是,那很大概率上,问题就在改动部分里面。你要重点先审查新版本的改动代码部分,往往通过这一步已经能找到问题了。
程序是不是已经做过了单元测试?尤其是近期改动过,或者算法较复杂的模块的单元测试? 如果没有,可以先对高风险模块做一下单元测试,确保各种边界下输出正常。
用Valgrind跑一下,看看有没有内存越界或者泄露。内存越界的后果是不确定的,尤其是越界不严重时,啥时候挂完全是运气问题,但这个往往很容易被忽视。
程序的输入是固定的测试数据吗?如果是的话,看看单线程下能否重现。若可以,那基本上就是逻辑错误,和并发无关。如果单线程或者少量并发OK,高并发跪,那么有几种办法:
1. 可以打出verbose日志,对照日志逐步分析。
2. 可以去掉程序中的某些模块后,编译,运行(当然输出也可能不一样了),校验新的输出是否正常。 用类似于折半查找的方式,几次后就可以较准确的大致定位了。
如果不是,而是生产环境中的输入,那麻烦一点。
一般来说,高并发的程序都是前面都有负载均衡的,上线也是灰度的。先将所有机器回滚至旧版本,留一台新版本,将其verbose日志打出。下载下来分析。若日志太多影响性能,也可以仅打出输入部分的日志,拿到测试环境中尝试重现。
第二个问题我看的不是太懂。不过一般来说都是越界操作之类导致。那一般就是用valgrind工具或者代码审查来解决。
已知的是,运行一段时间(一定次数)后会有可能出现bug,而且程序有大量并发。
并发的确加大了查找问题的困难(如果确实是并发引起的),但是也不用一开始就把bug和并发联系在一起。通常的思路是避免去猜,应当有目的性的缩小排查范围,从最简单的环节开始。
第一条:不要慌。这个bug是能在期望的时间内重现的,有这一条你就有底:修正bug只是时间问题。最麻烦的情况是,bug不知道怎么什么时候能重现,你这情况已经比较好解决了。
程序是刚上线的吗? 是不是新版本才有这毛病,旧版本没有?如果是,那很大概率上,问题就在改动部分里面。你要重点先审查新版本的改动代码部分,往往通过这一步已经能找到问题了。
程序是不是已经做过了单元测试?尤其是近期改动过,或者算法较复杂的模块的单元测试? 如果没有,可以先对高风险模块做一下单元测试,确保各种边界下输出正常。
用Valgrind跑一下,看看有没有内存越界或者泄露。内存越界的后果是不确定的,尤其是越界不严重时,啥时候挂完全是运气问题,但这个往往很容易被忽视。
程序的输入是固定的测试数据吗?如果是的话,看看单线程下能否重现。若可以,那基本上就是逻辑错误,和并发无关。如果单线程或者少量并发OK,高并发跪,那么有几种办法:
1. 可以打出verbose日志,对照日志逐步分析。
2. 可以去掉程序中的某些模块后,编译,运行(当然输出也可能不一样了),校验新的输出是否正常。 用类似于折半查找的方式,几次后就可以较准确的大致定位了。
如果不是,而是生产环境中的输入,那麻烦一点。
一般来说,高并发的程序都是前面都有负载均衡的,上线也是灰度的。先将所有机器回滚至旧版本,留一台新版本,将其verbose日志打出。下载下来分析。若日志太多影响性能,也可以仅打出输入部分的日志,拿到测试环境中尝试重现。
第二个问题我看的不是太懂。不过一般来说都是越界操作之类导致。那一般就是用valgrind工具或者代码审查来解决。
其实第一个问题面试很容易被问。
记住这个问题千万不要去较真是否真的无法重现:技术就是技术,代码就是代码,一切出现的异常都是正常的,不可能没有原因!
所以回答你平日遇到不容易排查出的异常后的整个解决流程就行了,面试官只是想通过这个看似很难重现的幌子测试你是不是善于排查问题,所以千万别把焦点带到难以重现上去!
记住这个问题千万不要去较真是否真的无法重现:技术就是技术,代码就是代码,一切出现的异常都是正常的,不可能没有原因!
所以回答你平日遇到不容易排查出的异常后的整个解决流程就行了,面试官只是想通过这个看似很难重现的幌子测试你是不是善于排查问题,所以千万别把焦点带到难以重现上去!
匿名用户
在腾讯,问题一,用平时工作来说。
如果是core,又不是严重影响现网服务,给机器开个core等下次有core文件了直接gdb一般就出来了。如果有严重影响还是先开core然后屏蔽那台机器继续gdb…
如果不是core,都能用通过查日志和看代码解决。现网没有日志就加到有。
测试环境当然更好,但是可能环境优点混乱,大家都在乱改。
如果是core,又不是严重影响现网服务,给机器开个core等下次有core文件了直接gdb一般就出来了。如果有严重影响还是先开core然后屏蔽那台机器继续gdb…
如果不是core,都能用通过查日志和看代码解决。现网没有日志就加到有。
测试环境当然更好,但是可能环境优点混乱,大家都在乱改。
看了很多回答,个人不懂这些专业知识,但是看到很多说这超出了实习生应聘的范围,强人所难之类的回答。如果这两道题真的很难。那么,腾讯面试官很可能是通过这两道题来考验你对工作中出现问题的态度,而不是你的专业知识。
我感觉第一个问题的关键在于怎么重现和定位这个bug,加上原话里有“面试官说这个bug很难重现”,而且100万是一个泛泛的数字,后来又说那10万次呢,其潜台词就是,一个很大的数,但是这个概率你不能忽略,那么就可以判断,这1百万次重现的并发不能通过常规的运行模式来捕捉,需要人为的调整设定出bug的相关线程的并发频率。
象有些回答里说的常规的加压也许肯能达到要求,但是我觉得这是系统测试工程师的做事方式,对于开发者,应该可以做的更加有技术含量,所以我认为这个题目的本意还是想考你怎么通过已有信息缩小范围,再有针对性的人对这个范围内的任务“加压”,比如通过修改参数,通过调整最大最小阀值,模拟一个极端的测试环境,如果怀疑是溢出,就开始查malloc之类的内存区域,可以大幅度调大和缩小这些值,看看bug是不是出现的更快和更慢,甚至做一个仿真测试,都有助于快速的定位这个bug。
象有些回答里说的常规的加压也许肯能达到要求,但是我觉得这是系统测试工程师的做事方式,对于开发者,应该可以做的更加有技术含量,所以我认为这个题目的本意还是想考你怎么通过已有信息缩小范围,再有针对性的人对这个范围内的任务“加压”,比如通过修改参数,通过调整最大最小阀值,模拟一个极端的测试环境,如果怀疑是溢出,就开始查malloc之类的内存区域,可以大幅度调大和缩小这些值,看看bug是不是出现的更快和更慢,甚至做一个仿真测试,都有助于快速的定位这个bug。
匿名用户
这种问题太模糊了,要问清楚再答,直接给答案没多大意义。
比如第一题,这个业务每秒调用次数多少?是导致崩溃的致命问题,还是只是ui展现有微小差异的可忽略问题?是否系统热点?有无日志?有无try catch?有无异常检查?问题是否出现在共享数据上?之后再根据dump,日志,或者客户端压测工具,甚至gdb condition break定位。
第二题,对象崩溃是什么意思?对象是一个进程吗?虚函数表指针被破坏,只要找出调用者是谁就行了。对于系统管理的地址空间系统会抛异常,如果越界到系统崩溃自然可以查dmp。如果没越界到这份上,可以用很多工具来查越界,也可以用共享内存加魔法数字,检查魔法数字被更改情况定位问题。如果越界只有那几个字节。。上gdb,还是不行,好好检查一遍代码,再不行,在关键位置插assert代码,只要不是太离谱的问题,这一套基本都能查出来了。
比如第一题,这个业务每秒调用次数多少?是导致崩溃的致命问题,还是只是ui展现有微小差异的可忽略问题?是否系统热点?有无日志?有无try catch?有无异常检查?问题是否出现在共享数据上?之后再根据dump,日志,或者客户端压测工具,甚至gdb condition break定位。
第二题,对象崩溃是什么意思?对象是一个进程吗?虚函数表指针被破坏,只要找出调用者是谁就行了。对于系统管理的地址空间系统会抛异常,如果越界到系统崩溃自然可以查dmp。如果没越界到这份上,可以用很多工具来查越界,也可以用共享内存加魔法数字,检查魔法数字被更改情况定位问题。如果越界只有那几个字节。。上gdb,还是不行,好好检查一遍代码,再不行,在关键位置插assert代码,只要不是太离谱的问题,这一套基本都能查出来了。