Hi Claudio, Sorry for my late response. I have gone through Claudio's solution. It's good. But I think this is not a common solution. There are two points: 1. This solution must use Pointerbyreference to encapsulate the Pointer. This is not clean. 2. Libvirt provides virFree method. But a common library could not provide memory management functions. My proposal is as following: 1. Add a new Class Libc.java public interface Libc extends Library{ Libc INSTANCE = (Libc) Native.loadLibrary("c", Libc.class); public void free(Pointer p); } 2. Transfer the following code as following: public SchedParameter[] getSchedulerParameters() throws LibvirtException { IntByReference nParams = new IntByReference(); SchedParameter[] returnValue = new SchedParameter[0]; - String scheduler = libvirt.virDomainGetSchedulerType(VDP, nParams); + Pointer pScheduler = libvirt.virDomainGetSchedulerType(VDP, nParams); processError(); - if (scheduler != null) { + if (pScheduler != null) { + String scheduler = pScheduler.getString(0); + libc.free(pScheduler); virSchedParameter[] nativeParams = new virSchedParameter[nParams.getValue()]; returnValue = new SchedParameter[nParams.getValue()]; libvirt.virDomainGetSchedulerParameters(VDP, nativeParams, nParams); What about your opinion? B.R. Benjamin Wang -----Original Message----- From: Claudio Bley [mailto:cbley@xxxxxxxxxx] Sent: 2012年10月8日 20:33 To: veillard@xxxxxxxxxx Cc: Benjamin Wang (gendwang); libvir-list@xxxxxxxxxx; Yang Zhou (yangzho) Subject: Re: Memory free in libvirt JNA Hi Daniel, At Fri, 28 Sep 2012 22:34:13 +0800, Daniel Veillard wrote: > > sorry for the delay, I need to focuse one something else ATM ! Me too. So, no worries! ;) > First do you have a small pointer indicating where in JNA that kind of > native deallocation must take place, since most of the time JNA can do > the marshalling all by itself ? This effects mostly Strings. JNA takes the safe assumption that functions are returning "const char*"s because it can't distinguish a string (const char*) from a string (char*). See https://github.com/twall/jna/blob/master/www/FrequentlyAskedQuestions.md#how-do-i-read-back-a-functions-string-result So, here is a list of methods of org.libvirt.jna.Libvirt which return a string (/probably/ a char*, not const char*) which need to be checked: virConnectBaselineCPU virConnectDomainXMLFromNative virConnectDomainXMLToNative virConnectFindStoragePoolSources virConnectGetCapabilities virConnectGetHostname virConnectGetType virConnectGetURI virDomainGetName virDomainGetOSType virDomainGetXMLDesc virDomainSnapshotGetXMLDesc virInterfaceGetMACString virInterfaceGetName virInterfaceGetXMLDesc virNWFilterGetName virNWFilterGetXMLDesc virNetworkGetBridgeName virNetworkGetName virNetworkGetXMLDesc virNodeDeviceGetName virNodeDeviceGetParent virNodeDeviceGetXMLDesc virSecretGetUsageID virSecretGetXMLDesc virStoragePoolGetName virStoragePoolGetXMLDesc virStorageVolGetKey virStorageVolGetName virStorageVolGetPath virStorageVolGetXMLDesc > And second would you have an idea how to systematically detect such > leaks, the kind of loop suggested to expose the issue is nor really > practical to chase the leaks ... I tried valgrind, but it didn't produce any output. mtrace wasn't very helpful either. So, I just hacked this up: ,----[ memcheck.py ] | import gdb | | allocations = {} | | class AllocBreak(gdb.FinishBreakpoint): | def stop(self): | global allocations | | if self.return_value != None: | callstack = [] | frame = gdb.selected_frame() | | while frame: | name = frame.name() | func = frame.function() | sal = frame.find_sal() | | funcname = func.print_name if func else '?' | line = sal.line | filename = sal.symtab.filename if sal.symtab else '?' | | callstack.append((name, filename, line, funcname)) | | frame = frame.older() | | addr = int(str(self.return_value), 16) | allocations[addr] = callstack | | | class MemAlloc (gdb.Command): | "Track allocations." | def __init__(self): | super(MemAlloc, self).__init__("memalloc", gdb.COMMAND_NONE) | | def invoke(self, arg, from_tty): | top = gdb.selected_frame() | frame = top.older() | | if frame: | func = frame.function() | | if func: # and func.name.startswith("virAlloc"): | ab = AllocBreak(top, True) | ab.silent = True | | | class MemFree(gdb.Command): | "Track de-allocations." | def __init__(self): | super(MemFree, self).__init__("memfree", gdb.COMMAND_NONE) | | def invoke(self, arg, from_tty): | global allocations | | block = gdb.selected_frame().block() | while block: | for sym in block: | if sym.is_argument: | addr = int(str(gdb.parse_and_eval(sym.name)), 16) | | if addr in allocations: | del allocations[addr] | | return | block = block.superblock | | | class MemReport(gdb.Command): | def __init__(self): | super(MemReport, self).__init__("memreport", gdb.COMMAND_NONE) | | def invoke(self, arg, from_tty): | global allocations | | nr = 1 | for k, v in allocations.iteritems(): | print "{0}. @{1}".format(nr, k) | i = 1 | gap = 0 | nr += 1 | for name, filename, line, funcname in v: | if name: | if gap > 0: | print " #{0} ... [{1}]".format(i, gap) | i += 1 | gap = 0 | print " #{0} {1} {2} {3}:{4}".format(i, name, funcname, filename, line) | i += 1 | else: | gap += 1 | if gap > 0: | print " #{0} ... [{1}]".format(i, gap) | | MemAlloc() | MemFree() | MemReport() `---- ,----[ .gdbinit ] | set pagination off | source memcheck.py | set breakpoint pending on | | break calloc | commands | silent | memalloc | cont | end | | break free | commands | silent | memfree | cont | end | | break malloc | commands | silent | memalloc | cont | end | | disable | | tbreak virInitialize | commands | silent | finish | end `---- Just drop these two files into the current directory, so that GDB will find them. Using this short Java program ,----[ Memcheck.java ] | | package org.libvirt; | | import com.sun.jna.Library; | import com.sun.jna.Native; | | import java.io.InputStream; | import java.io.InputStreamReader; | import java.io.BufferedReader; | import java.io.FileInputStream; | import java.io.IOException; | import java.util.Set; | import java.util.HashSet; | | class Memcheck { | public static void main(String[] args) throws IOException { | Connect conn = null; | Domain dom = null; | | try { | conn = new Connect("test:///default", false); | | dom = conn.domainDefineXML("<domain type='test' id='2'>" + " <name>deftest</name>" | + " <uuid>004b96e1-2d78-c30f-5aa5-f03c87d21e70</uuid>" + " <memory>8388608</memory>" | + " <vcpu>2</vcpu>" + " <os><type arch='i686'>hvm</type></os>" | + " <on_reboot>restart</on_reboot>" + " <on_poweroff>destroy</on_poweroff>" | + " <on_crash>restart</on_crash>" + | "</domain>"); | | System.out.println("virDomainGetSchedulerType:" + | dom.getSchedulerType()[0]); | | dom.undefine(); | } catch (LibvirtException e) { | System.out.println("exception caught:" + e); | System.out.println(e.getError()); | } finally { | if (conn != null) | try { | dom.free(); | conn.close(); | } catch (LibvirtException e) { | } | } | } | } `---- and running gdb --args java -cp \ /usr/share/java/jna.jar:target/classes/:target/testclasses/ \ org.libvirt.Memcheck with libvirt-java @efbe26d yielded this GDB output: (gdb) run ... 0x00007fffe7dfda14 in ffi_call_unix64 () from /usr/lib/x86_64-linux-gnu/libffi.so.6 Value returned is $1 = 0 (gdb) enable (gdb) c ... (gdb) memreport 1. @140737220921872 #1 testDomainGetSchedulerType testDomainGetSchedulerType /build/buildd/libvirt-0.9.12/./src/test/test_driver.c:2679 #2 virDomainGetSchedulerType virDomainGetSchedulerType /build/buildd/libvirt-0.9.12/./src/libvirt.c:6848 #3 ffi_call_unix64 ? ?:0 #4 ffi_call ? ?:0 #5 ... [1] #6 Java_com_sun_jna_Function_invokePointer ? ?:0 #7 ... [7] > In the meantime I pushed you initial patch, thanks ! Thank you! Evidently, this really fixed the memleak above. Yay! :) -- AV-Test GmbH, Henricistraße 20, 04155 Leipzig, Germany Phone: +49 341 265 310 19 Web:<http://www.av-test.org> Eingetragen am / Registered at: Amtsgericht Stendal (HRB 114076) Geschaeftsfuehrer (CEO): Andreas Marx, Guido Habicht, Maik Morgenstern -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list