2023年8月12日

您可以使用错误报告中的“Assember”选项卡做什么?它甚至有用吗?

一位客户联系我们,声称他的应用程序运行良好,直到他添加了EurekaLog。具体来说,他的应用程序开始引发E访问违规以下消息出现异常:
模块“Sample.exe”中地址03DB472F处的访问冲突。读取地址5653E4CC

客户很友好地分享了错误报告调用堆栈中提到的代码:
TContosoEventCollection函数。FindEvent(const AValue:TContosoEvent):整数;变量X: 整数;开始结果:=-1;如果未分配(FEvents),则退出;对于X:=0到FEvents。计数-1 do//-此处发生崩溃开始如果SameMethod(A值,F事件[X]),则开始结果:=X;中断;结束;结束;结束;
在哪里?F事件TList(TList)中的字段TContoso事件集合类。

提醒一下:TList(TList)类在RTL中的实现如下:
类型TList=类(TObject)私有的FList:TPointerList;FCount:整数;// ...受保护的函数Get(Index:Integer):指针;//返回FList[索引]// ...  公众property Items[Index:Integer]:指针读取Get-write Put;违约;属性计数:整数读取FCount写入SetCount;// ...结束;
因此,在尝试读取F计数的字段TList(TList)类-这可能意味着TList(TList)对象无效(换句话说:垃圾)。

嗯,错误报告不能是假阳性。那么,发生了什么?为什么在没有EurekaLog的情况下编译的同一个应用程序中没有访问冲突?

对于内存错误,您经常会看到这样的消息:
地址处的访问冲突。。。在模块“…”中。读取地址DEADBEEF
数据地址与DEADBEEF值匹配或接近的地方——这是EurekaLog在被删除的内存中填充的调试标记。除了DEADBEEF之外,您还可以遇到其他值:$CCCCCCCC、$FEEEFEEE、$FDFDFD、$FADEDEAD、$00009999。EurekaLog尝试为调试目的保留其中至少一个,并将使用保留成功的第一个。也可能有接近它们的值(例如调试标记+偏移量,例如DEADBE8F)。

然而,在这种特殊情况下:数据地址是5653E4CC,它与任何已知的调试器标记都不匹配。所以,乍一看能够是有效的数据值。

请注意,5653E4CC应该是TList(TList)F计数字段。现在,让我们看看EL错误报告中的“Assember”选项卡:
; 行=3297-偏移=23; ---------------------03DB472F 8B7808 MOV EDI,[EAX+8];<--例外情况
现在我们可以看到TList(TList)F计数字段的起始偏移为8TList(TList)对象。换句话说:它是类中的第二个字段。因此TList(TList)对象本身(换句话说:F事件客户的字段TContoso事件集合对象)是5653E4CC-8=5653E444。

为什么这很重要?

嗯,5653E4C4=1448338628,它是不是可以被16整除。事实上,到了8点,它甚至不可能实现。这很重要,因为Delphi上的分配粒度至少为16个字节。

结论:5653E4C4不能是任何TList(TList)对象。这意味着它完全是垃圾价值。(否则,我们可能会怀疑它的值为一些 TL列表对象-已删除的对象。)

摘要:F事件(TList(TList))客户的字段TContoso事件集合对象已被丢弃。

让我们进一步挖掘。再次查看“Assembler”选项卡:
; ContosoUnit(控制单元)。TContoso事件集合。FindEvent(行=1234-偏移=0); ---------------------------------------------------------03DB4718 55推EBP03DB4719 8BEC MOV EBP、ESP
这听起来像是TContoso事件集合。查找事件方法。我们对德尔菲方法了解多少?好吧,他们过去了自我作为隐式的第一个参数。我们对x86中传递的参数了解多少?第一个参数存储在EAX公司CPU寄存器。通过相同的“Assember”选项卡进一步确认:
03DB471F 8BF0 MOV ESI,EAX公司; 如果未分配(FEvents),则03DB4728 8B4604 MOV EAX,[ESI+4]03DB472B 85C0测试EAX,EAX
我们可以清楚地看到自我正在从复制EAX公司进入之内ESI公司,然后ESI公司正在用于读取FE事件字段(我们记得,它是垃圾)-偏移量为4(例如类中的第一个字段-也由客户的源代码确认)。

因此,当发生碰撞时自我必须存储在ESI公司CPU寄存器。让我们来看看它:00451C10。

你觉得它有什么问题吗?

嗯,虽然00451C10值可以被16整除,但客户的EXE加载到默认的00400000地址,该地址非常接近00451C10。事实上,客户的应用程序已在03DB472F地址崩溃(请参阅原始异常消息),鉴于客户的exe大小超过100'000'000字节(从“模块”选项卡中检查),该地址距离00400000已近。100'000'000=05F5E100,因此00400000+05F5E100=0635E100-比03DB472F大。因此,任何有效数据/对象的地址必须仅高于0635E100(或低于00400000)。

换句话说:00451C10是代码地址。它不可能是任何数据/TObject(目标)地址。

结论:客户的TContoso事件集合对象也被丢弃了,难怪我们从中读取了垃圾数据,所以它F事件/TList(TList)田地也变成了垃圾。客户很不幸,因为TContoso事件集合对象实际上是可读的,所以CPU可以加载(垃圾)F事件之后才坠毁。如果客户幸运的话,他会在访问时立即崩溃TContoso事件集合F事件字段。

摘要:客户应该进一步向上查找调用方代码:它正在调用查找事件无效对象值上的方法。它从哪里读取TContoso事件集合对象?不管是什么,现在都被扔掉了。

客户报告说,他能够根据此信息找到并解决问题。

至于为什么在没有EurekaLog的情况下编译的同一个应用程序中没有访问违规的问题——嗯,没有EurekaLog的应用程序中绝对没有内存检查!所以大多数与内存相关的错误都不会被注意到

第页。阅读更多这样的故事阅读客户的反馈