2023年1月

如果用户模式应用程序已经在运行,调试器可以非侵入性地对其进行调试。对于非侵入性调试,您没有那么多的调试操作。但是,您可以最小化调试器对目标应用程序的干扰。如果目标应用程序已停止响应,则非侵入性调试非常有用。
在非侵入性调试中,调试器实际上并不附加到目标应用程序。调试器挂起目标的所有线程,并可以访问目标的内存、寄存器和其他此类信息。但是,调试器无法控制目标,因此g(Go)等命令不起作用。
如果尝试执行非侵入性调试期间不允许的命令,则会收到一条错误消息,指出“The debugger is not attached, so process execution cannot be monitored.(未附加调试器,因此无法监视进程执行。)”

选择要调试的进程

您可以通过进程ID(PID)或进程名指定目标应用程序。如果按名称指定应用程序,则应使用进程的完整名称,包括文件扩展名。如果两个进程具有相同的名称,则必须改用进程ID。

通过WinDbg命令行

要从WinDbg命令行无创地调试正在运行的进程,请使用以下语法指定-pv选项、-p选项和进程ID。

windbg -pv -p ProcessID

或者,要通过指定进程名来无创地调试正在运行的进程,请改用以下语法。

windbg -pv -pn ProcessName

通过WinDbg菜单

当WinDbg处于休眠模式时,通过单击“文件”菜单上的“附加到进程”或按F6键,可以无创地调试正在运行的进程。出现“附加到进程”对话框时,选中“非侵入性”复选框。然后,选择包含所需进程ID和名称的行。(也可以在“进程ID”框中输入进程ID。)最后,单击“确定”。

简介

本文介绍了在Windows中运行的VisualC++程序中处理异常和错误的标准技术。
异常(或严重错误或崩溃)通常意味着程序停止正常工作,需要停止执行。例如,由于程序访问无效的内存地址(如空指针)、无法分配内存缓冲区(内存不足)、C运行时库(CRT)检测到错误并请求程序终止等,可能会发生异常。
C++程序可以处理几种例外:SEH异常,通过操作系统的结构化异常处理机制产生,由C运行库产生的CRT错误,最后是信号。每种错误类型都需要安装异常处理程序函数,该函数将截获异常并执行一些错误恢复操作。
如果应用程序有多个执行线程,事情可能会更复杂。有些异常处理程序可用于整个进程,但有些仅用于当前线程。所以必须在每个线程中安装异常处理程序。
应用程序中的每个模块(EXE或DLL)都链接到CRT库(静态或动态)。异常处理技术在很大程度上依赖于CRT链接类型。
错误类型的多样性、在多线程程序中处理异常的差异,以及异常处理对CRT链接的依赖性,需要大量的工作来处理应用程序允许处理的所有异常。本文旨在帮助您更好地理解异常处理机制,并在C++应用程序中有效地使用异常处理。
本文附带了一个小型控制台演示应用程序ExceptionHandler。演示程序可以引发和捕获不同类型的异常,并生成一个崩溃小型转储文件,允许查看发生异常的代码行。

背景

不久前,我需要一种方法来拦截我的一个开源项目CrashRpt的异常,CrashRpt是一个用于Windows应用程序的崩溃报告库。CrashRpt库处理应用程序中发生的异常,收集有关错误的技术信息(如崩溃小型转储、错误日志、桌面截图),并提供用户通过Internet发送错误报告(图1)。

图1-CrashRpt库的错误报告窗口和错误报告详细信息对话框

也许您已经看到Windows错误报告窗口(图2)突然出现在您的桌面上,CrashRpt库也做了同样的事情,只是它将错误报告发送到您自己的web服务器,而不是Microsoft的服务器。

浏览MSDN时,我得到了SetUnhandledExceptionFilter()函数,该函数用于处理访问冲突。但很快我发现我的应用程序中的一些异常不知怎么地没有被处理,而Watson博士的窗口仍然出现,而不是崩溃的窗口。我又浏览了MSDN,发现许多其他CRT提供的函数可以用来处理CRT错误。下面是此类函数的一些示例:set_terminate(),_set_invalid_parameter_handler(),_set_purecall_handler()。然后我发现有些CRT处理程序只对当前线程有效,但有些处理程序对进程的所有线程都有效。继续我的研究,我发现开发人员必须理解许多细微差别才能有效地使用异常处理。我的研究结果如下。

关于例外的几句话

如您所知,异常或严重错误通常意味着程序停止正常工作,需要停止其执行。例如,可能由于以下原因发生异常:

  • 程序访问无效的内存地址(例如空指针)
  • 无限递归导致堆栈溢出
  • 大数据块被写入一个小缓冲区
  • C++类的纯虚方法称为C++类。
  • 无法分配内存缓冲区(内存不足)
  • 无效参数传递给C++系统函数
  • C运行时库检测错误并请求程序终止

有两种例外,它们有不同的性质:SEH异常(结构化异常处理、SEH)和类型化C++异常。操作系统提供结构化异常处理机制(这意味着所有Windows应用程序都可以引发和处理SEH异常)。SEH例外最初是为C语言设计的,但它们也可以用在C++中。SEH异常是使用_try{}_except(){}构造处理的。程序的main()函数由这样的构造保护,因此默认情况下,所有未处理的SEH异常都会被捕获并调用Dr.Watson。SEH异常是VisualC++编译器特有的。如果编写可移植代码,则应使用#ifdef/#endif保护结构化异常处理构造。

下面是一个代码示例:

int* p = NULL;   //pointer to NULL
__try
{
//Guarded code *p = 13; //causes an access violation exception }
__except(EXCEPTION_EXECUTE_HANDLER)
//Here is exception filter expression {//Here is exception handler//Terminate program ExitProcess(1);
}

在WinDBG的Command窗口中, 默认情况下, 你输入的命令很容易和该命令以及附近命令的输出混杂在一起. 在寻找之前命令的结果的时候, 眼睛看起来会很累.

WinDBG提供了一个选项, 通过这个选项, 你可以把命令输入与输出的颜色进行设置.

选项名:

  • Prompt level command window text
  • Prompt level command windows text background

 

结果如下:

 


怎样打印某函数调用关系

命令功能适用范围
uf /c /D 地址 打印当前函数对其他函数的调用 用户态/内核态
# 函数名 起始地址 l长度 打印在某段地址范围内代码对该函数的引用 内核态/用户态