对 User breakpoint called from code at XXX 问题分析汇总

分析一,转自独奏的同名Bloghtml

      今天调试程序时在Debug版跳出这个错误,我程序根本没设置断点,而其好像说是个人堆有问题,而编译了个Release版本运行正常,后来google下,查到以下解释:编程

      说是调试状态下,操做系统用DebugWin32Heap来替代正常的heap分配内存空间。在这个堆上的任何操做debug的堆管理器会检查堆的数据完整性,若是它发现了一个错误,就会报告一个消息上来。当一个应用程序PageHeap机制被激活时,该应用程序的全部的堆分配被放到内存中,这样堆的边界就与虚拟内存的边界排在一块儿了。与堆相邻的虚拟内存页面被设置为NO_ACCESS。在该应用程序中对堆后面的空间的访问就会马上引发错误,这就能够在一个调试工具中被捕获。在释放堆时,过程与之相似。PageHeap修改释放的应用程序虚拟页面为NO_ACCESS,这样,若是应用程序试图读写该内存时就会发生访问错误。windows

      既然是Heap的问题我就查遍了全部源代码的new和delete操做的代码,感受没有任何问题,该分配的都分配了该释放的也都释放了,并且没有错误状况。后来没办法弄个极端的,我把程序的中全部delete 的语句所有注释,再运行程序果真没问题了- - 。我又继续查了下程序,感受仍是没问题。后来定位到一条delete, 只要有它程序就报错,没有就正常。这个delete位于一个移除服务的函数中,这里有必要说下个人程序结构app

      我将服务所有存入dll中,主要函数一个四个(其余的就不说了)
      createSrv 创建服务 removeSrv 移除服务 runSrv 运行服务 stopSrv 中止服务函数

      而这个delete正好位于removeSrv中,可是个人delete没有任何问题。后来没办法又google下,找到了一个和我遇到一样问题的人,引用原文以下:工具

      "重复释放致使的问题,User breakpoint called from code at 0x77f9193c ,以上缘由是因为释放了一个类的员,最后在做该类的析构时因为它的成员已经被释放致使出错(该成员被释放可是没有设 NULL)"this

      看完这句话后我马上去看个人类的析构函数(因为个人dll为了通用并非导出类而是导出函数,而我调用方编写了调用类来调用dll导出函数,到统一管理和封装的目的)。果真 - -|| :我怕万一个人调用没有" stopSrv 中止服务"和"removeSrv 移除服务" ,我在析构函数中又调用了这个两个函数,郁闷我从9点陆陆续续调试到如今 - - ......写此文章警示后人......
google

分析二转自除虫记之十二:费解的NTDLL断点编码

好久没有写东西了,此次是为了完善好久好久之前写的一个培训ppt(VC的使用与调试技巧),才想起来写点东西的。下面的文章参考了http://www.debuginfo.com/tips/userbpntdll.html,但不是翻译,偶英语太烂了。

咱们在调试程序的过程当中,有时会忽然的显示一个对话框,上面显示这样一条信息:
User breakpoint called from code at 0x77fa018c
或者是
Unhandled exception at 0x77f767cd (ntdll.dll) in myapp.exe: User breakpoint.
不过我遇到过的都是第一条信息,没有遇到过第二条信息。spa

怎么回事?咱们没有设置断点呀!为何会有一个用户断点?而且这个问题看起来并无那么严重,不在调试状态下,程序正常运行,即便在调试状态下咱们把这个对话框按了肯定后,再继续F5,好像什么事情也没有发生,程序仍然在正常运行!

隐患!千万不要忽视她!这个信息告诉咱们,程序中某个地方已经开始溃烂,若是你频繁的碰到这个对话框,就说明溃烂已经扩大了。

若是你够仔细,你会发如今你点了这个对话框的肯定按钮以后,会在Output窗口中发现多了一行信息:
HEAP[DebugInfo2.exe]: Heap block at 00030FD8 modified at 00031010 past requested size of 30

查看堆栈窗口,里面显示这样的信息:
NTDLL! 7c921230()
NTDLL! 7c97db9c()
NTDLL! 7c98cd11()
NTDLL! 7c980af8()
KERNEL32! 7c85e7af()
_CrtIsValidHeapPointer(const void * 0x00031000) line 1697
_free_dbg(void * 0x00031000, int 0x00000001) line 1044 + 9 bytes
operator delete(void * 0x00031000) line 49 + 16 bytes
WinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00151f15, int 0x00000001) line 13 + 15 bytes
WinMainCRTStartup() line 198 + 54 bytes
KERNEL32! 7c816d4f()

重现这样的现场很容易的,只需几行代码就能够了
char *p = new char[4];
lstrcpy(p, "this is a test");
delete p;

为何会有这样的信息消息框出现呢?这是由于若是咱们在调试状态下,操做系统用DebugWin32Heap来替代正常的heap分配内存空间。在这个堆上的任何操做,debug的堆管理器会检查堆的数据完整性,若是它发现了一个错误,就会报告一个消息上来。

那么,咱们怎么样才能知道形成错误的缘由呢?若是只是相似上面的演示代码,不用任何技巧都能发现而且定位的。可是,绝大多数状况下,Output窗口和CallStack窗口告诉咱们的信息都不能让咱们精肯定位。

用BoundsChecker作运行期的检查能够查到这个错误的缘由,而且我我的认为在程序发布以前,在BoundsChecker下完整的跑一遍,检查内存和资源是很是有必要的。可是每次都用BoundsChecker是比较麻烦的。

还有一个办法,可让这种定位更准确一点。那就是windows 2000 SP2以后提供的PageHeap机制。

下面的这段文字是别人写的,我抄的,:)
当一个应用程序的PageHeap机制被激活时,该应用程序的全部的堆分配被放到内存中,这样堆的 边界就与虚拟内存的边界排在一块儿了。与堆相邻的虚拟内存页面被设置为NO_ACCESS。在该应用程序中对堆后面的空间的访问就会马上引发错误,这就能够 在一个调试工具中被捕获。在释放堆时,过程与之相似。PageHeap修改释放的应用程序虚拟页面为NO_ACCESS,这样,若是应用程序试图读写该内 存时就会发生访问错误。若是为一个应用程序运行PageHeap特性,应用程序要比正常时运行得慢,而且须要更多的虚拟内存,由于每个堆的分配都须要两 个完整的虚拟内存页面。随着应用程序对堆的使用的增长,可能须要增长系统的虚拟内存的大小,不然会出现虚拟内存不够的错误信息。除非系统有至关大的虚拟内 存,不然建议不要同时运行两个以上的激活了PageHeap特性的应用程序。

让咱们的程序启用Full Page Heap机制,只需在注册表中
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\YourAppName.exe下增长以下设置:
GlobalFlag,字符串类型,值是x02200000
PageHeapFlags,字符串类型,值是0x3
VerifierFlags,DWORD类型,值是00000001
其中YourAppName.exe换成你的程序的名字,不要带路径。

若是嫌麻烦,你能够到http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx下载Debugging Tools for Windows,安装后,有一个gflags工具,使用下面的命令:
gflags –p /enable YourAppName.exe /full

这个工具帮你写好了上面的注册表项目。这个工具是一个GUI的程序,你能够双击而且设置相关调试入口。

若是设置了Full PageHeap模式,咱们在调试运行的时候,那么就会在弹出那个使人费解的断点对话框以前出现一个断言对话框,哈哈,这个对话框但是有一个Retry按钮的,点击以后进入CallStack,你能够看到此时发生了什么了。

若是你编写的是一个dll,也可使用这个机制的,不过注册表项下的值就要设置成以下:
GlobalFlag,字符串类型,值是0x02000000"
PageHeapTargetDlls,字符串类型,值是调试的dll名称,不带路径
VerifierFlags,DWORD类型,值是00000001
PageHeapFlags,字符串类型,0x403

或者使用命令行
gflags –p /enable YourAppName.exe /full /dlls YourDll.dll

分析三,转自jiahehao的同名Blog

在调试程序中遇到提示“user  breakpoint  called  from  code  at  0x......(地址)”时,这并不必定是因没用户设置了断点的关系,而是由于系统执行了一个硬编码断点操做(hard  coded  breakpoint  instruction)。    

例如在Windows  NT下当正被调试的应用程序得到焦点时,若是F12键按下,则Windows  NT调用一个相似于DebugBreak()函数的函数,这个被调用的函数执行一个hard  coded  breakpoint  instruction,因而调试器捕捉到这个操做产生的例外,因此中断并给出上述的提示(User  breakpoint  called  from  code  at  <address>)或者是给出Break  caused  by  hard  coded  breakpoint  instruction.,在这样的状况下,只要让调试继续进行便可(按下F5键)。     值得注意的是当程序涉及到全局性的共享内存(对象)时,如CShareFile,这些对象维护着一个全局性的内存块,若是由于程序的疏忽对仍然锁定的对象 (locked  object)进行释放内存的话,则在NT下也会出现User  breakpoint  called  from  code  at  <address>这样的提示。     此外,在低版本的Visual  C++调试器中,在很多地方(例如一些有关打印设备的操做)都用到GlobalLock,编程不当也会致使上述的提示出现,不过在win32中,不少这样的调用已经再也不必要了。     但愿这些信息能对您有所帮助,建议您检查一些是否是程序当中有一些涉及到释放全局性内存的操做。

相关文章
相关标签/搜索