Technote (troubleshooting)
问题
在有限的个案中,用户可能会遇到 Domino 进程异常终止。有什么方法可以诊断这种类型的宕机?
诊断问题
在 W32 上怎么定义异常终止?
异常终止是指一个进程结束时没有清理干净它的资源,或者更准确地说,是在结束时没有调用标准的终止方法。在 Domino 中的定义是一个子进程没有报告任何错误就终止运行了,导致 Domino 服务器仍然认为它还在运行。在 32 位的 Windows 操作系统(W32)上,这可能是多种不同的潜在原因引起的。
1. 第三方的嵌入任务(或调用 Notes API 的独立应用程序)异常结束,而且没有调用 Notes API 的终止方法。
2. Domino 的嵌入任务 (第三方或者 Domino 核心任务)遇到严重的堆栈覆盖,造成进程的异常(无意识)结束,最终宕机。
3. Domino 任务抛出一个用户自定义的未经处理的异常。这个现象与前两个稍有不同,在它发生时,可能会显示一个对话框,内容为“process.exe encountered a c++ runtime exception(process.exe C++ 运行异常)”,该对话框仅有“确定”按钮,如果点击“确定”,任务就会终止。除了有对话框这一个主要区别外,任务异常终止行为同其它两种情况相同。
在 W32 平台上,如果一个子进程异常终止,父进程不会得到通知(UNIX 上是相同的)。区别在于在 W32 平台上,进程终止时没有任何提示(例如错误信息或对话框出现),只是简单的从进程列表中消失。在先前的 Domino 版本中,nSERVER 不知道子进程意外终止,这会导致 Domino 服务器显示该进程依然在运行但是不再出现在操作系统的进程列表中。
关键点:一个进程从操作系统进程列表中消失,不显示任何警告信息,Domino 认为该进程仍然在运行中。
进程异常终止看起来很像进程挂起,任务不响应输入也无法通过 Domino 控制台干净的关闭。然而这两种问题的根源是完全不同的。当试图解决挂起时,通过检查操作系统进程列表判断有问题的进程状态是否为运行中,对于断定问题是否属于进程异常终止是很重要的。
从 Notes/Domino 7 W32 版本开始, Domino 服务器进行定期检查(每分钟)以检测异常终止,以避免和挂起混淆。关于此新特性的更多信息,参阅文档 #1604207,《运行在W32上的Domino主控台报错“Process has terminated abnormally”》。
解决问题
有一些工具可以用于 W32 异常终止问题的诊断。他们的共同之处在于采用相同的模式 - 提前开启调试器,附加到有问题的进程,等待问题的出现。必须这么做的原因是不管采用 NSD, WinDbg 或者 MSVC,这些调试器的 JIT 能力都未被操作系统正确的使用。以下是三种可用的选项:
- NSD -monitor - 这是首选方式,因为可以直接利用 Domino 诊断。以“监控”模式启动 NSD ( 带-monitor 参数),这允许 NSD 预先附加于 Domino 问题进程,在进程异常退出前直接捕获发生致命错误时的状况。需要注意 NSD 会附加在所有 Domino 进程上。这是与使用 WinDbg 或 .NET 工具诊断的一个显著区别。
- WinDbg - WinDbg 是微软免费提供的调试器,帮助捕获调用堆栈信息。虽然使用的是不同的调试器,但概念是相同的;预先附加于有问题的 Domino 进程,等待致命错误发生。一旦捕捉到正确的文件,应该发送给 IBM 支持人员审阅。
- MSVC 或 .NET - 使用 Microsoft Visual Studio (MSVC) 或者 Microsoft .NET 是最后一个选择,主要原因是要求购买具有完全许可的 MSVC 或 .NET 。当然,如果你发现 WinDbg 或者 NSD 不能搜集足够的信息,有 Visual Studio 的许可证的话,也可以用 Visual Studio 搜集调用堆栈信息。一旦捕捉到正确的文件,应该发送给 IBM 支持人员审阅。
这三种选择中的任一个,如果 Domino 作为服务启动,需要以具有系统管理权限的管理员账号登录 Windows ,以使调试器可以正确的加载。
警告:谨慎使用上述调试器。在调试器附加到进程以后,关闭调试器可能导致进程过早结束。这并不能被认为是宕机,因为导致进程终止的因素已经超越了服务器控制范围。在调试器附加到进程后,要确保调试器能持续运行(除非到了预期停机时间)。在 Windows 2003 或者 Windows XP,可以把调试器从进程分离,然后再退出调试器。但是,在异常终止案例中,需要手动关闭 Domino 服务器,或者使用 nsd -kill 结束所有服务器相关进程。
使用 NSD -monitor 步骤
- 手动启动 Domino 服务器。如果以服务方式启动 Domino 会由于权限限制造成 NSD 收集遇到问题。
- 从命令行启动 “ NSD -monitor ”(从数据目录或者程序目录均可), NSD 会在启动每个 Domino 进程时将自身附加上去,然后返回 nsd 提示符。
- 一旦进程异常终止,NSD 会自动收集致命错误发生时的情况,收集调用堆栈信息(运行完整 NSD)。
- 一旦 NSD 完成调用堆栈收集,会返回 NSD 提示符 (“ nsd> ”), NSD 依然附加在所有 Domino 进程上。需要用“ kill ”命令结束所有进程,然后手动重启 Domino。
- 启动顺序不重要。可以先启动 Domino 然后执行 NSD -monitor 或者反之。关键是确保 NSD 在异常终止前附加在所有 Domino 进程上。在 Windows XP 或者 Windows 2003 系统, 可以使用 “detach ”命令将 NSD 从 Domino 进程分离,并在宕机前退出 NSD。
注意:为了避免 NSD 捕获非 Domino 进程,必须在位于 data 目录的 NSD.INI 文件中设置 “ monitor=notes ”。使用此功能需要升级到 NSD Build 2383 或更高。
使用 WinDbg 步骤
- 获取和安装 Windows 调试工具。
- 启动 Domino 服务器。
- 启动 Microsoft Debugger ( Start -> Debugging Tools for Windows -> WinDbg )。
- 在 WinDbg 中选择 File -> Attach to Process 。 会出现一个窗口显示所有正在运行的进程,选择有问题的进程(如“ nHTTP.exe ”)然后点击 OK 。
如果一切正确加载,可以看到类似下图的一个屏幕:
- 这时候调试器附加于 HTTP 进程。按 F5 启动调试器。在按 F5 之前访问 web 站点可以测试调试器是否附加成功。如果已附加,在按 F5 之前服务器不会有任何回应,按 F5 之后可以看到提示“ Debugger is running…”的屏幕:
- 现在调试器已附加于进程并开始监控直到进程终止。
- 在命令行窗口中点击,然后从 Edit 菜单选择 Write Window to Text File ,命名文件为 loadedmodules.txt。
- 一旦致命错误发生,选择 Calls 窗口。
- 在 Calls 窗口点 Addrs 按钮。
- 从 Edit 菜单选择 Write Window to Text File,保存到文件 callstack.txt , 获取 Calls 窗口屏幕截图。
- 如果你已按指示运行 HTTP 请求日志,此时可以把这些文件移到一个临时目录。
- 关闭调试器。
- 关闭 Domino 服务器。
- Domino 服务器应该不会干净的关闭,所以有必要手动终止其余的 Domino 进程或者运行 NSD -kill 。
- 发送 modules.txt,callstack.txt 和一些其他的相关文件(例如控制台日志,HTTP 线程日志等)。
使用 MSVC (版本 6)步骤
- 启动 Domino 服务器。
- 从图标启动 MSVC (启动用户界面:UI )。
- 选择 Build -> Start Debug -> Attach to Process ,打开一个对话框列出每个进程,选择有问题的进程,打开一个空的 call stack 对话框。
- 下次进程遇到致命错误时,call stack 对话框会显示致命线程的调用堆栈,拷贝并粘贴信息到一个文本文件。
- 选择 Debug->Modules 菜单项,会显示对话框列出每个模块和它的地址范围。点击 Modules 栏可以按模块名称排序列表。向下拉,显示所有 notes 模块并显示出问题的 DLL 的 base address,然后截屏。
- 选择 Debug -> Threads ,会显示对话框列出该进程的所有线程 ID。焦点显示的线程(故障线程)会显示一个星号,请记下这个线程 ID 。
使用 .NET (版本 7)步骤
- 启动 Domino 服务器。
- 从图标启动 .NET (启动 UI )。
- 选择 Tools -> Debug Processes ,会打开一个对话框列出每个进程。 在 “Available Process”选择有问题的进程并点击 “attach”。然后选择“Native”程序类型,会打开一个空的 “call stack”对话框。如果 Domino 以服务方式运行,可能需要选择“show system processes”。
- 下次进程遇到致命错误时,会显示一个对话框标示未处理异常。选择 “break”,会显示另一个对话框提示没有源代码,点击确定 。
- call stack 对话框会显示致命线程的调用堆栈,拷贝并粘贴信息到一个文本文件。
- 选择菜单项 Debug -> Windows -> Modules ,会打开一个对话框列出每个模块和它的地址范围。点击 Modules 栏可以按模块名称排序列表。拷贝和粘贴窗口内容到一个文本文件(不象 MSVC 6 ,你在 .NET 中可以直接拷贝和粘贴)。
- 选择菜单项 Debug -> Windows -> Threads ,会打开一个对话框列出该进程的所有线程。焦点显示的线程(故障线程)会显示一个黄色箭头,请记下这个线程 ID 。
APIDeveloper INI 设置
在版本 5 ,宕机事件中 Domino 会调用系统的即时(JIT)调试器(例如 Dr. Watson, QNC,MSDEV)。Domino 6 中已不再有这种情况。从 Domino 版本 6 开始,宕机后调用的故障恢复方法已被修改。
为了帮助 C/C++ API 开发人员,可以添加 notes.ini 变量 APIDeveloper=1 。当 notes.ini 添加了变量 APIDeveloper = 1 时,C API 开发人员可使用 JIT 捕获和调试宕机应用,和 Notes/Domino 版本 6 之前非常类似。如果不设置这个变量,将不允许系统 JIT 调试器调试 Domino 进程时通过例如 drwtsn32.exe -i 之类的命令调用 Dr. Watson。然而,这个 INI 参数不是诊断异常所需的,而是如果调试器提前附加在进程上,就不再调用 NSD