注意:我的目标关注点是 C# 使用常规 MSIL 以 CLR 为目标,以防万一有一些东西适用于此但不适用于更一般的情况。
一些现有的源代码调试支持示例
最近发布了 Sourcepack 项目,它允许用户重写 pdb 文件中的源路径以指向不同的位置。当您拥有程序集的源代码但不想尝试将其放入与构建时完全相同的文件系统位置时,这非常有用。
http://lowleveldesign.wordpress.com/2011/08/26/sourcepack-released/
对于开源项目,使用http://www.symbolsource.org/作为让项目用户轻松获取符号和源代码的一种方式是一个绝妙的主意。
问题
但是,经常有项目出于法律或方便的原因,使用这种方法不是很可行。此外,可能正在调试项目的人员可能相对较少或包含在内。
默认情况下,项目的 pdb 包括指向磁盘上文件 (IIRC) 的指针,然后源索引可以添加将指针嵌入源位置的能力(例如,在版本控制系统中),然后使用源服务器实际获取源的指针。
目标
看起来事情可能更简单(对于某些构建,例如调试和/或仅限内部),只需将实际源代码放入 pdb(实际上只是取消引用当前写入 PDB 中的指针)。看起来你可以跳过整个源服务器部分(至少在理论上)并消除对调试时间故事的一些依赖。是否将源存储为压缩在很大程度上是正交的,但为了使现有调试器更容易实现,第一遍可能不会这样做。
由于 PDB-matching-binary 的故事已经很好了,将源放入 PDB 甚至比源服务器指针更好,因为指针可能会随着时间的推移而中断(源控制系统移动,或更改为不同的系统,或无论如何),但位于 PDB 中的实际源代码“永远”是好的。
这与“源服务器”支持有何不同?
(这是在 Tigran 的评论询问有什么好处之后通过编辑添加的)
应该与之比较的“基线”场景是今天使用“正常”源服务器实例的“正常”调试体验。在那种情况下,(AFAIK)调试引擎从 PDB (通过备用流)获取指针,然后使用已注册的源服务器尝试通过该指针获取源。由于给定的程序集通常将包含多个源文件,因此要么有一个包含基本位置的指针,要么在 PDB(或其他东西)中有多个指针,但这应该与本讨论正交。
对于需要隐藏/不可访问源的项目(大多数 Microsoft 产品,例如,包括 Windows、Office、Visual Studio 等),让 PDB 包含指针远优于包含实际源(即使它是加密)。如果没有必要的网络访问和权限,这样的指针是毫无意义的,因此这种方法意味着您可以将 PDB 发送给地球上的任何人,而不必担心他们能够访问您的源(最坏的情况下,他们可以一瞥您的源树是安排好的,我想)。
但是,有 2 组大型项目(特别是构建)不存在这种“隐藏源代码”的好处。
第一个是仅由无论如何都可以访问源代码的人使用的构建。在您自己的机器上完成且永远不会离开该机器的构建就是一个很好的例子,因为攻击者无论如何都需要从您的文件系统中读取文件以获取源代码,因此从一个文件 (.cs) 与另一个文件 (.cs) 读取。 pdb)在攻击难度/向量方面是一个相对较小的差异。同样,完成并推送到测试/登台环境的构建,在该环境中访问机器上 pdb 的人等于或可以“正常”访问源的人的子集。
第二个是(有些明显的)开源项目,其中项目的源代码已经对所有人开放,因此对任何人隐藏源代码没有任何好处。
请注意,这可以相对容易地扩展以包含加密形式的源(因为我们已经在讨论必须存储格式/编码数据),但是增加的复杂性会使这种情况可能不太有用而不仅仅是使用“普通”源服务器。
好处?
有了上面的描述,允许这样做的潜在好处列表包括(但不限于:)这些现在突然出现在我脑海中的东西:
- 无需处理设置源服务器支持。It Just Works (IJW),至少当/如果调试器知道查看 pdb 时。
- 与此同时,你仍然可以做一个“固定”的源服务器,它只是一个提取源并将其反馈给调用者的虚拟服务器。这样的配置可能对每个人都是相同的(例如,使用 localhost),仍然消除了当前实际配置源服务器的需要
- 构建不需要包含“源索引”
- 由于构建会读取源文件并写入 pdb 文件,因此我们只是修改写入 pdb 中的内容,而不会对进行网络调用或读取内存中没有的数据进行任何构建时性能影响。
- 在“原生”构建支持将源代码放入之前,它可能是一个简单的构建后步骤,可能首先通过 Sourcepack 项目的一个小分支实现,因为它已经完成了读取/修改 PDB 文件的工作:)
- 不依赖于拥有源代码控制系统的团队/项目
- 不依赖于被签入源代码控制系统的每个文件的特定版本(大多数人不会签入他们在 IDE 中执行的每个构建)
- 无需访问具有该文件的特定源代码控制系统
- 例如,在 DVCS 情况下,PDB 指针可能指向 git 或 mercurial 或其他任何“随机”实例,不一定是您有权访问的实例
- 用于将该版本跟踪回您有权访问的源代码控制服务器实例(如果它甚至存在)的源服务器工具尚不存在 AFAIK)
- 如果项目死亡(被删除)或移动,没问题
- 例如,如果项目从一个转移到另一个:自托管、sourceforge、github、bitbucket、codeplex、code.google.com 等。
- 如果您正在调试的机器没有(或不足)网络访问权限,则没有问题
- 例如,如果你在一个盒子里做一个“网络 KVM”来调试一个问题,但它要么没有网络,要么只能与断开的网络通信,这样它就无法访问你的源代码控制服务器)。
- 在极端情况下,能够从构建中恢复一些项目源。;)
注意:另一种方法是将源包含在实际程序集中(例如,作为资源),但 pdb 是更好的选择(很容易在没有 pdb 的情况下发布构建,如果源在 pdb 中,则没有正常的运行时性能命中因为程序集是相同的代码和相同的大小等)
如何实施?
从表面上看,这种支持似乎并不难添加,但我觉得这是因为我对所涉及的机制了解得不够多,而不是实际上是一件简单的事情来实施。:)
我的猜测是:
- 添加一个构建后步骤,该步骤将执行类似于 Sourcepack 的操作,但不会更改指针,而是将其替换为实际源代码。
- 根据源服务器需要做什么,它可能需要添加前缀,或者实际源将位于不同的备用数据流中,并且“指针”被更新为“source-in-pdb:ads-foo.cs” ' 管他呢。前缀或指针也可以包括源文件的存储方式(未压缩、gzip、bzip2 等,以及文件的编码)
- 实现一个“源服务器”,它实际上从有问题的 pdb 中提取源并将其返回。
- 不知道源服务器“API”是否有足够的信息来获取 PDB 的位置,更不用说它是否有权实际读取内容。
完整性检查?
随着上面的喋喋不休,问题真的是:
- 这种东西是不是已经存在了?(如果是这样,请提供指点!)
- 假设它还不存在,上述作为第一次实现是否有意义?是否存在上述跳过的陷阱或复杂性?
- 假设上述情况为“否”和“是”,是否存在一个有意义的现有项目(它接近或在其现有范围内)?