Use Pseudoregisters to debug in MSVC++

本文介绍如何利用伪寄存器提高调试效率,重点讲解@ERR和@TIB的使用方法,帮助开发者快速定位错误。

Introduction

Let's start with the reason why I wrote this article. One day, a colleague asked me to help him debug a problem he had. So I was watching him stepping in his code, when I noticed the following line:

int test = GetLastError();

He did this, because he wanted to know the error code, if the previous function failed. He was adding this line every time he wanted to know the error code. I advised him to remove all those lines and use the @ERR pseudoregister in his watch window. He didn't know what it was and asking around in the office, a lot of other people didn't. So I came up with this article for people who have never heard of pseudoregisters.

What is a pseudoregister anyway?

A pseudoregister is not an actual hardware register, but is displayed as though it were a hardware register. With a pseudoregister, you can see and use certain values (error codes, thread information block...) in the debugger.

Let's have a look at the @ERR pseudoregister. Fire up your debugger with your favourite home-written application. Put a breakpoint in your code so that the debugger will break execution. Open the watch window if it isn't already (do this by right clicking on some empty toolbar space, and select "Watch" from this list). Add @ERR in this watch window. You should see 0 in the Value column. Now step through your code, and watch this value. It will always show the GetLastError() number for the current thread. So if something goes wrong in your code, this value will change.

If you want to test this, but your code doesn't have any errors, I advise to put some in (but don't forget to remove them afterwards). You can insert something like this:

FILE *fp = fopen("c://a_file_that_does_not_exist.txt", "r");

If you step over this line, you'll see that the @ERR value changed to 2. Go to Tools->Error Lookup to see what this error value means ("The system cannot find the file specified" if you were wondering). Lazy bums like me, and smart lads / lasses like you can change the @ERR pseudoregister to @ERR,hr . Doing this will change the value of the pseudoregister to the error string. Now you even don't have to lookup the error. I leave the @ERR,hr in the watch window all the time.

Conditional Expressions

Pseudoregisters can also be used in conditional expressions. To try this out, put following lines after the fopen:

if (fp)
{ 
    fclose(fp); 
}

Put a breakpoint on the if (fp) line. Go to Edit->Breakpoints (or press Alt-F9). Select the breakpoint you just inserted and press the "Condition" button. Here, you can enter the @ERR==2 condition. Now start the debugger. The debugger will break on this breakpoint if fopen() failed because it couldn't find the file. If the file does exist, the debugger won't break, even if it encountered another error (say error 4: could not open the file). Try this out by running the code (not stepping) after creating, and deleting the "a_file_that_does_not_exist.txt" file on c:/.

Just for the very curious (and otherwise totally irrelevant to this article) : what does @ERR do? How does it get the error number? As it turns out, @ERR does exactly the same thing as GetLastError() does. These functions have a whopping 3 lines of assembly code:

mov eax,fs:[00000018h] 
mov eax,dword ptr [eax+34h] 
ret

So @ERR grabs the DWORD at offset 0x34 in the thread environment block pointed to by fs:[18h].

The @TIB pseudoregister

The @ERR pseudoregister is not the only one that exists. Another important pseudoregister is @TIB. This is the thread information block for the current thread and is extremely helpful in multi-threaded debugging. If you place a breakpoint in a function that is called by multiple threads, the debugger will break execution every time no matter which thread passes the breakpoint. Even if you're stepping through your code, the debugger can jump to the breakpoint if another thread called the function. To solve this, you'll need to do the following. If execution breaks in the thread you want, add @TIB in the watch window. You will see some value like "0x7ffa6000" or "2147115008" in regular display. Go to the breakpoint menu (Alt-F9) and select the breakpoint. You can now add the @TIB==0x7ffa6000 condition filter. Doing this, the debugger will only break execution for this thread. All other threads using the same function will not result in a break.

This doesn't work in Windows 98 though. For Windows 98, you'll need to look at the Intel CPU FS register, which is unique for each thread. You can use the expression @FS==value

Complete list of pseudoregisters

Pseudoregister

Description

@ERR

Last error value; the same value returned by the GetLastError() API function

@TIB

Thread information block for the current thread; necessary because the debugger doesn't handle the "FS:0" format

@CLK

Undocumented clock register; usable only in the Watch window

@EAX, @EBX, @ECX, @EDX, @ESI, @EDI, @EIP, @ESP, @EBP, @EFL

Intel CPU registers

@CS, @DS, @ES, @SS, @FS, @GS

Intel CPU segment registers

@ST0, @ST1, @ST2, @ST3, @ST4, @ST5, @ST6, @ST7

Intel CPU floating-point registers

[Table from "Debugging Applications" by John Robbins] 

内容概要:本文详细介绍了一个基于C++的养老院管理系统的设计与实现,旨在应对人口老龄化带来的管理挑战。系统通过整合住户档案、健康监测、护理计划、任务调度等核心功能,构建了从数据采集、清洗、AI风险预测到服务调度与可视化的完整技术架构。采用C++高性能服务端结合消息队列、规则引擎和机器学习模型,实现了健康状态实时监控、智能任务分配、异常告警推送等功能,并解决了多源数据整合、权限安全、老旧硬件兼容等实际问题。系统支持模块化扩展与流程自定义,提升了养老服务效率、医护协同水平和住户安全保障,同时为运营决策提供数据支持。文中还提供了关键模块的代码示例,如健康指数算法、任务调度器和日志记录组件。; 适合人群:具备C++编程基础,从事软件开发或系统设计工作1-3年的研发人员,尤其是关注智慧养老、医疗信息系统开发的技术人员。; 使用场景及目标:①学习如何在真实项目中应用C++构建高性能、可扩展的管理系统;②掌握多源数据整合、实时健康监控、任务调度与权限控制等复杂业务的技术实现方案;③了解AI模型在养老场景中的落地方式及系统架构设计思路。; 阅读建议:此资源不仅包含系统架构与模型描述,还附有核心代码片段,建议结合整体设计逻辑深入理解各模块之间的协同机制,并可通过重构或扩展代码来加深对系统工程实践的掌握。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值