How to troubleshot native memory leaks on Windows: GFlags and UMDHMemory issues are amongst the worst one to solve because pointing precisely the source is often difficult and painful. Memory leaks are not an exception, especially with real- world application: most of the time, programmers start to worry about it when the application outputs some “out of memory” errors.
At this moment, you have to find which one, among thousands of functions and many more allocated blocks, causes the application to leak and eventually to crash. Let’s summarize what you really need when you have a memory leak (in addition to a way to reproduce the issue): You want to find which object(s) are leaking. You want to know why they are leaking: is there some static reference to it, or maybe they are not freed? The process described today deals with the first one, which is often the most difficult. As full . NET application have their allocations tracked by the CLR (heard about the garbage collector?), a good profiler, such as the free CLRProfiler or the not- so- expensive . NET Memory Profiler or Dot. Trace for more complex cases, can help you point the issue.
使ったことはないのですがx64関係で苦労したことあるので気になった点だけ述べておきます。 使われている環境はx64の様ですが「Debugging Tools for Windows」は64ビット版でしょうか。インストールされたパスは32ビットのように. 概要・まとめ GFlags で”ページヒープ” を有効にして、アプリのバッファーオーバーフローを検出する方法を紹介します。 内容 使用するツール:GFlags GFlags を利用すると、高度なトラブルシューティング手法を有効にすることが. Hi Julien, I am trying to hunt down reason for memory build up in my application (native code on windows) In umdh when i take a diff between logs i find call stacks arranged by decreasing size of allocations. in the topmost I have a. この資料では、Windows のデバッグ方法について説明します。WinDbg デバッガー (windbg.exe) を使用してサービスを指定します。Windows サービスをデバッグするには後に、サービスをホストするプロセスに WinDbg デバッガーを接続.
2011-9-27 烛秋 昨天、今天调dump,对windbg相当的不熟悉,但也慢慢的知道了一些常用的命令,几周前听说到有gflags这样个工具,今天正好测试下。 gflags.exe是<Debugging Tools for Windows>中的一个小工具。.
With this, and the ‘! GCRoot’ command of SOS in Windbg, you can always track down the issue.
Things get harder when native code is involved, and it’s often the case, even if you don’t know it. The CLR allocates native memory (I mean memory not handled by the garbage collector) for some operations, and if you are working on big software, chances are that some third- party use such memory. Track those allocations is always difficult and time- consuming, and you often ends up with some expensive native memory profiler that have a major impact on performances when you enable it. I will show you today a quite simple (and not well- known) method that I used at work to hunt memory allocations. It requires just the symbol files of your application (you should always have them, read this if you’re not sure why), a few tools to dig down the data generated, and have a performance penalty small enough to think about temporary use it in production processes. Start the allocation stack trace collecting mode.
Pageheap.exe sets page heap flags that help to find heap-related corruption. It can also help detect leaks in programs that are running on Windows 2000 Professional Service Pack 2 (SP2) and Windows XP Professional systems.. Gflags Examples Updated: March 28, 2003 Applies To: Windows Server 2003, Windows Server 2003 R2, Windows Server 2003 with SP1, Windows Server 2003 with SP2. The following command displays the current. For IIS: Enable pageheap corruption checking using the following command: gflags.exe -p /enable w3wp.exe /full Now, recycle the Application Pool associated with the application. Be sure that you don’t recycle the. Platform SDK (Windows SDK) サポートの平田です。私は、元 ADSI/ILM/FIM を担当しておりました。ひょっとすると http:// をご覧になられた方がいらっしゃるかもしれません。ぴろととして記事を書いておりました.
The first thing we have to do is inform the heap service of Windows that we want to track down allocations for a specific process. Once again, it’s the magic tool GFlags that we have to use (available in the Debugging Tools for Windows package, that you can easily download here). Start it, and fill the fields as it is shown (of course replace ‘Allocate. Some. Cpp. Data. exe’ by your application process name): By checking the “Create user mode stack trace database”, you notify the Windows’ heap service that it has to record the call stack for each allocation done in the heap.
If we agree that a leak is something that is not freed by your program while it is running, the steps are the following: Reproduce the issue, many times, in the same process, so you ends up with a process with lots of private bytes, among which most bytes come from the leak. Find which call stack(s) allocated most of the memory. Go to your code, and check why the data allocated at the line X of the file Y (you got this information with at least one frame of the call stack and of course with symbol files including source code information) is not released, may it be a missing ‘delete’ or a store in some static container. The first step may be or not be easy, but every diagnostic process starts with it: reproduce the issue! The second step is handled by GFlags/UMDH, and the third one is all up to you. Now that you’re convinced (I hope so?) that having the call stacks that allocates most of the memory is mandatory, and sometimes sufficient, to track down memory leaks, here is the next part of the process: use UMDH.
Use UMDH to get the allocation data. UMDH (also available in the Debugging Tools for Windows package) can take a snapshot of the allocation data at a specific time, and also compare two snapshots. You first have to ask yourself: what is the best time to take my snapshots? My advice would be to follow those steps: Start your program, and do what you need to let the leak appears, but stop just before the leak indeed happen. Take the first snapshot. Reproduce the leak, many times if you can, until a large part of the private bytes allocated by the process comes from the leak (use Process Explorer to watch your process’ memory profile)Take the second snapshot. Compare the two snapshots.
Then you can kill your process if you need, not before! Now that you got the idea, let’s do it: after the setup of GFlags, start you process. Once you want to take a snapshot, start a command line. Beware that for every use of UMDH, the environment variable _NT_SYMBOL_PATH must be set!
This variable contains your symbol path, and it should look like this: ‘srv*< some local cache folder> *http: //msdl. Download/Symbols’ if you don’t have a specific symbol server for your software‘srv*< some local cache folder> *< your symbol server path> ;srv*< some local cache folder> *http: //msdl. Download/Symbols’ if you have a symbol server. Whether you define this environment variable at system level, or only in your command line, make sure it is set with the right information (to understand the symbol path syntax, you can peek some information here).
You’re ready to take your first snapshot. At the command line prompt, type: C: \Debugging Tools for Windows> umdh –p: < pid of your process> - f: Snapshot.
Play with your software, as explained previously, make the leak appears as many times as you can, and take a second snapshot: C: \Debugging Tools for Windows> umdh –p: < pid of your process> - f: Snapshot. Make the comparison: C: \Debugging Tools for Windows> umdh –d Snapshot. Snapshot. 1. txt - f: Result. The ‘- d’ option stands for decimal in output: although I use hexadecimal for anything related to memory address, I just prefer decimal when it comes to counting stuff.
So, what do you have in the result. A bunch of symbol search log at the beginning, and the call stacks ordered by amount of bytes allocated (and not freed) looking like this: + 1. Back. Trace. B7. 8AC+ 1 ( 1 – 0) Back.
Trace. B7. 8AC allocationsntdll! Rtl. Allocate. Heap+0. MSVCR1. 00. D!_heap_alloc_base+0.
MSVCR1. 00. D!_heap_alloc_dbg_impl+0. FC (f: \dd\vctools\crt_bld\self_x. MSVCR1. 00. D!_nh_malloc_dbg_impl+0. F (f: \dd\vctools\crt_bld\self_x. MSVCR1. 00. D!_nh_malloc_dbg+0.
C (f: \dd\vctools\crt_bld\self_x. MSVCR1. 00. D! malloc+0.
B (f: \dd\vctools\crt_bld\self_x. MSVCR1. 00. D! operator new+0. Allocate. Some. Cpp. Data! operator new[]+0. E (f: \dd\vctools\crt_bld\self_x. Allocate. Some. Cpp.
Data! wmain+0. 00. A (c: \users\julien\bugslasher\myfriendumdh\allocatesomecppdata\allocatesomecppdata.
Here I can see that the line 2. Allocate. Some. Cpp. Data. cpp’ made 1 allocation of 1. But let’s describe exactly what you can obtain with some dumb little C++ program. A step by step investigation with a real (but very small) C++ code.
Here is the C++ code that I’m going to use for the demo. TCHAR* argv[]). std: :cout < < "Press a key to start allocating memory" < < std: :endl. Press a key to start again" < < std: :endl. As you can see, it has a main infinite loop that allocates some memory each time the user press a key. You can also notice that an array of 1. Of course we expect UMDH to give us a leak at line 1. Just to be sure that this code leaks, here is the Process Explorer memory profile of it: Remember to configure GFlags before starting the process as explained in the beginning of the post, and check your _NT_SYMBOL_PATH environment variable!
Here are the command lines that I typed (those are explained before), the first one for the first snapshot, the second one for the second snapshot, and the third one at the end: C: \Debugging Tools for Windows> umdh –p: < pid of your process> - f: Snapshot. C: \Debugging Tools for Windows> umdh –p: < pid of your process> - f: Snapshot. C: \Debugging Tools for Windows> umdh –d Snapshot. Snapshot. 1. txt : f: Result. Let’s have a look at the ‘result.
The first lines are some information about the symbol resolving process: // Debug library initialized …DBGHELP: Allocate. Some. Cpp. Data – private symbols & lines. C: \Bug. Slasher\My. Friend. UMDH\Allocate. Some. Cpp. Data\Debug\Allocate. Some. Cpp. Data. pdb.
DBGHELP: ntdll – public symbols. C: \Symbols\wntdll. DCCFF2. D4. 83. FA4. DEE8. 1DC0. 45. 52.
C7. 3BB5. E2\wntdll. You should check if everything is ok here: remember that if some symbol file is not resolved, you will not have the function name of all the call stack’s frame located in the matching dll, and maybe worst, the remaining of the call stack after a call of this dll will probably be wrong (symbol files are needed to properly walk a call stack, read this to learn about FPO).
Just after is a reminder of the data format: //// Each log entry has the following syntax: //// + BYTES_DELTA (NEW_BYTES – OLD_BYTES) NEW_COUNT allocs Back. Trace TRACEID// + COUNT_DELTA (NEW_COUNT – OLD_COUNT) Back. Trace TRACEID allocations// … stack trace …//// where: //// BYTES_DELTA – increase in bytes between before and after log// NEW_BYTES – bytes in after log// OLD_BYTES – bytes in before log// COUNT_DELTA – increase in allocations between before and after log// NEW_COUNT – number of allocations in after log// OLD_COUNT – number of allocations in before log// TRACEID – decimal index of the stack trace in the trace database// (can be used to search for allocation instances in the original// UMDH logs).//With this you should be able to understand the remaining of the file (I put only the first two call stacks, the third one is about internals of the std: :string object, and is not relevant here): + 5. Back. Trace. 11. 78. AC+ 6 ( 6 – 0) Back.
Trace. 11. 78. AC allocationsntdll! Rtl. Allocate. Heap+0. MSVCR1. 00. D!_heap_alloc_base+0.