From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-0.9 required=5.0 tests=BAYES_00,FORGED_GMAIL_RCVD, FREEMAIL_FROM autolearn=no autolearn_force=no version=3.4.4 X-Google-Thread: 103376,ec2a500cce3658c4 X-Google-Attributes: gid103376,public X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news1.google.com!postnews1.google.com!not-for-mail From: mosteo@gmail.com (Alex R. Mosteo) Newsgroups: comp.lang.ada Subject: Re: Memory leak - What the ...? Date: 12 Oct 2004 05:05:52 -0700 Organization: http://groups.google.com Message-ID: References: NNTP-Posting-Host: 62.101.167.106 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit X-Trace: posting.google.com 1097582753 12732 127.0.0.1 (12 Oct 2004 12:05:53 GMT) X-Complaints-To: groups-abuse@google.com NNTP-Posting-Date: Tue, 12 Oct 2004 12:05:53 +0000 (UTC) Xref: g2news1.google.com comp.lang.ada:5087 Date: 2004-10-12T05:05:52-07:00 List-Id: Brian May wrote in message news:... > >>>>> "Stephen" == Stephen Leake writes: > > Stephen> There may be a bug with this in GNAT 3.15p on your OS. > > With Gnat 3.15p under Linux, I got: > > Debug Pool info: > Total allocated bytes : 1530000 > Total deallocated bytes : 1530000 > Current Water Mark: 0 > High Water Mark: 511008 First off, let me thanks everyone of you for your help. Getting in someone's code is sometimes a bore. I regret that I only have access via google to the news by the moment, so the discussion can't be more fluid :( I think some points have been raised that are already clear. The Data pointer is not initialized because for uninitialized ones, pointers value is null in Ada by default. Second, this code is only valid because we are talking about an Adjust procedure of a Controlled type. Third, we agree in that there's no logic flaw (what a relief!). Now some results. I've tested your gnat.debug_pools code (only in win32, which is the only one I have available until thursday) and I get: Debug Pool info: Total allocated bytes : 1530000 Total deallocated bytes : 1530000 Current Water Mark: 0 High Water Mark: 511008 That's it, no leak reported. But running gnatmem 3 test.exe I get: Global information ------------------ Total number of allocations :6001 Total number of deallocations : 0 Final Water Mark (non freed mem) : 1.49 Megabytes High Water Mark : 1.49 Megabytes Which clearly shows... not a single deallocation !? (BTW the result is similar without the Debug_Pool). This is wrong. That's the reason I firstly stated the test case was leaking. Too fast jump to conclusions. But linking with -lgmem and running the post-mortem gnatmem gnatmem 10 -i gmem.out test.exe I get: Global information ------------------ Total number of allocations :3001 Total number of deallocations :3000 Final Water Mark (non freed mem) : 24 Bytes High Water Mark : 499.05 Kilobytes Allocation Root # 1 ------------------- Number of non freed allocations : 1 Final Water Mark (non freed mem) : 24 Bytes High Water Mark : 24 Bytes Backtrace : a-except.adb:1289 ada.exceptions.process_raise_exception a-except.adb:2638 b~test.adb:106 adainit b~test.adb:189 main Which is consistent. I've placed traces in adjust and finalize and they're called as expected (some of you have already reproduced this). However, one thing is absolutely true: changing this type by an all static one has removed the leak in my full program. Since we can agree that the implementation is right, I must be doing something different in my program which this test case doesn't show. I'll try to summarize all accesses to this type for you, or better still to get a faultly test case. I started this bug chase because I have a program which apparently runs fine but that after 24 hours of uptime starts to bog down a server (not under my control, one in which I can't get memory usage) so it seems plausible that the leak is making it to swap like crazy. I didn't suspect of a leak until I observed it in windows, and I haven't been back to linux since. Finally, here are posted the memory-usage functions I use in both Linux and Win32, which you can find of interest. -------- Win32 version (requires win32ada binding) -------- with Interfaces; with Interfaces.C; with System; with Win32; use Win32; with Win32.Winbase; with Win32.Winerror; with Win32.Winnt; package body Adagio.Os.Memory is ------------------------------------------------------------------------ -- LocalHeapSize -- ------------------------------------------------------------------------ function LocalHeapSize (Heap : in Win32.Winnt.HANDLE) return Natural is use Interfaces; use Interfaces.C; use Win32.Winbase; use Win32.Winerror; use Win32.Winnt; he : aliased PROCESS_HEAP_ENTRY; Size : Natural := 0; Blocks : Natural := 0; begin he.lpData := System.Null_address; loop if HeapWalk (Heap, he'unchecked_access) = 0 then if GetLastError = ERROR_NO_MORE_ITEMS then exit; else raise Walk_error; end if; end if; Blocks := Blocks + 1; if (Unsigned_32 (he.wFlags) and Unsigned_32 (PROCESS_HEAP_UNCOMMITTED_RANGE)) = 0 then Size := Size + Natural (he.cbData); end if; end loop; return Size; end LocalHeapSize; ------------------------------------------------------------------------ -- MemorySize -- ------------------------------------------------------------------------ function MemorySize return Natural is use Win32.Winbase; use Win32.Winnt; heaps : array (1 .. 100) of aliased HANDLE; nHeaps : DWORD; cSize : Natural := 0; begin nHeaps := GetProcessHeaps (heaps'length, heaps (heaps'first)'Unchecked_access); if Natural (nHeaps) = heaps'Last then raise Constraint_error; end if; for N in 1 .. Natural (nHeaps) loop cSize := cSize + LocalHeapSize (heaps (N)); end loop; return cSize; end MemorySize; ------------------------------------------------------------------------ -- Heap_usage -- ------------------------------------------------------------------------ -- Returns the heap memory in use in bytes function Heap_usage return Natural is begin return MemorySize; end Heap_usage; end Adagio.Os.Memory; ----- Linux version, requires access to /proc filesystem --------------- -- There are some auxiliary functions missing, but they're very basic: -- A integer-to-string without leading space conversion, -- a extract-column-from-line... function Get_Pid return Integer; pragma Import (C, Get_Pid, "__getpid"); ------------------------------------------------------------------------ -- Heap_usage -- ------------------------------------------------------------------------ -- Returns the heap memory in use in bytes -- Any thread pid is OK, since all threads share the memory info (?) function Heap_usage return Natural is Pid : constant Integer := Get_Pid; use Text_IO; File : File_Type; Line : String (1 .. 256); Last : Natural; begin Open (File, Name => "/proc/" & Agpl.Strings.To_String (Pid) & "/statm", Mode => In_file); Get_Line (File, Line, Last); Close (File); -- The second number in /proc//statm gives the resident memory size return 1024 * Natural'Value ( Agpl.Strings.Fields.Select_Field (Line (1 .. Last), 2)); exception when others => if Is_Open (File) then Close (File); end if; return 0; end Heap_usage;