源索引和符号服务器:简化调试指南






4.93/5 (16投票s)
索引和备份调试信息文件,以帮助调试来自野外的崩溃。
- 下载 BrokenApplication - 49.43 KB
- 下载 VSSourceIndexing_src - 880.26 KB
- 下载 VSSourceIndexingSetup - 1.05 MB
目录
- 第一部分
- 引言
- 什么是符号服务器
- 什么是源代码索引
- 如何设置符号服务器
- 如何索引您的源文件
- 脚本中发生了什么
- 第二部分
- Visual Studio 扩展
- 使用扩展
- 编写自己的索引器
- 第三部分
- 生成小型转储
- 链接
引言
前段时间,我需要设置一个带有源代码索引的符号服务器,当时关于这个主题的信息不多,所以我遇到了一些麻烦,无法让一切按我们想要的方式工作。不幸的是,现在仍然只有相同的信息,但自从微软发布 .NET Framework 的源代码并自动将他们的符号服务器添加到 Visual Studio 2010 后,似乎有更多人意识到了它。大多数使用 .NET Framework 的人现在都知道他们可以通过使用微软的符号服务器从微软获取符号文件和源代码来调试框架,但有多少人真正了解符号服务器是什么或者 Visual Studio 如何获取 .Net framework 的源代码呢?答案出奇的简单,与某些人认为这项技术已经存在了很长时间的信念相反,它可供您使用。所以本文旨在向您介绍符号服务器的奇妙之处,也许更重要的是,源代码索引。
什么是符号服务器?
符号服务器本质上只是一个使用文件系统的非常简单的数据库,用于存储不同版本的符号文件。WinDbg 和 Visual Studio 都可以通过 Windows 调试工具包中提供的 SymSrv DLL 使用这些数据库,为正在调试的应用程序加载匹配的符号。它实际上更像是一组方便的结构化文件夹和文件,而不是数据库,因此您可以随意复制和粘贴您的数据库到任何您喜欢的地方,符号服务器仍然会记录事务并保留已添加到或从数据库中删除的所有内容的记录,因此据我所知,您不能手动添加文件。关于符号服务器的一个重要注意事项是,它不支持同时进行多个事务,没有锁定机制可以防止其他人在事务进行时更新数据库,因此您可能需要注意一次只有一个人更新数据库。
使用符号服务器意味着每个人都可以轻松访问最新版本的符号,如果您需要调试旧版本的应用程序或库,则无需担心自己查找符号文件,它们将自动加载。
符号服务器本身可以非常方便,因为它允许您在调试时查看调用堆栈和其他有用的信息。但举例来说,您正在调试客户通过加载小型转储遇到的崩溃,并且您机器上的源代码不再与用于构建该特定版本应用程序的源代码匹配,您可能很难找到导致崩溃的原因;这就是源代码索引的作用。
什么是源代码索引?
源代码索引是将命令嵌入到符号文件中,当运行这些命令时,它们将从您的源代码控制系统或您可能已有的其他备份中提取正确版本的源代码。您的调试器能够在需要打开文件时运行这些命令,以便获取正确的源文件。因此,当您加载客户通过愤怒的电子邮件发送给您的小型转储时,您只需在 Visual Studio 中加载它,您将看到用于构建该版本的确切源代码以及遇到的错误(希望)有一个大箭头指向有问题的代码行。
如何设置符号服务器
符号服务器的先决条件是您有一些网络位置来存储数据库,即使您是唯一的用户,它也可以只是您硬盘上的一个文件夹。下一步是告诉您的调试器符号服务器在哪里,以便它在调试时可以检查符号文件。
在 Visual Studio 的较新版本中,您需要转到 工具->选项->调试->符号 并添加符号服务器的路径。如果您的符号服务器位于某个网络上,那么您还应该为 Visual Studio 指定一个本地缓存来复制符号,这样,下次您需要这些符号时,就不需要从网络下载它们了。
在 Visual Studio 的旧版本中,仅仅指定符号服务器的路径是不够的,您还需要告诉它这是一个符号服务器。您可以通过在符号服务器路径前加上 SRV*
来实现。
SRV*
只是 symsrv*symsrv.dll*
的简写,所以如果您看到完整版本,它的意思完全相同。SRV*
语法有几种变体
SRV*LocalCache*SymbolServerPath
SRV*LocalCache*NetworkCache*SymbolServerPath
因此,您可以为每个符号服务器指定不同的缓存位置。如果您是从异地位置获取符号,您还可以指定网络缓存,这样当其他用户需要符号文件时,他们只需直接从您自己的网络下载,而不是从地球的另一端下载。
如果您正在使用 WinDbg
,要添加符号服务器,您需要转到 文件->符号文件路径
并使用上面 SRV*
语法添加符号服务器。
您还可以为符号服务器设置一个环境变量,以便 Visual Studio 和 WinDbg(以及可能任何其他兼容的调试器)都可以在不显式在每个应用程序中设置的情况下知道您的服务器。
您需要创建的环境变量是 _NT_SYMBOL_PATH
,可以作为 用户
或 系统
变量创建。它使用 SRV*
语法用于每个服务器,如果您需要指定多个服务器,则需要用分号将每个服务器分隔开。
_NT_SYMBOL_PATH= SRV*c:\symbols*\\symbolserver;
SRV*c:\symbols*http://www.someotherplace.co.uk/symbols
此时,您的调试器应该在您的服务器中查找符号,此时您的服务器可能为空。要将符号添加到您的服务器,您需要使用 Windows 调试工具中提供的 SymStore.exe。向服务器添加一组符号的基本命令是
symstore add /f “c:\MyProject\Output\*.*” /s “\\MySymbolServer\Symbols” /t “MyProject”
/v “Build 1234” /c “Example Transaction”
关于每个命令的说明
add
告诉 symstore 我们正在添加文件。/f
我们要添加的文件(或在此例中是文件)的路径,如果您像我一样指定路径,它将搜索任何兼容的文件添加到服务器 - 这些文件包括 Visual Studio 生成的调试 pdb 文件以及二进制文件本身。请记住,如果您要从小型转储进行调试,那么您可能还需要将二进制文件添加到符号服务器。我不知道有任何方法可以指定要添加的多个路径,所以如果您只想要 .PDB 和 .DLL 文件(但没有可执行文件),您要么必须为每个文件运行单独的命令,要么将所有要备份的文件移动到同一个文件夹。/s
要添加文件的符号服务器路径。如果它只是一个空文件夹,那么它将添加必要的文件和文件夹,将您的空文件夹变成一个符号服务器。/t
事务的名称,这是一个必需的参数,通常您只需在此处放置项目名称或任何其他标识性string
。/v
您正在添加的文件的版本号。它不是必需的,只是为了您的方便,所以如果您需要手动查找特定的一组符号,您可以做到。/c
事务的注释,同样它不是必需的,只是为了日志文件和您的利益。
Symstore 还有相当多的其他参数,允许您以几种不同的方式设置符号服务器。我在此处不介绍它们,因为有一个很棒的 MSDN 页面解释了所有这些,您可以从文章底部的链接中获取。
如何索引您的符号文件
在将符号文件添加到服务器之前,您可以将命令嵌入其中,以从版本控制系统或其他任何地方提取当前源代码。Windows 调试工具中包含一些脚本,这些脚本将把来自各种版本控制系统的源代码索引到您的 PDB 文件中。我将快速举例说明如何使用这些脚本,然后我将详细介绍实际发生的情况,以便您在需要时可以编写自己的脚本。
要使用源代码索引脚本,您首先需要安装 Perl,因为这些实际上是 Perl 脚本。一旦您构建了项目并希望使用附加信息索引 PDB 文件,您的调试器将需要从版本控制系统提取源代码,您需要转到 Windows 调试工具安装中的 srcsrv 文件夹,并找到适用于您的版本控制的相关脚本,例如,如果您正在使用 Subversion,则需要运行 svnindex.cmd。您需要向脚本传递两个参数,以便它可以索引您的文件,它们是源代码路径的半冒号分隔列表,指向您项目的工作目录,以及 PDB 文件所在的文件夹的半冒号分隔列表。因此,您的命令将如下所示
svnindex.cmd /source=”c:\SharedModules;C:\MyVeryImportantProject”
/symbols=”c:\SharedModules\Release;c:\MyVeryImportantProject\Release”
然后,该脚本将为 PDB 文件中列出的每个文件插入从 SVN 提取它们的命令。在 SVN 的情况下,如果您需要使用特定的用户名和密码,您还可以传入 /user=”MyUserName” /pass=”MyPassword”
,请注意这些参数特定于 svn 脚本,其他脚本可能不总是接受用户名和密码,并且可能有自己的特定设置。与控制台中的大多数事物一样,您可以通过传递 -?
作为参数来查看每个脚本的帮助。
每个索引脚本都支持从环境变量以及名为 srcsrv.ini 的配置文件中加载变量。运行脚本时,它们将使用最本地的设置,因此命令行参数将覆盖 srcsrv.ini,而 srcsrv.ini 将覆盖环境变量。您还可以通过添加 /ini=”Path to ini file”
来指定在运行脚本时使用的特定配置文件。
脚本中发生了什么
基本上,索引脚本的目的是生成一个看起来像这样的数据块
SRCSRV: ini ------------------------------------------------
VERSION=1
INDEXVERSION=2
VERCTRL=Test
DATETIME=Mon, 04 October 2010
SRCSRV: variables ------------------------------------------
SRCSRVTRG=%targ%\%var4%\%var2%\%fnfile%(%var1%)
SRCSRVCMD=cmd /c copy “%var1%” "%SRCSRVTRG%"
SRCSRV: source files ---------------------------------------
D:\Documents\SKProjects\AlphaForms\AlphaForms\LayeredWindow.cs*11*_*AlphaForms\AlphaForms
*svn://192.168.1.5/AlphaForms/trunk/AlphaForms/LayeredWindow.cs
SRCSRV: end ------------------------------------------------
其中顶部有一些关于如何构建命令的信息,然后下面列出了每个文件的命令参数。所以如果命令是一个简单的复制,那么命令可能是
copy “%var1%” “%srcsrvtrg%
var1
指的是变量列表中的第一个项目,在本例中是 D:\Documents\SKProjects\AlphaForms\AlphaForms\LayeredWindow.cs。srcsrvtrg
是我们数据块头中的一个命名变量,它指定文件将被复制到哪里。您的调试器会在调用命令之前检查文件是否存在,所以如果您以前打开过此版本的文件,则无需重新运行可能非常慢的命令。srcsrvtrg
由其他几个命名变量组成。targ
是文件将被放入的本地缓存目录,fnfile
实际上是一个函数,它从括号中指定的路径(在本例中是 var1
,文件的路径)获取文件名。
您可能已经注意到,两个百分号之间的所有内容都被视为变量,并将被它们所代表的实际数据(如果可能)替换,此值替换操作也是递归的,因此就像 srcsrvtrg
变量一样,它将被其所代表的 string
替换,然后变量 targ
和 var1
将被填充。
因此,将这些数据插入到 PDB 文件中将完全无用,因为它只会将您系统上已有的文件复制到其他地方。一个更实际的例子是这样
SRCSRV: ini ------------------------------------------------
VERSION=1
INDEXVERSION=2
VERCTRL=Subversion
DATETIME=Mon, 04 October 2010
SRCSRV: variables ------------------------------------------
SRCSRVTRG=%targ%\%var4%\%var2%\%fnfile%(%var1%)
SRCSRVCMD=cmd /c "svn cat "%var5%@%var2% --non-interactive > "%SRCSRVTRG%"
SRCSRV: source files ---------------------------------------
D:\Documents\SKProjects\AlphaForms\AlphaForms\LayeredWindow.cs*11*_*AlphaForms\AlphaForms
*svn://192.168.1.5/AlphaForms/trunk/AlphaForms/LayeredWindow.cs
D:\Documents\SKProjects\AlphaForms\AlphaForms\AlphaForm_WndProc.cs*10*_
*AlphaForms\AlphaForms*svn://192.168.1.5/AlphaForms/trunk/AlphaForms/AlphaForm_WndProc.cs
D:\Documents\SKProjects\AlphaForms\AlphaForms\AlphaForm.cs*10*_
*AlphaForms\AlphaForms*svn://192.168.1.5/AlphaForms/trunk/AlphaForms/AlphaForm.cs
SRCSRV: end ------------------------------------------------
然后,实际的命令将由主部分中指定的变量构建,因此对于第一个文件,它将如下所示
cmd /c "svn cat " svn://192.168.1.5/AlphaForms/trunk/AlphaForms/LayeredWindow.cs@11
--non-interactive > "C:\Documents…”
然后将执行此命令,并希望将所需文件放入我们的目标目录,调试器将尝试在该目录中打开它。
如果您确实想编写自己的脚本或程序来索引 PDB 文件,那么您所需要做的就是生成一个像这样的数据块,然后使用 pdbstr.exe 将其插入到您的 PDB 文件中。自己索引源代码的基本步骤将类似于以下内容
- 收集工作目录中的文件列表
- 获取每个文件的提取命令参数列表
- 使用
srctool
获取 PDB 中引用的文件列表 - 将数据块的头写入某个临时文件
- 对于 PDB 中的每个文件,将参数添加到临时文件
- 使用
pdbstr
将数据插入 PDB
要获取 PDB 中引用的文件列表,请使用
srctool.exe “path to pdb file” –r
这将把 pdb 中的每个文件打印到新行。要将数据块添加到 PDB 文件,您需要使用
pdbstr –w –p:”path to pdb file” –s:srcsrv –i:”path to temp file”
-w
开关指定您正在写入文件,使用 -r
会将数据流(如果存在)打印到控制台。-s
给出我们要写入的数据流的名称,在此示例中为 srcsrv
。您实际上可以使用任何您喜欢的流名称将任何您想要的内容插入到 PDB 文件中,但 Visual Studio 将在 srcsrv
流中查找数据。-i
给出将插入到 PDB 文件中的输入文件的路径,在您的情况下,这将是您写入数据的文件。
第二部分
Visual Studio 扩展
碰巧,我实际上为 Visual Studio 编写了一个扩展,以便您可以更轻松地索引符号文件并将它们放到符号服务器上,该扩展适用于 VS2005 – VS2010,并将自动安装到您已安装的任何 Visual Studio 版本中。所有索引代码都用 C# 编写(尽管仍然使用 Windows 调试工具包提供的工具),因此不再需要安装 Perl。我还添加了一些标准脚本不具备的功能,尽管目前我只实现了 Subversion,所以整个扩展的可用性目前有些受限,但我已经使其尽可能容易地通过一个简单的接口来包含您自己的实现,我将很快介绍。
安装扩展后,您将在“工具->源代码索引”下看到一组新命令。这些选项仅在加载解决方案时可用,原因是设置是解决方案特定的,因此直接存储在 .sln 文件中,并且索引器会从当前加载的解决方案中收集一些信息。
您将与此扩展交互的主要界面是设置对话框,它为您提供了设置您希望索引的工作副本位置以及您希望存储在符号服务器中的符号文件(以及二进制文件)、符号服务器的位置、将用于索引文件的版本控制系统插件,最后是 工具->选项->调试->符号 页面的镜像,以便您更方便地访问 Visual Studio 将使用的符号服务器位置。
我添加的功能中,脚本没有覆盖的一项是选择将修改或未版本控制的文件备份到另一个位置,并让 Visual Studio 从那里而不是从源代码管理中拉取文件。这样,例如,如果您有一些本地修改的代码已进入日常构建但尚未签入(也许您为了构建而搞砸了一些东西),那么您仍然可以选择备份这些文件以供以后调试使用。所提供的 SVN 实现还比标准脚本多了一个优点,它会识别 svn:externals
属性并相应地索引这些文件夹,而脚本会忽略这些文件夹,因为它们不属于当前工作副本。
使用扩展
在索引和存储符号文件之前,您需要提供一些信息。第一步是转到 工具->源代码索引->设置->常规 并添加解决方案正在使用的所有工作目录位置。这并非严格必要,因为索引器会检查解决方案中的每个文件以确保其被索引,但依赖该备份功能可能会非常慢,因为每个文件的信息将单独收集,而不是一次性收集。在同一页面上,您至少需要指定要索引并存储在符号服务器上的 PDB 文件,您还可以添加任何要备份的二进制文件。
下一步是指定将使用的符号服务器,这对于索引文件是必需的,没有它索引器将无法运行。您可以在 服务器
页面上设置此选项,该页面还为您提供了将任何修改或未版本控制的文件备份到某个外部位置的选项,如果您想使用此选项,您需要指定文件将备份到的位置,这可以是任何本地或网络路径。
最后的必要条件是选择您要用于索引文件的版本控制插件,这可以在“版本控制”页面上完成。如果您的版本控制系统需要身份验证,您可以在此处指定,请注意此用户名和密码将以纯文本形式存储在 .sln 文件中。另一种选择是留空用户名和密码字段,然后当您索引解决方案时会要求您提供它们,再次警告这些凭据将(以纯文本形式)嵌入到要索引的 PDB 文件中。
设置完所有必填字段后,您可以通过单击 工具->源代码索引->索引解决方案 来索引和备份您的解决方案,然后会出现一个对话框,要求您提供有关符号服务器当前事务的信息。唯一必填的字段是 产品名称,它已有一个默认值,设置完您想要的内容后,只需单击确定,您的符号就会被备份。
编写自己的索引器
如果您想为不同的版本控制系统编写自己的索引器(目前只支持 Subversion),那么您只需实现 SourceIndexingInterface.dll 中提供的一个非常简单的接口,并创建一个 DLL,您可以将其放入源代码索引安装文件夹的 Modules 文件夹中。该接口如下所示
namespace SourceIndexing.Interface
{
public interface ISourceIndexer
{
IIndexerHost Host { set; }
string Name { get; }
void IndexFolder(string FolderPath, bool recursive);
FileStatus GetFileInfo(string FilePath, ref ICommandArgs Args);
void SetCredentials(string userName, string password);
string[] GetCommandArgs();
string GetExtractCommand();
}
public interface ICommandArgs
{
string this[string key] { get; set; }
}
public interface IIndexerHost
{
void AddFile(string filePath, FileStatus status, ICommandArgs Args);
ICommandArgs CreateCommandArgs();
void OutputInfo(string text);
}
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class CustomIndexer : Attribute
{
public string Name { get; set; }
public CustomIndexer(string Name)
{
this.Name = Name;
}
}
public enum FileStatus
{
Modified,
Unversioned,
CheckedIn
}
}
您需要实现的接口是 ISourceIndexer
- 您可以在单个 DLL 中拥有任意数量的索引器,只要每个索引器都标记有 CustomIndexer
属性;您提供给此属性的名称将是设置对话框中显示的名称。
IIndexerHost
是通用索引器,它将调用实际索引器的方法,并在首次加载 ISourceIndexer
的实现时设置。如果还需要凭据,它们将通过 SetCredentials
方法设置,并且您在与源代码控制系统交互时应使用它们。
这里最重要的函数是 IndexFolder
,从此函数,您需要从指定文件夹中的每个文件收集任何必要的信息,并为收集信息的每个文件调用您的 IIndexerHost
上的 AddFile
。如果设置了递归标志,那么您还需要收集所有子目录的信息。在实现这些函数时,请记住您永远不能提供太多文件的信息,因为只有最终 PDB 中列出的文件才会被包含。但是,如果您遗漏了解决方案中的任何文件,那么将为每个文件调用 GetFileInfo
,您不应该在此处调用 AddFile
,只需填写 ICommandArgs
结构(如果可以)并返回文件的状态,对于已签入并最新文件返回 CheckedIn
,对于在版本控制下但有本地更改的文件返回 Modified
,对于不在源代码控制下的文件返回 Unversioned
。如果您为文件状态指定 Unversioned
,则无需填写 ICommandArgs
结构。
在我继续之前,我将解释 GetCommandArgs
方法和 ICommandArgs
结构。还记得我之前讨论插入 PDB 文件的数据格式时,数据主部分中列出的每个变量都由 ICommandArgs
结构表示。为了使我的实现更具结构化,您需要命名您将插入到 PDB 文件中的每个变量,这些变量仅限于您从 GetCommandArgs
方法返回的变量。您需要添加的一个常见参数是源代码所在的存储库的路径或地址。SVN 插件的实现如下所示
public string[] GetCommandArgs()
{
//'Filepath' and 'Revision' are already included, as they are
//always necessary. Names are case sensitive.
string[] myArgs = {"Url"};
return myArgs;
}
如注释中所述,文件路径和修订版本始终包含在命令参数中,因为它们对于索引器正常运行是必需的。Filepath
参数必须始终包含您正在收集信息的文件完整路径,并且是文件系统上的路径,而不是存储库中的路径。您需要的任何其他参数都必须在此处指定,当您在索引器主机上调用 CreateCommandArgs
时,它将创建一个您可以使用的结构实例,但它只允许您使用您已经指定的参数。
要在 ICommandArgs
结构中设置变量,您只需像字典一样使用它(尽管您不能向其添加任何新键,也不能查询键是否存在,也不能删除键。我想这真是一个糟糕的例子)
ICommandArgs args = m_host.CreateCommandArgs();
Args[“Url”] = “repository path to file”;
Args[“Filepath”] = “path to file”;
Args[“Revision”] = “56”;
所有变量都是 string
类型,您不能直接将 int
或其他数据类型放入 ICommandArgs
结构中。
您需要实现的最后一个函数是 GetExtractCommand
,您将在其中返回一个命令,该命令与您指定的参数一起构建时,将从某个存储库或其他位置提取源代码并复制到本地硬盘。
对于 SVN,命令是这样的(不使用凭据时)
"svn cat \"%Url%@%Revision%\" --non-interactive > \"%SRCSRVTRG%\""
其中我们已将 Url
指定为存储库中文件的路径,并将文件的修订号放在 Revision
字段中。请记住,所有变量都必须用百分号括起来,并且您必须将文件复制到 SRCSRVTRG
,它将是输出文件的路径。
当然,您可以参考 SI_Subversion
项目中的源代码,以获取此接口的实现(该项目与 VSSourceIndexing
源代码在一起)。
第三部分
生成小型转储
如果只是根据一些模糊的错误报告来调试应用程序的最新版本,那么所有这些服务器和所有这些索引有什么意义呢?您可以使用 Windows 调试工具中 DbgHelp
DLL 中的 MiniDumpWriteDump
函数,让应用程序在未处理的异常发生时尝试写入自己的小型转储。这适用于本机 C++ 应用程序以及托管 .NET 应用程序。VS2010 为小型转储的混合模式调试提供了更好的支持,这意味着您可以轻松地使用托管应用程序的小型转储。不幸的是,我无法详细介绍调试托管小型转储,因为我在这方面经验不多。但我可以告诉您如何捕获未处理的异常并创建可用的小型转储。
接下来的部分适用于本机代码和托管代码,我们将用于捕获未处理异常的主要函数是 SetUnhandledExceptionFilter
,它接受一个指向如下所示函数的指针
LONG WINAPI UnhandledExceptionFilter(__in struct _EXCEPTION_POINTERS *ExceptionInfo);
//or in .Net
int UnhandledExceptionFilter(IntPtr exception_pointers);
当发现未处理的异常发生时,在搜索任何显式处理程序后,将调用此函数并传递有关异常的信息。我们需要这些信息来生成可用的小型转储。当使用 SetUnhandledExceptionFilter
设置处理程序时,它会返回一个指向先前设置的处理程序的指针,如果此指针不为 null
,则您应该在处理程序的末尾调用此函数。Windows 始终只保留一个处理程序,并通过期望每个处理程序调用上一个处理程序来构建处理程序链。
.NET Framework 为未处理的异常提供了自己的回调,但不幸的是,有时调用这些回调时。我们仍然没有 EXCEPTION_POINTERS
结构,通常是因为某些东西抛出了纯托管异常(例如通过 throw new NotImplementedException
)。这些回调的另一个缺点是它们不会用于任何访问冲突异常或任何其他潜在的致命错误。因此,我们需要确保调用我们的 UnhandledExceptionFilter
。在控制台应用程序的情况下,没有问题,但对于 winforms 应用程序,您需要将未处理的异常模式设置为此
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
这将绕过标准的 winforms 异常处理程序(即弹出对话框,让您选择尝试继续或退出应用程序的那个),并允许我们到达原生的 Win32 错误处理程序,该处理程序应该调用我们的异常处理程序并提供我们小型转储所需的 EXCEPTION_POINTERS
结构。
我想指出的是,此未处理异常过滤器将在自 Vista 以来出现的新的“应用程序崩溃……”对话框之后被调用,并且它永远不会被 Stack Overflow
调用(它不能,因为没有堆栈留下,并且函数需要在抛出异常的线程上下文中被调用),它也不会被某些堆损坏错误调用。在这两种情况下,应用程序都将关闭(当然,除非附加了调试器)。
实际生成转储很简单
//C# example
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
[DllImport("dbghelp.dll")]
public static extern bool MiniDumpWriteDump(IntPtr hProcess, uint ProcessId,
IntPtr hFile, MINIDUMP_TYPE DumpType,
IntPtr ExceptionParam, IntPtr UserStreamParam,
IntPtr CallbackParam);
FileStream fs = File.Create(@"D:\Minidump.dmp");
excInfo.ClientPointers = false;
excInfo.ThreadId = GetCurrentThreadId();
excInfo.ExceptionPointers = exception_pointers;
int size = Marshal.SizeOf(excInfo);
IntPtr exc = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(excInfo, exc, false);
MiniDumpWriteDump(Process.GetCurrentProcess().Handle, Process.GetCurrentProcess().Id,
fs.SafeFileHandle.DangerousGetHandle(), MINIDUMP_TYPE.MiniDumpNormal,
(excInfo.ExceptionPointers == IntPtr.Zero) ? IntPtr.Zero : exc, IntPtr.Zero, IntPtr.Zero);
fs.Close();
Marshal.FreeHGlobal(exc);
然后您可能希望启动一个新进程,向用户显示一个对话框,提供他们通过电子邮件发送或上传小型转储的选项,在不了解刚刚发生的错误的情况下,在应用程序中打开新对话框是不明智的,甚至写出小型转储也可能存在风险。
您可以通过下载 BrokenApplication
项目来查看在 .NET 中写入小型转储的示例。
链接
注意:您将需要 Visual Studio SDK 来修改 Visual Studio 扩展本身,但不需要它使用的插件。
参考资料,其中一些文章可能会扩展我在此处涵盖的主题
- 使用 Microsoft 符号服务器获取调试符号文件
- 如何使用符号服务器与 Visual Studio .NET 调试器
- 设置符号服务器
- 源服务器帮助您在 Visual Studio 2005 中彻底消灭 Bug
- Bugslayer:符号和崩溃转储
- 源服务器
- 使用 SymSrv
结语
嗯,我想这就是我对这个主题的所有了解。如果您能留下任何评论或建议,以便我进行改进,我将不胜感激。
如果能有其他版本控制系统的实现贡献,我也将不胜感激,因为我只接触过 SVN,并且没有安装任何其他版本控制系统。
历史
- 2010-10-04:文章上传