Fix severe leaks when using string object, and update the test suite

to try and catch this in the future.  Not part of the build.


git-svn-id: svn://10.0.0.236/trunk@142252 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
mhammond%skippinet.com.au
2003-05-09 01:23:22 +00:00
parent e9ba9ad8db
commit 75e9d25200
3 changed files with 106 additions and 10 deletions

View File

@@ -852,19 +852,13 @@ PyXPCOM_InterfaceVariantHelper::~PyXPCOM_InterfaceVariantHelper()
}
}
if (ns_v.IsValDOMString() && ns_v.val.p) {
PythonTypeDescriptor &ptd = m_python_type_desc_array[i];
if (XPT_PD_IS_OUT(ptd.param_flags) || XPT_PD_IS_DIPPER(ptd.param_flags))
delete (const nsAString *)ns_v.val.p;
delete (const nsAString *)ns_v.val.p;
}
if (ns_v.IsValCString() && ns_v.val.p) {
PythonTypeDescriptor &ptd = m_python_type_desc_array[i];
if (XPT_PD_IS_OUT(ptd.param_flags) || XPT_PD_IS_DIPPER(ptd.param_flags))
delete (const nsACString *)ns_v.val.p;
delete (const nsACString *)ns_v.val.p;
}
if (ns_v.IsValUTF8String() && ns_v.val.p) {
PythonTypeDescriptor &ptd = m_python_type_desc_array[i];
if (XPT_PD_IS_OUT(ptd.param_flags) || XPT_PD_IS_DIPPER(ptd.param_flags))
delete (const nsACString *)ns_v.val.p;
delete (const nsACString *)ns_v.val.p;
}
if (ns_v.IsValArray()) {
nsXPTCVariant &ns_v = m_var_array[i];
@@ -1171,6 +1165,8 @@ PRBool PyXPCOM_InterfaceVariantHelper::FillInVariant(const PythonTypeDescriptor
PyErr_NoMemory();
BREAK_FALSE;
}
// We created it - flag as such for cleanup.
ns_v.flags |= nsXPTCVariant::VAL_IS_DOMSTR;
break;
}
case nsXPTType::T_CSTRING:
@@ -1203,6 +1199,8 @@ PRBool PyXPCOM_InterfaceVariantHelper::FillInVariant(const PythonTypeDescriptor
PyErr_NoMemory();
BREAK_FALSE;
}
// We created it - flag as such for cleanup.
ns_v.flags |= bIsUTF8 ? nsXPTCVariant::VAL_IS_UTF8STR : nsXPTCVariant::VAL_IS_CSTR;
break;
}
case nsXPTType::T_CHAR_STR: {
@@ -1455,6 +1453,7 @@ PRBool PyXPCOM_InterfaceVariantHelper::PrepareOutVariant(const PythonTypeDescrip
ns_v.flags |= nsXPTCVariant::VAL_IS_DOMSTR;
// Dippers are really treated like "in" params.
ns_v.ptr = new nsString();
ns_v.val.p = ns_v.ptr; // VAL_IS_* says the .p is what gets freed
if (!ns_v.ptr) {
PyErr_NoMemory();
rc = PR_FALSE;
@@ -1467,6 +1466,7 @@ PRBool PyXPCOM_InterfaceVariantHelper::PrepareOutVariant(const PythonTypeDescrip
NS_ABORT_IF_FALSE(XPT_PD_IS_DIPPER(td.param_flags) && XPT_PD_IS_IN(td.param_flags), "out DOMStrings must really be in dippers!");
ns_v.flags |= ( XPT_TDP_TAG(ns_v.type)==nsXPTType::T_CSTRING ? nsXPTCVariant::VAL_IS_CSTR : nsXPTCVariant::VAL_IS_UTF8STR);
ns_v.ptr = new nsCString();
ns_v.val.p = ns_v.ptr; // VAL_IS_* says the .p is what gets freed
if (!ns_v.ptr) {
PyErr_NoMemory();
rc = PR_FALSE;

View File

@@ -0,0 +1,72 @@
# test tools for the pyxpcom
# export a "getmemusage()" function that returns a useful "bytes used" count
# for the current process. Growth in this when doing the same thing over and
# over implies a leak.
try:
import win32api
import win32pdh
import win32pdhutil
have_pdh = 1
except ImportError:
have_pdh = 0
# XXX - win32pdh is slow, particularly finding our current process.
# A better way would be good.
# Our win32pdh specific functions - they can be at the top-level on all
# platforms, but will only actually be called if the modules are available.
def FindMyCounter():
pid_me = win32api.GetCurrentProcessId()
object = "Process"
items, instances = win32pdh.EnumObjectItems(None,None,object, -1)
for instance in instances:
# We use 2 counters - "ID Process" and "Working Set"
counter = "ID Process"
format = win32pdh.PDH_FMT_LONG
hq = win32pdh.OpenQuery()
path = win32pdh.MakeCounterPath( (None,object,instance, None, -1,"ID Process") )
hc1 = win32pdh.AddCounter(hq, path)
path = win32pdh.MakeCounterPath( (None,object,instance, None, -1,"Working Set") )
hc2 = win32pdh.AddCounter(hq, path)
win32pdh.CollectQueryData(hq)
type, pid = win32pdh.GetFormattedCounterValue(hc1, format)
if pid==pid_me:
win32pdh.RemoveCounter(hc1) # not needed any more
return hq, hc2
# Not mine - close the query and try again
win32pdh.RemoveCounter(hc1)
win32pdh.RemoveCounter(hc2)
win32pdh.CloseQuery(hq)
else:
raise RuntimeError, "Can't find myself!?"
def CloseCounter(hq, hc):
win32pdh.RemoveCounter(hc)
win32pdh.CloseQuery(hq)
def GetCounterValue(hq, hc):
win32pdh.CollectQueryData(hq)
format = win32pdh.PDH_FMT_LONG
type, val = win32pdh.GetFormattedCounterValue(hc, format)
return val
g_pdh_data = None
# The pdh function that does the work
def pdh_getmemusage():
global g_pdh_data
if g_pdh_data is None:
hq, hc = FindMyCounter()
g_pdh_data = hq, hc
hq, hc = g_pdh_data
return GetCounterValue(hq, hc)
# The public bit
if have_pdh:
getmemusage = pdh_getmemusage
else:
def getmemusage():
return 0

View File

@@ -15,11 +15,16 @@
# Contributor(s): Mark Hammond <MarkH@ActiveState.com> (original author)
#
import sys, os
import sys, os, time
import xpcom.components
import xpcom._xpcom
import xpcom.nsError
try:
import gc
except ImportError:
gc = None
num_errors = 0
component_iid = xpcom.components.ID("{7EE4BDC6-CB53-42c1-A9E4-616B8E012ABA}")
@@ -453,6 +458,8 @@ except ImportError:
def gettotalrefcount():
return 0
from pyxpcom_test_tools import getmemusage
def test_from_js():
# Ensure we can find the js test script - same dir as this!
# Assume the path of sys.argv[0] is where we can find the js test code.
@@ -492,11 +499,17 @@ def doit(num_loops = -1):
if i==0:
# First loop is likely to "leak" as we cache things.
# Leaking after that is a problem.
if gc is not None:
gc.collect()
num_refs = gettotalrefcount()
mem_usage = getmemusage()
if num_errors:
break
if gc is not None:
gc.collect()
lost = gettotalrefcount() - num_refs
# Sometimes we get spurious counts off by 1 or 2.
# This can't indicate a real leak, as we have looped
@@ -504,6 +517,17 @@ def doit(num_loops = -1):
if abs(lost)>2:
print "*** Lost %d references" % (lost,)
# sleep to allow the OS to recover
time.sleep(1)
mem_lost = getmemusage() - mem_usage
# working set size is fickle, and when we were leaking strings, this test
# would report a leak of 100MB. So we allow a 2MB buffer - but even this
# may still occasionally report spurious warnings. If you are really
# worried, bump the counter to a huge value, and if there is a leak it will
# show.
if mem_lost > 2000000:
print "*** Lost %.6f MB of memory" % (mem_lost/1000000.0,)
if num_errors:
print "There were", num_errors, "errors testing the Python component :-("
else: