堆栈回溯

启用单层存储器堆内存管理器

从单级存储堆函数生成 C2M1211 或 C2M1212 消息时,代码将检查名为 QGPL/QC2M1211 或 QGPL/QC2M1212的 *DTAARA。 如果数据区存在,那么将转储程序堆栈。 如果数据区不存在,那么不会执行任何转储。

启用太字节空间堆内存管理器

从太字节空间堆函数生成 C2M1211 消息或 C2M1212 消息时,代码将检查名为 QGPL/QC2M1211 或 QGPL/QC2M1212的 *DTAARA。 如果数据区存在并且包含至少 50 个字符的数据,那么将从数据区检索 50 个字符的字符串。 如果数据区中的字符串与下列其中一个字符串匹配,那么将触发特殊行为。

_C_TS_dump_stack
_C_TS_dump_stack_vfy_heap
_C_TS_dump_stack_vfy_heap_wabort
_C_TS_dump_stack_vry_heap_wsleep

如果数据区不存在,那么不会执行转储或堆验证。

在以下情况下,行为缺省为 _C_TS_dump_stack 行为:
  • 数据区存在,但不包含字符数据。
  • 数据区长度小于 50 个字符。
  • 数据区不包含任何列出的字符串。

数据区中的字符串具有以下含义:

_C_TS_dump_stack

将执行转储堆栈的缺省行为。 未执行堆验证。

_C_TS_dump_stack_vfy_heap

转储堆栈后,将调用 _C_TS_malloc_debug() 函数以验证堆控制结构。 如果在堆控制结构中检测到任何损坏,那么将转储堆错误和所有堆控制信息。 转储的任何堆信息都包含在与堆栈转储相同的文件中。 如果未检测到堆损坏,那么不会转储堆信息。

执行验证后,控制将返回到生成 C2M1211 或 C2M1212 消息的原始程序,并继续执行。

_C_TS_dump_stack_vfy_heap_wabort

_C_TS_dump_stack_vfy_heap_wabort 具有与 _C_TS_dump_stack_vfy_heap相同的验证行为。

如果检测到堆损坏,那么将调用 abort() 函数以停止执行,而不是将控制权返回给原始程序。

_C_TS_dump_stack_vfy_heap_wsleep

_C_TS_dump_stack_vfy_heap_wsleep 具有与 _C_TS_dump_stack_vfy_heap相同的验证行为。

如果检测到堆损坏,那么将调用 sleep() 函数以无限期休眠,并暂停执行以允许调试应用程序,而不是将控制权返回给原始程序。 需要手动结束应用程序。

以下是如何创建数据区以指示每当生成 C2M1212 消息时调用 _C_TS_malloc_debug 以验证堆的示例:
CRTDTAARA DTAARA(QGPL/QC2M1212) TYPE(*CHAR) LEN(50)
     VALUE('_C_TS_dump_stack_vfy_heap')

分析

一旦数据区到位,就会创建名为 QPRINT 的假脱机文件,其中包含每个 C2M1211 消息或 C2M1212 消息的转储信息。 将为运行获取消息的作业的用户创建假脱机文件。 例如,如果获取 C2M1211 消息或 C2M1212 消息的作业是在用户标识 ABC123 下运行的服务器作业或批处理作业,那么将在用户标识 ABC123的输出队列中创建假脱机文件。 一旦获取了包含堆栈回溯的假脱机文件,就可以除去数据区,并分析回溯。

堆栈回溯可用于查找导致问题的代码。 以下是堆栈回溯示例:
PROGRAM NAME  PROGRAM LIB  MODULE NAME  MODULE LIB  INST#   PROCEDURE       STATEMENT#
QC2UTIL1      QSYS         QC2ALLOC     QBUILDSS1   000000  dump_stack__Fv  0000001019
QC2UTIL1      QSYS         QC2ALLOC     QBUILDSS1   000000  free            0000001128
QYPPRT370     QSYS         DLSCTODF37   QBUILDSS1   000000  __dl__FPv       0000000007
FSOSA         ABCSYS       OSAACTS      FSTESTOSA   000000  FS_FinalizeDoc  0000000110
ABCKRNL       ABCSYS       A2PDFUTILS   ABMOD_8     000000  PRT_EndDoc_Adb  0000000625
ABCKRNL       ABCSYS       A2PDFUTILS   ABMOD_8     000000  PRT_EndDoc      0000000003
ABCKRNL       ABCSYS       A2ENGINE     ABMOD_8     000000  ABCReport_Start 0000000087
ABCKRNL       ABCSYS       A2ENTRYPNT   ABMOD_8     000000  ABCReport_Run   0000000056
ABCKRNL       ABCSYS       A2ENTRYPNT   ABMOD_8     000000  ABCReport_Entry 0000000155
PRINTABC      ABCSYS       RUNBATCH     ABMOD_6     000000  main            0000000040
PRINTABC      ABCSYS       RUNBATCH     ABMOD_6     000000  _C_pep
QCMD          QSYS                                  000422

第一行是标题行,它显示程序名,程序库,模块名,模块库,指令号,过程名和语句号。

标题下的第一行始终是 dump_stack 过程-此过程正在生成 C2M1211 消息或 C2M1212 消息。 下一行是调用 dump_stack 过程的过程-几乎始终是 free 过程,但它可能是 realloc 或其他过程。 下一行是 __dl__FPv 过程,这是处理 C++ 删除运算符的过程。 对于 C++ 代码,此过程通常在堆栈中-对于 C 代码,它不是。

freedelete 函数是代表调用者释放内存的库函数。 它们对于确定内存问题的来源并不重要。

__dl__FPv 过程之后的行是使事情变得有趣的行。 在此示例中,过程称为 FS_FinalizeDoc ,此代码包含不正确的删除调用 (正在删除先前已删除/释放的对象)。 该应用程序的所有者需要在给定的语句号处查看该过程的源代码,以确定要删除/释放的内容。 在某些情况下,此对象是某种类型的本地对象,很容易确定问题。 在其他情况下,可以将对象作为参数传递到过程,并且需要检查该过程的调用者。 在这种情况下, PRT_EndDoc_Adb 过程是 FS_FinalizeDoc的调用者。

对于此示例,问题在 ABCSYS 库中的代码中。