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:
@@ -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;
|
||||
|
||||
72
mozilla/extensions/python/xpcom/test/pyxpcom_test_tools.py
Normal file
72
mozilla/extensions/python/xpcom/test/pyxpcom_test_tools.py
Normal 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
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user