zl程序教程

您现在的位置是:首页 >  系统

当前栏目

暴雪团队使用VS进行Linux平台崩溃分析

Linux团队vs平台 分析 进行 崩溃 使用
2023-09-14 09:10:46 时间

蝎子

暴雪正在使用Visual Studio 2019在WSL上对Linux平台上发生的崩溃进行分析。本文来自暴雪高级软件工程师Bill Randolph,目前他负责暗黑破坏神4的开发工作。感谢Bill给我们带来这次技术分享。

 

简介

在暗黑4的开发中,我们主要是在Windows平台进行代码开发,然后我们会将代码编译到多个目标平台。这些平台中包括我们的Linux服务器。(当然,如各位所想到的,在代码中我们添加了一系列条件编译以及必要的平台相关代码)
我们使用这种工作方式有很多原因。第一个,就是我们团队的核心优势是在Windows平台,甚至我们的服务器开发工程师也大部分对Windows平台开发比较擅长。我们认为,团队中所有的工程师能使用比较常用的开发工具集和知识库是非常有帮助的。
我们选择在Windows作为开发平台的另一个原因是Visual Studio所提供的强大功能和丰富的工具集和。而在Linux世界里,并没有一个可以与之抗衡的集成开发环境,即使我们之前也曾在Linux平台上做代码开发。

然而,这个开发模式也存在一些问题。例如,当我们部署在Linux上的程序发生崩溃时,我们希望能有一种方法可以对崩溃转储进行调试,看看到底发生了什么事情。
可以想到的一种方法是:远程登录到发生崩溃的服务器上,然后执行GDB调试器来进行崩溃分析。但是这种方法有很多缺陷,第一是我们并不希望在目标服务器上部署源代码,所以如果没有源代码,GDB就很难分析出什么有意义的东西。
另一个原因就是GDB调试器本身:除非工程师经常使用GDB,否则新手工程师就很难熟练地使用GDB进行代码调试,而我们的工程师都比较喜欢使用简单易用的调试工具,同时他们也十分熟悉这些工具。因为我们开发团队只有2到3名工程师对GDB比较熟悉,所以实际上也只有他们能有效地的调试产品崩溃问题,这对于我们来说,实在是不太高效。

一直以来,我们都在寻找一种更加简单的方式来在Linux服务器上调试代码。我们很高兴地发现:我们能够利用新的Visual Studio功能,使我们能够在熟悉的开发环境中进行简单方便的调试!
毫不夸张地说,这真是:梦想成真啊。

 

说说我们的调试工作流程

只有在安装了WSL或者在Connection Manager中添加了Linux连接之后,你才可以在Visual Studio中调试Linux程序。我们开发团队的每个人都安装了WSL,使用的发行版本是我们即将实际部署的版本。我编写了一个脚本,可以用来在WSL里一键安装所有用于构建服务器程序的开发工具和支持库。

(另外我还想强调的一点是,我们发现WSL是开发者用来验证代码改动的最佳Linux环境。非常神奇的一个地方是,开发者可以直接进入WSL环境中的共享文件夹,然后就可以直接进行代码构建了。这比使用虚拟机或者容器,实在是强太多了。如果你正在使用CMake工具,那你也可以使用Visual Studio中对于WSL的原生支持)

下面我里提供一些有关我们构建的背景信息吧。我们在Windows上开发代码,并使用了一台可以在Windows下运行的Windows版本代码。这对于正常功能开发很有用。但是,我们在Linux上部署服务器,这需要在Linux本身上构建代码。Linux版本是在构建服务器场上生成的,该构建服务器场使用Linux机器上的构建系统来构建服务器及其要部署的容器。Linux可执行文件仅部署在容器中,开发人员通常无权对其进行访问。

当服务器在我们的基础架构中崩溃时,持续集成流程会通知我们,并将核心转储文件存档到网络共享文件夹中。要在Linux或Visual Studio中调试代码崩溃,必须具有正在运行的可执行文件。它还有助于调试已部署容器上使用的对应的共享库。我们使用另一个脚本来获取这些文件。首先,我们将内核复制到本地计算机,然后运行脚本并将其指向内核。该脚本会下载使用该版本构建的Docker容器,从中提取服务器二进制文件,以及某些共享的运行时库供GDB使用。(这避免了WSL版本与部署的Linux版本不完全匹配时可能遇到的GDB兼容性问题。) 脚本将写入~/ .gdbinit,以将共享库设置为调试会话的系统库。

然后,我们切换到Visual Studio,真正有意思的地方开始了。我们加载解决方案以构建Windows版本的服务器。然后,我们在“调试”->“其他调试目标”->“仅使用本机调试Linux核心转储”下打开新的调试对话框。我们启用“在WSL上调试”复选框,并填写转储文件和服务器二进制文件的(特定于WSL!)路径。之后,我们点击“调试”,就可以开始愉快的调试了!

 

Visual Studio在后台调用WSL中的GDB。在进行一些磁盘活动之后,VS会弹出一个崩溃的调用堆栈,指令指针位于相关代码行上。这是一个勇敢的新世界!

因此,接下来是识别崩溃的任务。我们有一个崩溃处理程序来拦截崩溃以执行一些记录工作,因此实际崩溃将落在单线程服务器中的调用堆栈中。但是,我们的某些服务器是多线程的,崩溃可能是由这些线程中的任何一个引起的。我们的崩溃处理程序会记录崩溃文件和行的来源,因此检查这些变量可为我们提供第一个线索;我们将查找执行该代码的调用堆栈。

在几周前的过去,我们将使用GDB获取所有线程的回溯并仔细阅读结果列表,以查看哪个线程具有最有可能崩溃的调用堆栈。例如,如果一个线程仅在休眠,则很可能不是崩溃的线程。我们将寻找一个栈,该栈的内容要多于几个带有“睡眠”的帧,然后检查代码以查看问题是否明显,或者进入GDB本身以检查进程状态。

但是,Visual Studio为我们提供了强大得多的选项。对于多线程内核,可以在调试会话中打开“线程”窗口,并在每个线程中四处查看以查看堆栈的外观。这与gdb方法非常相似,如果有50个线程,可能会非常乏味。幸运的是,有一个功能可以使此操作变得容易得多:并行堆栈。

我承认,直到Erika Sweet和她的团队告诉我们,我们大多数人都不了解Parallel Stacks。调用Debug-> Windows-> Parallel Stacks(仅在调试会话期间可用)会打开一个新窗口,其中显示了进程中每个线程的调用堆栈。这是整个进程空间的一个迷人视图。你可以双击任何线程中的任何堆栈框架,Visual Studio会在源和调用堆栈窗口中都跳转到该框架。对于我们来说,这是一个非常节省的时间。

一旦我们可以看到崩溃附近的代码,就可以使用鼠标悬停,QuickWatch或Visual Studio中的其他任何工具检查变量。的确,在发布版本中,许多变量已被优化,但同时许多变量还没有被优化!与使用GDB相比,使用Visual Studio界面可以更快地解决问题。

 

总结

我们的开发团队对于从Visual Studio的生产环境中调试Linux内核的能力感到非常兴奋! 对于我们来说,这是一个改变游戏规则的方法,因为它使更多开发人员可以“狂野地”主动诊断问题,并且它使我们所有人都可以使用功能强大的Visual Studio调试工具集。 初始设置完成后,只需一分钟左右的时间即可在Visual Studio中进行调试。 此功能将使我们的代码中发现问题的速度更快,更有效! 感谢Erika和她的团队在此方面与我们合作!

最后

Microsoft Visual C++团队的博客是我非常喜欢的博客之一,里面有很多关于Visual C++的知识和最新开发进展。大浪淘沙,如果你对Visual C++这门古老的技术还是那么感兴趣,则可以经常去他们那(或者我这)逛逛。
本文来自:《Blizzard Diablo IV debugs Linux core dumps from Visual Studio》