在AIX版本5.3上使用MALLOCDEBUG隔离和解决内存泄漏

在对应用程序进行编程时,动态内存分配是一个福音。 它有助于在程序运行时分配所需的内存,而不是在进程开始时分配。 但是,有效管理此内存非常重要。 在大型,复杂的应用程序中,内存泄漏是一个非常普遍的问题。 当不再需要或不可访问但仍未释放先前分配的内存时,就会发生内存泄漏,从而导致该进程缺少总可用内存。 虽然良好的编程习惯可以确保将泄漏降到最低,但我们的经验是,当相同的内存块被众多功能处理时,内存泄漏往往会发生。 遇到错误路径时尤其如此。

本文概述了解决内存泄漏时可以使用的方法,并讨论了如何利用AIX®随附的MALLOCDEBUG工具。

为什么内存泄漏是很难解决的问题

您可以根据应用程序遇到的条件在其生命周期的不同时间释放一块内存。 必须对应用程序进行设计,以便它知道何时准备释放一块内存,并尽早执行该操作,而不是稍后执行。 根据控制流,可以在代码中的不同点释放内存。 覆盖所有此类控制流,而不仅仅是命中率最高的代码流路径,这一点非常重要。

同时,在准备释放内存之前释放内存可能是致命的。 因此,对应用程序内的控制流有很好的了解对于理解准备释放块的位置非常有用。

检测内存泄漏

在以下情况下,某些症状表明可能存在泄漏:

  • Malloc返回errno设置为ENOMEM
  • 发生核心转储,并且堆栈中的功能之一来自malloc子系统。
  • 与正常运行时相比,核心时的进程大小很大。
  • 进程大小(可以通过ps命令看到)Swift增长。

如果有迹象表明您有内存泄漏,则最好使用内存监视工具。 有一些第三方工具,例如Zerofault和IBMRational®Purify®,但是本文讨论了AIX随附的MALLOCDEBUG工具。

使用MALLOCDEBUG报告内存泄漏

MALLOCDEBUG是易于使用的malloc子系统监视工具。 您可以使用两个环境变量MALLOCDEBUG和MALLOCTYPE轻松地为特定应用程序的malloc子系统启用调试功能。 您需要将这些变量导出到将启动应用程序的外壳程序中。 错误报告将写入AIX版本5.3中的stderr。

合金型

MALLOCTYPE变量确定应使用哪个malloc子系统,以及是否启用调试。 下表1显示了可以设置MALLOCTYPE的各种值。 请参阅相关信息的链接,在“开发和移植的C和C ++在AIX上的应用程序。” IBM红皮书。

表1. MALLOCTYPE变量
MALLOCTYPE = 内存分配器 IBM红皮书“在AIX上开发和移植C和C ++应用程序”中的详细解释
3.1 3.1内存分配器 部分4.2.1,3.1内存分配器
空值 默认内存分配器 部分4.2.2,默认的内存分配器
水桶 具有malloc存储桶扩展名的默认内存分配器 部分4.2.3,带有malloc buckets扩展名的默认内存分配器
调试 调试内存分配器 部分4.2.4,调试内存分配器
用户:archive_name 用户定义的内存分配器 部分4.2.5,用户定义的malloc替换

您应该知道,MALLOCDEBUG假定使用默认分配器。

MALLOCDEBUG

MALLOCDEBUG变量可以从下表2中的列表中获取多个逗号分隔的值。

表2. MALLOCDEBUG变量
MALLOCDEBUG = 简要说明
对齐:n n是内存对齐的字节数。 请参阅相关主题以获取更多信息。
postfree_checking postfree_checking使MALLOCDEBUG报告尝试访问释放的内存,然后中止程序的尝试。
validate_ptrs 如果将以前未由malloc分配的指针传递给free()调用,则会引发错误消息,并且程序将中止。
overlay_signal_handling 当MALLOCDEBUG检测到内存错误时,它将使用SIGSEGV或SIGABRT强制程序进入内核。 设置override_signal_handling可以确保程序终止并运行,即使这些信号已由应用程序处理。
allow_overreading allow_overreading会忽略过度读取。
report_allocations report_allocations报告所有在应用程序退出时未释放的内存分配。
record_allocations record_allocations报告所有内存分配。
继续 继续会报告错误,但不会终止程序。 这是AIX 5.3版中可用的有用功能。
stack_depth:n stack_depth:n将错误堆栈的深度报告为n ,而不是默认值6。

在这些选项中, report_allocations选项与使用最相关,因为它为您提供了在应用程序中检测到的泄漏的列表。

在本节中,您将研究一个示例和一些示例代码清单,以说明如何使用MALLOCDEBUG。 此示例的源代码可在“ 下载”部分中找到。

清单1. MALLOCDEBUG和MALLOCTYPE设置导出到外壳
/home/user> export MALLOCTYPE=debug /home/user> export
                MALLOCDEBUG=report_allocations
清单2.程序的输出
/home/user> ./stud
                2>mal_dbg_stud Student Name: Manish Subjects # Maths English Student Name: Vaarun
                Subjects # Chemistry Physics Student Name: Sandeep Subjects # Biology Maths ......
                ...... ...... ...... Student Name: Govind Subjects # Physics Biology Student Name:
                Manish Subjects # Maths English Current allocation report: Allocation #0: 0x2000EFE8
                Allocation size: 0x14 Allocation traceback: 0xD0371F64 malloc 0xD0322194 init_malloc
                0xD0323224 malloc 0x10000BE4 initialize_leaky Allocation #1: 0x20010FF0 Allocation
                size: 0xA Allocation traceback: 0xD0371F64 malloc 0x10000C08 initialize_leaky
                0x10000DD4 main 0x100001B4 __start Allocation #2: 0x20012FF0 Allocation size: 0xA
                Allocation traceback: 0xD0371F64 malloc 0x10000C08 initialize_leaky 0x10000DD4 main
                0x100001B4 __start ...... ...... ...... ...... Allocation #17: 0x20030FF0 Allocation
                size: 0x10 Allocation traceback: 0xD0371F64 malloc 0x100008F4 getnode 0x10000B08
                insert 0x10000DDC main Allocation #18: 0x20032FF8 Allocation size: 0x8 Allocation
                traceback: 0xD0371F64 malloc 0x10000894 allocate_for_dbl_ptr 0x10000910 getnode
                0x10000B08 insert Allocation #19: 0x20034FE8 Allocation size: 0x14 Allocation
                traceback: 0xD0371F64 malloc 0x10000A5C allocate_name 0x10000B18 insert 0x10000DDC
                main Allocation #22: 0x2003AFF0 Allocation size: 0x10 Allocation traceback:
                0xD0371F64 malloc 0x100008F4 getnode 0x10000B3C insert 0x10000DDC main ...... ......
                ...... ......

MALLOCDEBUG的输出格式不是非常用户友好。 它仅打印每个未释放的已分配块的调用malloc的函数堆栈。 它还返回该块的大小以及malloc返回的指针的值。

为了使上面的输出更易于理解,我们编写了一个示例脚本。 (请参见下载部分。请注意,这是一个示例脚本,旨在与示例程序类似地用于输出。)下面的清单3显示了在上述堆栈上运行脚本后的输出。

清单3.通过脚本解析MALLOCDEBUG的输出
/home/user>
                ./format_mallocdebug_op.ksh Usage : ./stack.ksh stackfile /home/katiyar/DW>
                ./format_mallocdebug_op.ksh ./mal_dbg_stud | tee ./mal_dbg_stud_formatted Parsing
                output from debug malloc ... Analyzed 50 stacks ... main insert allocate_subjects
                malloc ################################ 220 bytes leaked in 22 Blocks
                ################################ main insert allocate_name malloc
                ################################ 220 bytes leaked in 11 Blocks
                ################################ __start main initialize_leaky malloc
                ################################ 190 bytes leaked in 16 Blocks
                ################################ main insert getnode malloc
                ################################ 176 bytes leaked in 11 Blocks
                ################################ insert getnode allocate_for_dbl_ptr malloc
                ################################ 88 bytes leaked in 11 Blocks
                ################################ initialize_leaky malloc init_malloc malloc
                ################################ 20 bytes leaked in 1 Blocks
                ################################

调查应从最大的泄漏开始。 上面的第一个堆栈显示了main调用insert ,后者调用了allocate_subjectsallocate_subjects通过调用malloc分配一个内存块,并且永远不会释放该块。 脚本输出提到22个单独的分配已累计泄漏了220个字节。 这意味着您应该查看代码中我们在allocate_subjects函数中分配了10个字节数据的位置,并检查其所有实例是否都已释放。 对程序进行仔细的分析后,您会找到应该释放有关内存块的位置。

对报告中的每个堆栈都进行了类似的分析,并对其进行了修复。 在程序中,如果在运行程序之前将FREE_BLOCKS=1环境变量导出到外壳程序,则该修复程序将生效。

清单4.释放分配的块后的输出
/home/katiyar/DW> ./stud
                Student Name: Manish Subjects # Maths English Student Name: Vaarun Subjects #
                Chemistry Physics Student Name: Sandeep Subjects # Biology Maths ...... ......
                ...... ...... Student Name: Govind Subjects # Physics Biology Student Name: Manish
                Subjects # Maths English Current allocation report: Total allocations: 0.

结论

在客户计算机上维修软件时,已经注意到MALLOCDEBUG是少数可用的malloc子系统监视工具之一。 为了避免在机器上获取服务时要求客户提供MALLOCDEBUG日志时出现大量malloc错误,在将应用程序交付给客户之前使用MALLOCDEBUG测试应用程序可能非常有益。 我们希望本文为必须处理客户计算机上的内存泄漏的开发人员和服务工程师提供了有用的介绍。


翻译自: https://www.ibm.com/developerworks/aix/library/au-mallocdebug.html

Introduction The usual implementation of malloc and free are unforgiving to errors in their callers' code, including cases where the programmer overflows an array, forgets to free memory, or frees a memory block twice. This often does not affect the program immediately, waiting until the corrupted memory is used later (in the case of overwrites) or gradually accumulating allocated but unused blocks. Thus, debugging can be extremely difficult. In this assignment, you will write a wrapper for the malloc package that will catch errors in the code that calls malloc and free. The skills you will have learned upon the completion of this exercise are pointer arithmetic and a greater understanding of the consequences of subtle memory mistakes. Logistics Unzip debugging_malloc.zip into an empty directory. The files contained are as follows: File(s): Function: debugmalloc.c Contains the implementation of the three functions you will be writing. This is the one file you will be editing and handing in. debugmalloc.h Contains the declaration of the functions, as well as the macros that will call them. driver.c Contains main procedure and the code that will be calling the functions in the malloc package dmhelper.c, dmhelper.h Contain the helper functions and macros that you will be calling from your code grader.pl Perl script that runs your code for the various test cases and gives you feedback based on your current code debugmalloc.dsp Exercise 3 project file debugmalloc.dsw Exercise 3 workspace file tailor.h, getopt.c, getopt.h Tools that are used only by the driver program for I/O purposes. You will not need to know what the code in these files do. Others Required by Visual C++. You do not need to understand their purpose Specification Programs that use this package will call the macros MALLOC and FREE. MALLOC and FREE are used exactly the same way as the malloc() and free() functions in the standard C malloc package. That is, the line void *ptr = MALLOC ( n ) ;will allocate a payload of at least n bytes, and ptr will point to the front of this block. The line FREE(ptr);will cause the payload pointed to by ptr to be deallocated and become available for later use. The macros are defined as follows: #define MALLOC(s) MyMalloc(s, __FILE__, __LINE__) #define FREE(p) MyFree(p, __FILE__, __LINE__) The __FILE__ macro resolves to the filename and __LINE__ resolves to the current line number. The debugmalloc.c file contains three functions that you are required to implement, as shown: void *MyMalloc(size_t size, char *filename, int linenumber); void MyFree(void *ptr, char *filename, int linenumber); int AllocatedSize(); Using the macros above allow MyMalloc and MyFree to be called with the filename and line number of the actual MALLOC and FREE calls, while retaining the same form as the usual malloc package. By default, MyMalloc and MyFree() simply call malloc() and free(), respectively, and return immediately. AllocatedSize() should return the number of bytes currently allocated by the user: the sum of the requested bytes through MALLOC minus the bytes freed using FREE. By default, it simply returns 0 and thus is unimplemented. The definitions are shown below: void *MyMalloc(size_t size, char *filename, int linenumber) { return (malloc(size)); } void MyFree(void *ptr, char *filename, int linenumber) { free(ptr); } int AllocatedSize() { return 0; } Your job is to modify these functions so that they will catch a number of errors that will be described in the next section. There are also two optional functions in the debugmalloc.c file that you can implement: void PrintAllocatedBlocks(); int HeapCheck(); PrintAllocatedBlocks should print out information about all currently allocated blocks. HeapCheck should check all the blocks for possible memory overwrites. Implementation Details To catch the errors, you will allocate a slightly larger amount of space and insert a header and a footer around the "requested payload". MyMalloc() will insert information into this area, and MyFree() will check to see if the information has not changed. The organization of the complete memory block is as shown below: Header Checksum ... Fence Payload Footer Fence Note:MyMalloc() returns a pointer to the payload, not the beginning of the whole block. Also, the ptr parameter passed into MyFree(void *ptr) will point to the payload, not the beginning of the block. Information that you might want to store in this extra (header, footer) area include: a "fence" immediately around the requested payload with a known value like 0xCCDEADCC, so that you can check if it has been changed when the block is freed. the size of the block a checksum for the header to ensure that it has not been corrupted (A checksum of a sequence of bits is calculated by counting the number of "1" bits in the stream. For example, the checksum for "1000100010001000" is 4. It is a simple error detection mechanism.) the filename and line number of the MALLOC() call The errors that can occur are: Error #1: Writing past the beginning of the user's block (through the fence) Error #2: Writing past the end of the user's block (through the fence) Error #3: Corrupting the header information Error #4: Attempting to free an unallocated or already-freed block Error #5: Memory leak detection (user can use ALLOCATEDSIZE to check for leaks at the end of the program) To report the first four errors, call one of these two functions: void error(int errorcode, char *filename, int linenumber); errorcode is the number assigned to the error as stated above. filename and linenumber contain the filename and line number of the line (the free call) in which the error is invoked. For example, call error(2, filename, linenumber) if you come across a situation where the footer fence has been changed. void errorfl(int errorcode, char *filename_malloc, int linenumber_malloc, char *filename_free, int linenumber_free); This is the same as the error(), except there are two sets of filenames and line numbers, one for the statement in which the block was malloc'd, and the other for the statement in which the block was free'd (and the error was invoked). The fact that MyMalloc() and MyFree() are given the filename and line number of the MALLOC() and FREE() call can prove to be very useful when you are reporting errors. The more information you print out, the easier it will be for the programmer to locate the error. Use errorfl() instead of error() whenever possible. errorfl() obviously cannot be used on situations where FREE() is called on an unallocated block, since it was not ever MALLOC'd. Note: You will only be reporting errors from MyFree(). None of the errors can be caught in MyMalloc() In the case of memory leaks, the driver program will call AllocatedSize(), and the grader will look at its return value and possible output. AllocatedSize() should return the number of bytes currently allocated from MALLOC and FREE calls. For example, the code segment: void *ptr1 = MALLOC(10), *ptr2 = MALLOC(8); FREE(ptr2); printf("%d\n", AllocatedSize()); should print out "10". Once you have gotten to the point where you can catch all of the errors, you can go an optional step further and create a global list of allocated blocks. This will allow you to perform analysis of memory leaks and currently allocated memory. You can implement the void PrintAllocatedBlocks() function, which prints out the filename and line number where all currently allocated blocks were MALLOC()'d. A macro is provided for you to use to print out information about a single block in a readable and gradeable format: PRINTBLOCK(int size, char *filename, int linenumber) Also, you can implement the int HeapCheck() function. This should check all of the currently allocated blocks and return -1 if there is an error and 0 if all blocks are valid. In addition, it should print out the information about all of the corrupted blocks, using the macro #define PRINTERROR(int errorcode, char *filename, int linenumber), with errorcode equal to the error number (according to the list described earlier) the block has gone through. You may find that this global list can also allow you to be more specific in your error messages, as it is otherwise difficult to determine the difference between an overwrite of a non-payload area and an attempted FREE() of an unallocated block. Evaluation You are given 7 test cases to work with, plus 1 extra for testing a global list. You can type "debugmalloc -t n" to run the n-th test. You can see the code that is being run in driver.c. If you have Perl installed on your machine, use grader.pl to run all the tests and print out a table of results. There are a total of 100 possible points. Here is a rundown of the test cases and desired output (do not worry about the path of the filename): Test case #1 Code char *str = (char *) MALLOC(12); strcpy(str, "123456789"); FREE(str); printf("Size: %d\n", AllocatedSize()); PrintAllocatedBlocks(); Error # None Correct Output Size: 0 Points worth 10 Details 10 points for not reporting an error and returning 0 in AllocatedSize() Test case #2 Code char *str = (char *) MALLOC(8); strcpy(str, "12345678"); FREE(str); Error # 2 Correct Output Error: Ending edge of the payload has been overwritten. in block allocated at driver.c, line 21 and freed at driver.c, line 23 Points worth 15 Details 6 pts for catching error 3 pts for printing the filename/line numbers 6 pts for correct error message Test case #3 Code char *str = (char *) MALLOC(2); strcpy(str, "12"); FREE(str); Error # 2 Correct Output Error: Ending edge of the payload has been overwritten. in block allocated at driver.c, line 28 and freed at driver.c, line 30 Points worth 15 Details 6 pts for catching error 3 pts for printing the filename/line numbers 6 pts for correct error message Test case #4 Code void *ptr = MALLOC(4); *ptr2 = MALLOC(6); FREE(ptr); printf("Size: %d\n", AllocatedSize()); PrintAllocatedBlocks(); Error # None Correct Output Size: 6 Currently allocated blocks: 6 bytes, created at driver.c, line 34 Points worth 15 Details 15 pts for not reporting an error and returning 6 from AllocatedSize Extra for printing out the extra block Test case #5 Code void *ptr = MALLOC(4); FREE(ptr); FREE(ptr); Error # 4 Correct Output Error: Attempting to free an unallocated block. in block freed at driver.c, line 43 Points worth 15 Details 15 pts for catching error Extra for correct error message Test case #6 Code char *ptr = (char *) MALLOC(4); *((int *) (ptr - 8)) = 8 + (1 << 31); FREE(ptr); Error # 1 or 3 Correct Output Error: Header has been corrupted.or Error: Starting edge of the payload has been overwritten. in block allocated at driver.c, line 47 and freed at driver.c, line 49 Points worth 15 Details 9 pts for catching error 6 pts for a correct error message Test case #7 Code char ptr[5]; FREE(ptr); Error # 4 Correct Output Error: Attempting to free an unallocated block. in block freed at driver.c, line 54 Points worth 15 Details 15 pts for recognizing error Extra for printing correct error message Test case #8 (Optional) Code int i; int *intptr = (int *) MALLOC(6); char *str = (char *) MALLOC(12); for(i = 0; i < 6; i++) { intptr[i] = i; } if (HeapCheck() == -1) { printf("\nCaught Errors\n"); } Error # None Correct Output Error: Ending edge of the payload has been overwritten. Invalid block created at driver.c, line 59 Caught Errors Points worth Extra Details "Caught Errors" indicates that the HeapCheck() function worked correctly. Extra points possible. Your instructor may give you extra credit for implementing a global list and the PrintAllocatedBlocks() and HeapCheck() functions.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值