Fix memory leaks resulting from 'when' and 'return when' statements.

Addresses #946.
This commit is contained in:
Jon Siwek 2013-02-19 16:19:16 -06:00
parent 7e5115460c
commit d158c7ffdf
6 changed files with 106 additions and 6 deletions

View file

@ -87,6 +87,7 @@ Frame* Frame::Clone()
void Frame::SetTrigger(Trigger* arg_trigger) void Frame::SetTrigger(Trigger* arg_trigger)
{ {
ClearTrigger();
if ( arg_trigger ) if ( arg_trigger )
Ref(arg_trigger); Ref(arg_trigger);
trigger = arg_trigger; trigger = arg_trigger;

View file

@ -926,17 +926,22 @@ void NotifierRegistry::Register(ID* id, NotifierRegistry::Notifier* notifier)
DBG_LOG(DBG_NOTIFIERS, "registering ID %s for notifier %s", DBG_LOG(DBG_NOTIFIERS, "registering ID %s for notifier %s",
id->Name(), notifier->Name()); id->Name(), notifier->Name());
Attr* attr = new Attr(ATTR_TRACKED);
if ( id->Attrs() ) if ( id->Attrs() )
id->Attrs()->AddAttr(new Attr(ATTR_TRACKED)); {
if ( ! id->Attrs()->FindAttr(ATTR_TRACKED) )
id->Attrs()->AddAttr(attr);
}
else else
{ {
attr_list* a = new attr_list; attr_list* a = new attr_list;
Attr* attr = new Attr(ATTR_TRACKED);
a->append(attr); a->append(attr);
id->SetAttrs(new Attributes(a, id->Type(), false)); id->SetAttrs(new Attributes(a, id->Type(), false));
Unref(attr);
} }
Unref(attr);
NotifierMap::iterator i = ids.find(id->Name()); NotifierMap::iterator i = ids.find(id->Name());
if ( i != ids.end() ) if ( i != ids.end() )
@ -967,7 +972,9 @@ void NotifierRegistry::Unregister(ID* id, NotifierRegistry::Notifier* notifier)
if ( i == ids.end() ) if ( i == ids.end() )
return; return;
Attr* attr = id->Attrs()->FindAttr(ATTR_TRACKED);
id->Attrs()->RemoveAttr(ATTR_TRACKED); id->Attrs()->RemoveAttr(ATTR_TRACKED);
Unref(attr);
NotifierSet* s = i->second; NotifierSet* s = i->second;
s->erase(notifier); s->erase(notifier);

View file

@ -242,6 +242,7 @@ bool Trigger::Eval()
trigger->Cache(frame->GetCall(), v); trigger->Cache(frame->GetCall(), v);
trigger->Release(); trigger->Release();
frame->ClearTrigger();
} }
Unref(v); Unref(v);
@ -251,7 +252,6 @@ bool Trigger::Eval()
timer_mgr->Cancel(timer); timer_mgr->Cancel(timer);
Disable(); Disable();
UnregisterAll();
Unref(this); Unref(this);
return true; return true;
@ -331,6 +331,7 @@ void Trigger::Timeout()
#endif #endif
trigger->Cache(frame->GetCall(), v); trigger->Cache(frame->GetCall(), v);
trigger->Release(); trigger->Release();
frame->ClearTrigger();
} }
Unref(v); Unref(v);
@ -338,7 +339,6 @@ void Trigger::Timeout()
} }
Disable(); Disable();
UnregisterAll();
Unref(this); Unref(this);
} }
@ -426,6 +426,12 @@ Val* Trigger::Lookup(const CallExpr* expr)
return (i != cache.end()) ? i->second : 0; return (i != cache.end()) ? i->second : 0;
} }
void Trigger::Disable()
{
UnregisterAll();
disabled = true;
}
const char* Trigger::Name() const const char* Trigger::Name() const
{ {
assert(location); assert(location);

View file

@ -49,7 +49,7 @@ public:
// Disable this trigger completely. Needed because Unref'ing the trigger // Disable this trigger completely. Needed because Unref'ing the trigger
// may not immediately delete it as other references may still exist. // may not immediately delete it as other references may still exist.
void Disable() { disabled = true; } void Disable();
virtual void Describe(ODesc* d) const { d->Add("<trigger>"); } virtual void Describe(ODesc* d) const { d->Add("<trigger>"); }

View file

@ -696,7 +696,9 @@ string FuncType::FlavorString() const
FuncType::~FuncType() FuncType::~FuncType()
{ {
Unref(args);
Unref(arg_types); Unref(arg_types);
Unref(yield);
} }
BroType* FuncType::YieldType() BroType* FuncType::YieldType()

View file

@ -0,0 +1,84 @@
# Needs perftools support.
#
# @TEST-GROUP: leaks
#
# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks
#
# @TEST-EXEC: btest-bg-run bro HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local bro -m -b %INPUT
# @TEST-EXEC: btest-bg-wait 15
redef exit_only_after_terminate = T;
global my_set: set[string] = set();
global flag: string = "flag";
global done: bool = F;
function dummyfunc(s: string): string
{
return "dummy " + s;
}
function async_func(s: string): string
{
print dummyfunc("from async_func() " + s);
return when ( flag in my_set )
{
return flag + " in my_set";
}
timeout 3sec
{
return "timeout";
}
}
event set_flag()
{
add my_set[flag];
}
event do_another()
{
delete my_set[flag];
local local_dummy = dummyfunc;
local anon = function(s: string): string { return s + "!"; };
if ( ! done )
schedule 1sec { set_flag() };
when ( local result = async_func("from do_another()") )
{
print "async_func() return result in do_another()", result;
print local_dummy("from do_another() when block");
print anon("hi");
if ( result == "timeout" )
terminate();
else
{
done = T;
schedule 10msec { do_another() };
}
}
}
event bro_init()
{
local local_dummy = dummyfunc;
local anon = function(s: string): string { return s + "!"; };
schedule 1sec { set_flag() };
when ( local result = async_func("from bro_init()") )
{
print "async_func() return result in bro_init()", result;
print local_dummy("from bro_init() when block");
print anon("hi");
if ( result == "timeout" ) terminate();
schedule 10msec { do_another() };
}
}