使用 Visual Studio 调试共享库¶
在上一个示例中,我们讨论了如何在 Visual Studio 中调试依赖项。但在使用 Conan 依赖项的项目中,原始构建文件夹和构建文件可能不存在。默认情况下,Conan 包不包含在 Visual Studio 中调试库所需的信息,这些信息存储在库编译期间生成的 PDB 文件中。在使用 Conan 时,这些 PDB 文件生成在构建文件夹中,而构建文件夹仅在构建库时需要。因此,使用 conan cache clean 清理 Conan 缓存以移除构建文件夹并节省磁盘空间是一项常见操作。
对于这些构建文件夹不存在的情况,我们创建了一个钩子,它会将 PDB 文件从构建文件夹复制到包文件夹。默认情况下无法强制执行此行为,因为 PDB 文件通常比整个包大,并且会大大增加包的大小。
本节将通过一些示例,演示如何在不同情况下调试项目,以展示用户如何利用 PDB 钩子。
创建项目并按常规调试¶
首先,我们将按常规调试项目,这在 上一个示例 中已详细说明。我们可以像上一节一样从源代码开始构建我们的依赖项,只是这次我们将它们构建为共享库。首先,从 GitHub 上的 examples2 仓库 克隆示例所需的源代码,然后创建项目。
$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/tutorial/consuming_packages/simple_cmake_project
$ conan install . -o="*:shared=True" -s build_type=Debug --build="zlib/*"
...
Install finished successfully
# CMake presets require CMake>=3.23
$ cmake --preset=conan-default
注意
我们只涵盖依赖项构建为共享库的情况,因为 PDB 文件及其与库的链接方式对于静态库有所不同。
现在我们可以打开解决方案 compressor.sln,在 Visual Studio 中打开我们的项目并按上一个示例所述进行调试。在第 22 行设置断点,运行调试器并使用“步入”功能,将使我们能够调试到依赖项文件 deflate.c 内部。
在这种情况下,原始构建文件都存在,因此调试器按预期工作。接下来,我们将查看在从 Conan 缓存中移除构建文件后调试器如何工作。
从 Conan 缓存中移除构建文件¶
有多种原因可能导致依赖项编译后构建文件不存在。我们将使用 conan cache clean 命令清理缓存中的构建文件,以模拟其中一种情况。--build 标志确保我们仅移除构建文件,因为此示例需要源代码文件。
$ conan list "zlib/1.2.11:*"
$ conan cache path --folder build zlib/1.2.11:17b26a16efb893750e4481f98a154db2934ead88
$ conan cache clean zlib/1.2.11 --build
$ conan cache path --folder build zlib/1.2.11:17b26a16efb893750e4481f98a154db2934ead88
在关闭并重新打开 Visual Studio 中的解决方案后,我们可以再次尝试调试。如果您尝试在第 22 行的断点处步入依赖项,您会注意到它将直接跳到下一行,因为 Visual Studio 没有关于依赖项的调试信息。
安装一个钩子将 PDB 文件复制到包文件夹¶
为了解决包文件夹中 PDB 文件缺失的问题,我们创建了一个钩子,它将 PDB 文件从构建文件夹复制到包文件夹。该钩子在 conan-extensions 仓库 中可用。安装整个仓库也可以,但我们建议仅通过以下方式安装 conan-extensions 仓库中的钩子文件夹:
$ conan config install https://github.com/conan-io/conan-extensions.git -sf=extensions/hooks -tf=extensions/hooks
该钩子的设计默认不运行,因为它会显著增加包的大小。正如 钩子文档 中所述,我们需要将钩子的名称更改为以 hook_ 开头。要定位钩子放置的路径,请运行命令 conan config home 以查找您的本地缓存路径,然后转到 extensions/hooks 文件夹,并将 _hook_copy_pdbs_to_package.py 文件重命名。请注意,此钩子将在每次运行 package() 方法时运行,要禁用该钩子,只需将其重命名回以 _hook_ 开头。
该钩子实现为一个后包钩子(post-package hook),这意味着它将在通过配方(recipe)的 package() 方法创建包之后执行。这样可以避免任何潜在问题,执行顺序如下:
执行配方的
build()方法,生成 DLL 和 PDB 文件执行配方的
package()方法,将必要的文件复制到包文件夹(在此情况下为 DLL,但不包括 PDB)钩子执行,将 PDB 文件从构建文件夹复制到包文件夹中与其对应的 DLL 旁边
该钩子利用了 Visual Studio 安装中包含的 dumpbin 工具。此工具允许我们获取 DLL 的信息,在本例中是其关联 PDB 文件的位置。它将用于包中的每个 DLL,以查找其 PDB 并将其复制到包文件夹。
有关 PDB 文件如何在 Visual Studio 中工作以及我们如何使用它来创建钩子的更多信息,请参阅 钩子 README。
在没有构建文件的情况下调试¶
安装钩子后,我们将再次从源代码创建项目,这样钩子就可以将 PDB 文件与包 DLL 一起复制到包文件夹中,以便调试器找到它们。
$ conan install . -o="*:shared=True" -s build_type=Debug --build="zlib/*"
...
zlib/1.2.11: Calling package()
...
[HOOK - hook_copy_pdbs_to_package.py] post_package(): PDBs post package hook running
...
Install finished successfully
# CMake presets require CMake>=3.23
$ cmake --preset=conan-default
请注意,在运行 conan install 时,您将看到钩子在调用 package() 之后运行的输出。为了测试钩子,我们可以再次清理缓存以移除构建文件,这包括用于构建库的源代码以及最初生成的 PDB 文件。
$ conan cache clean zlib/1.2.11 --build
再次在 Visual Studio 中打开解决方案并启动调试器。当您尝试在第 22 行步入依赖项时,将弹出一个错误消息,告知文件未找到,并询问文件的位置。我们可以关闭此窗口,它将提供查看反汇编的选项,由于 PDB 的存在,反汇编可以被调试。PDB 仅包含调试信息,但 Visual Studio 缺少源文件,因此它将无法像最初那样进行源码调试。
为调试器定位源代码路径¶
删除原始构建文件后,Visual Studio 将无法自行找到源文件。为了能够进行源代码调试,有一个选项可以手动设置源文件夹路径,以便能够进行源代码调试。这要求依赖项的源文件存在。在我们的情况下,我们可以通过运行 conan cache path 来获取此源文件的位置。
$ conan cache path --folder source zlib/1.2.11
如果此源路径不存在,我们可以使用配置来重新下载源代码。
$ conan install . -o="*:shared=True" -s build_type=Debug -c:a="tools.build:download_source=True"
一旦我们有了源路径,就可以在 Visual Studio 中设置它,以便调试器可以找到源文件。在解决方案资源管理器中右键单击解决方案,然后选择“属性”。在“通用属性”部分中,转到“调试源文件”,然后添加我们的源路径。
再次启动调试器将允许我们像在第一个示例中一样步入依赖项的代码。
注意
如果对源文件有修补,我们将无法调试这些修改过的文件,因为我们使用的是源文件夹中的文件,而修补是在稍后一步应用,在构建文件夹中编译之前。
对源文件的任何修改都将不允许对其进行调试,因为 Visual Studio 会进行校验和检查,因此它们必须与库编译时使用的文件完全相同。