【问题标题】:Interpreting Access Violation Exceptions?解释访问冲突异常?
【发布时间】:2017-09-13 14:16:34
【问题描述】:

我们有一个围绕 VB/C++ 代码构建的自定义应用程序。 此代码将运行数天、数周、数月,而不会引发异常错误。

我正在尝试详细了解如何引发此错误,以及如何解释(如果可以)在引发异常时列出的错误。我已经搜索了一些信息并阅读了 Microsoft 提供的错误描述,但我仍然坚持解决在蓝月亮中发生的事情的任务。没有已知的与软件的交互会导致这种情况,并且似乎是随机发生的。

第一个异常是根本原因吗?一直到堆栈调用吗?谁能提供有关如何阅读这些代码的任何见解,以便我可以解释我实际需要查看的位置。

有关阅读异常或对其进行任何使用以及排除故障的任何信息或指导都会有所帮助。下面的测试是在引发事件时从 Windows 日志中复制的。

提前感谢您的帮助。

Application: Epic.exe Framework Version: v4.0.30319 Description: The process was terminated due to an unhandled exception. Exception Info: System.AccessViolationException [![enter image description here][1]][1]
at MemMap.ComBuf.IsCharAvailable(Int32) 
at HMI.frmPmacStat.RefreshTimer_Elapsed(System.Object, System.Timers.ElapsedEventArgs) 
at System.Timers.Timer.MyTimerCallback(System.Object) 
at System.Threading.TimerQueueTimer.CallCallbackInContext(System.Object) 
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, 
System.Threading.ContextCallback, System.Object, Boolean) 
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, 
System.Threading.ContextCallback, System.Object, Boolean) 
at System.Threading.TimerQueueTimer.CallCallback() 
at System.Threading.TimerQueueTimer.Fire() 
at System.Threading.TimerQueue.FireQueuedTimerCompletion(System.Object) 
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() 
at System.Threading.ThreadPoolWorkQueue.Dispatch() 
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

【问题讨论】:

  • 您有可以在 IDE 中打开的故障转储吗?它会在程序崩溃时向您显示程序的状态。
  • 我不确定...不是计算机程序员,只是有时不得不涉足的电气工程师。我已经附加了相关的 Windows 日志,并且该应用程序从现场客户机器上的 .exe 运行。
  • 一般来说,只要程序试图访问无效的内存地址,就会抛出这个错误。有许多类别的错误可能导致它(例如缓冲区溢出、释放后使用)。它们通常涉及某种未定义的行为,未定义的部分原因是编译器无法识别它们。
  • 也许这个帖子可以帮助你stackoverflow.com/questions/12228297/…'
  • 您需要有经验的程序员来解决这个问题。最好是参与该项目的人。不仅因为 AVE 是一个非常严重的事故,而且这看起来也是一个线程错误。

标签: c++ vb.net unhandled dep


【解决方案1】:

由于执行 throw 表达式,c++ 运行时环境会抛出异常,还有其他类型的错误是由操作系统或硬件捕获您的指令引起的。对内存的无效访问通常不会由 c++ 中的代码抛出,而是评估试图访问无效地址处内存的表达式的副作用,导致操作系统向进程发出信号,通常会杀死它。因为它在 C++ 之外,它往往是特定于平台的,但典型的错误是:

  • 读取空指针
  • 使用指向已删除对象的指针
  • 超出数组的有效元素范围
  • 在 STL 容器中使用无效的迭代器

一般来说,您可以在运行时测试 null 和数组边界,以便在问题发生之前检测到问题。使用悬空指针更难追踪,因为从删除到误用该指针之间的时间可能很长,而且如果没有内存调试器(例如 valgrind),很难找到它发生的原因。使用智能指针而不是原始指针可以帮助缓解内存管理不善的问题,并有助于避免此类错误。

无效的迭代器是一般悬空指针问题的子集,但也很常见,值得一提。了解您的容器以及哪些操作使它们无效至关重要,并且一些实现可以在“调试模式”下编译,这有助于检测无效迭代器的使用。

【讨论】:

    【解决方案2】:

    正如其他人所指出的,如果不深入研究代码和运行测试(自动或手动),这种类型的错误很难识别。您可以拉出并仍然复制它的系统组件越多越好。分而治之是你的朋友。

    除此之外,这一切都取决于解决此问题对您的重要性以及您愿意付出多少努力。至少有三类工具可以帮助解决此类间歇性问题:

    1. 在应用程序运行时跟踪潜在错误的应用程序监视器。这些往往会显着减慢您的程序(减慢 10 倍或更多)。示例包括:
      1. 微软的Application Verifier
      2. 开源跨平台Dr. Memory
      3. 谷歌的Crashpad。与前两个程序不同,这个程序需要检测您的代码。它也(据称 - 没有尝试过)更容易与 Backtrace's commercial integration for analyzing Crashpad output 等助手一起使用
      4. Google 的Sanitizers - 免费,有些内置于 gcc 和 clang。还有一个Windows port of Address Sanitizer,但粗略一看,可能有点二等公民。
      5. 如果你可以在 Linux 上运行和重现它,你可以使用valgrindrr(参见this CppCast ep)是gdb 的免费扩展,可以记录和重放您的程序,以便您可以记录崩溃的调试会话,然后单步执行以查看问题所在;和/或 UndoDB 和 Undo 软件的朋友,这是一个更复杂的商业产品,如 rr。
    2. 代码的静态分析。这是一组用于查找代码中常见错误的工具。它通常具有较低的信噪比,因此如果您在现有的大型项目上运行它(最好从一开始就使用这些东西开始一个项目),那么有很多小事情需要挖掘。也就是说,许多警告是无价的。例子:
      1. 大多数编译器都内置了此功能的子集。如果您使用的是 Visual Studio,请将 /W4 /WX 添加到 C++ 代码的编译标志中以强制发出最大警告,然后修复所有警告。对于 gcc 和 clang,添加 '-Wall -Wpedantic -Werror` 以强制不发出警告。
      2. PVS-Studio(商业)
      3. PC-Lint(商业)
    3. 如果您可以检测代码以编写日志消息,Debugview++ 之类的内容可能会有所帮助。

    如果您正在进行多线程处理,事情会变得更加困难,看起来您确实这样做了,因为不确定性变得更难跟踪,引入了新的可能错误类别,并且上述一些工具不会运行良好(例如,我认为 rr 只是单线程的)。除了像 Visual Studio 这样的完整 IDE,您还需要使用 Intel 的 Inspector(以前的 Intel Thread Checker),或者在 Linux、Valgrind 的 HelgrindDRD 和 ThreadSanitizer(在上面的消毒剂中,但也仅限 Linux AFAIK)。但希望这份清单能给你一个开始的地方。

    【讨论】:

    • 在实践中我无法运行示例 1 的大部分内容。这些 c++ 代码中有许多正在与价值超过数十万美元的现实世界硬件设备通信。如果幸运的话,我可能会获得几天甚至一周的访问权限,但错误通常会持续数月(如 6 个月或更常见)而不会发生。我不确定这些诊断工具是否会在更短的时间内将其清除?这些故障日志中是否有任何指标可以具体查看?或者问题出在这些列出的任何位置?
    • 异常细节在这种情况下并不是特别有用,因为任性的指针或线程错误可能会破坏远离程序当时正在执行的其他内存部分。由于像您这样的问题,我多次看到程序在一些看似无关的地方崩溃。在您描述的情况下,您可以做的最好的事情可能是检测代码以记录程序状态(工具 3),然后比较好运行和坏运行。这不会快速或准确,但它可能就是你所拥有的。
    • 真的,你和你的老板需要决定修复的重要性。这不太可能是一个简单的修复,所以计算成本。是否有其他解决方法 - 例如退出时自动重新启动进程——这可以接受吗?如果没有,需要什么样的投资才能解决?理论上,硬件可以通过依赖注入在测试中被欺骗。您要克服的最大问题是不确定性和间歇性。
    • 我认为应用程序中唯一的线程是 VS 生成的用于执行计时器的线程。许多表单由计时器更新以动态显示机器状态的差异,例如在它提到崩溃的表单上,有几个或更多计时器为不同的更新循环运行。与您所说的类似,它在崩溃中列出的形式不是系统退出时的形式。我确实验证了计时器已停止并以表格形式处理,因此可能很难像您所说的那样跟踪。
    • 这是线程方面的好消息。因此,在我看来,您的非排他性选项是:(1)在具有欺骗性硬件等的测试环境中脱机运行它以尝试重现,(2)检测代码以尝试捕获有关问题所在的更多详细信息以及在生产环境中时,和/或 (3) 对代码进行静态分析以查看是否出现任何真正的错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-01
    • 2012-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多