Skip to content

Commit

Permalink
Fix memory leaks in Debugger
Browse files Browse the repository at this point in the history
* shared structure `BreakpointLocationsInfo` between debuggger and ByteCodeBlock can cause memory leaks
* correctly delete each `BreakpointLocationsInfo`

Signed-off-by: HyukWoo Park <hyukwoo.park@samsung.com>
  • Loading branch information
clover2123 authored and ksh8281 committed Jan 17, 2025
1 parent 19498b4 commit a57df75
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 6 deletions.
29 changes: 28 additions & 1 deletion src/api/EscargotPublic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1718,6 +1718,7 @@ class DebuggerC : public Debugger {
virtual void consoleOut(String* output) override;
virtual String* getClientSource(String** sourceName) override;
virtual bool getWaitBeforeExitClient() override;
virtual void deleteClient() override;

DebuggerC(DebuggerOperationsRef::DebuggerClient* debuggerClient, Context* context)
: m_debuggerClient(debuggerClient)
Expand Down Expand Up @@ -1821,7 +1822,11 @@ void DebuggerC::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset,

void DebuggerC::byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock)
{
m_debuggerClient->codeReleased(reinterpret_cast<DebuggerOperationsRef::WeakCodeRef*>(byteCodeBlock));
if (m_debuggerClient) {
// Debugger could be removed earlier when this function called
// e.g. release global objects such as Context and VMInstance at the end of execution
m_debuggerClient->codeReleased(reinterpret_cast<DebuggerOperationsRef::WeakCodeRef*>(byteCodeBlock));
}
}

void DebuggerC::exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace)
Expand All @@ -1847,6 +1852,15 @@ bool DebuggerC::getWaitBeforeExitClient()
return false;
}

void DebuggerC::deleteClient()
{
if (m_debuggerClient) {
// delete DebuggerClient that was created and delivered from the user
delete m_debuggerClient;
m_debuggerClient = nullptr;
}
}

bool DebuggerC::processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest)
{
UNUSED_PARAMETER(state);
Expand Down Expand Up @@ -3093,6 +3107,19 @@ bool ContextRef::initDebugger(DebuggerOperationsRef::DebuggerClient* debuggerCli
#endif /* ESCARGOT_DEBUGGER */
}

bool ContextRef::disableDebugger()
{
#ifdef ESCARGOT_DEBUGGER
Context* context = toImpl(this);
if (context->debugger()) {
context->debugger()->deleteClient();
}
return true;
#else
return false;
#endif
}

bool ContextRef::initDebuggerRemote(const char* options)
{
#ifdef ESCARGOT_DEBUGGER
Expand Down
2 changes: 2 additions & 0 deletions src/api/EscargotPublic.h
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,7 @@ class ESCARGOT_EXPORT DebuggerOperationsRef {
// Base class for debugger callbacks
class DebuggerClient {
public:
virtual ~DebuggerClient(){};
virtual void parseCompleted(StringRef* source, StringRef* srcName, std::vector<DebuggerOperationsRef::BreakpointLocationsInfo*>& breakpointLocationsVector) = 0;
virtual void parseError(StringRef* source, StringRef* srcName, StringRef* error) = 0;
virtual void codeReleased(WeakCodeRef* weakCodeRef) = 0;
Expand Down Expand Up @@ -900,6 +901,7 @@ class ESCARGOT_EXPORT ContextRef {
void throwException(ValueRef* exceptionValue);

bool initDebugger(DebuggerOperationsRef::DebuggerClient* debuggerClient);
bool disableDebugger();
// available options(separator is ';')
// "--port=6501", default for TCP debugger
bool initDebuggerRemote(const char* options);
Expand Down
7 changes: 6 additions & 1 deletion src/debugger/Debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class InterpretedCodeBlock;

class Debugger : public gc {
public:
// The following code is the sam as in EscargotPublic.h
// The following code is the same as in EscargotPublic.h
class WeakCodeRef;

struct BreakpointLocation {
Expand Down Expand Up @@ -108,6 +108,10 @@ class Debugger : public gc {

void clearParsingData()
{
for (size_t i = 0; i < m_breakpointLocationsVector.size(); i++) {
// delete each shared Debugger::BreakpointLocationsInfo here
delete m_breakpointLocationsVector[i];
}
m_breakpointLocationsVector.clear();
}

Expand Down Expand Up @@ -163,6 +167,7 @@ class Debugger : public gc {
virtual void consoleOut(String* output) = 0;
virtual String* getClientSource(String** sourceName) = 0;
virtual bool getWaitBeforeExitClient() = 0;
virtual void deleteClient() {}
virtual bool skipSourceCode(String* srcName) const
{
return false;
Expand Down
8 changes: 5 additions & 3 deletions src/interpreter/ByteCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,11 @@ ByteCodeBlock::ByteCodeBlock()
static void clearByteCodeBlock(ByteCodeBlock* self)
{
#ifdef ESCARGOT_DEBUGGER
Debugger* debugger = self->codeBlock()->context()->debugger();
if (debugger != nullptr && self->codeBlock()->markDebugging()) {
debugger->byteCodeReleaseNotification(self);
if (!self->m_isOwnerMayFreed) {
Debugger* debugger = self->codeBlock()->context()->debugger();
if (debugger != nullptr && self->codeBlock()->markDebugging()) {
debugger->byteCodeReleaseNotification(self);
}
}
#endif
self->m_code.clear();
Expand Down
13 changes: 12 additions & 1 deletion src/interpreter/ByteCodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,11 +210,22 @@ ByteCodeBreakpointContext::ByteCodeBreakpointContext(Debugger* debugger, Interpr
: m_lastBreakpointLineOffset(0)
, m_lastBreakpointIndexOffset(0)
, m_originSourceLineOffset(codeBlock->script()->originSourceLineOffset())
, m_breakpointLocations()
, m_breakpointLocations(nullptr)
, m_sharedWithDebugger(false)
{
m_breakpointLocations = new Debugger::BreakpointLocationsInfo(reinterpret_cast<Debugger::WeakCodeRef*>(codeBlock));
if (debugger && codeBlock->markDebugging() && addBreakpointLocationsInfoToDebugger) {
debugger->appendBreakpointLocations(m_breakpointLocations);
m_sharedWithDebugger = true;
}
}

ByteCodeBreakpointContext::~ByteCodeBreakpointContext()
{
if (!m_sharedWithDebugger) {
// directly delete each BreakpointLocationsInfo
// because BreakpointLocationsInfo is not shared with Debugger
delete m_breakpointLocations;
}
}
#endif /* ESCARGOT_DEBUGGER */
Expand Down
4 changes: 4 additions & 0 deletions src/interpreter/ByteCodeGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,12 @@ struct ByteCodeBreakpointContext {
size_t m_originSourceLineOffset; // original source's start line offset
Debugger::BreakpointLocationsInfo* m_breakpointLocations;
bool m_parsingEnabled;
bool m_sharedWithDebugger;

ByteCodeBreakpointContext(Debugger* debugger, InterpretedCodeBlock* codeBlock, bool addBreakpointLocationsInfoToDebugger = true);
~ByteCodeBreakpointContext();

MAKE_STACK_ALLOCATED();
};
#endif /* ESCARGOT_DEBUGGER */

Expand Down
5 changes: 5 additions & 0 deletions test/cctest/testapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,7 @@ TEST(FunctionObject, Consturct)
Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, FunctionObjectRef* fn) -> ValueRef* {
ObjectRef* obj = fn->construct(state, 0, nullptr)->asObject();
EXPECT_TRUE(obj->instanceOf(state, fn));
return ValueRef::createUndefined();
},
fn);
}
Expand Down Expand Up @@ -2873,6 +2874,8 @@ TEST(Debugger, Basic)
EXPECT_EQ(debuggerParseErrorCount, 1);
EXPECT_EQ(debuggerTest->stopAtBreakpointCount, 5);

context->disableDebugger();

context.release();
instance.release();
}
Expand Down Expand Up @@ -2985,6 +2988,8 @@ TEST(Debugger, ObjectStore)
source = StringRef::createFromUTF8(debuggerSourceString2, sizeof(debuggerSourceString2) - 1);
evalScript(context, source, fileName, false);

context->disableDebugger();

context.release();
instance.release();
}

0 comments on commit a57df75

Please sign in to comment.