Skip to content

Commit a57df75

Browse files
clover2123ksh8281
authored andcommitted
Fix memory leaks in Debugger
* shared structure `BreakpointLocationsInfo` between debuggger and ByteCodeBlock can cause memory leaks * correctly delete each `BreakpointLocationsInfo` Signed-off-by: HyukWoo Park <[email protected]>
1 parent 19498b4 commit a57df75

File tree

7 files changed

+62
-6
lines changed

7 files changed

+62
-6
lines changed

src/api/EscargotPublic.cpp

+28-1
Original file line numberDiff line numberDiff line change
@@ -1718,6 +1718,7 @@ class DebuggerC : public Debugger {
17181718
virtual void consoleOut(String* output) override;
17191719
virtual String* getClientSource(String** sourceName) override;
17201720
virtual bool getWaitBeforeExitClient() override;
1721+
virtual void deleteClient() override;
17211722

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

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

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

1855+
void DebuggerC::deleteClient()
1856+
{
1857+
if (m_debuggerClient) {
1858+
// delete DebuggerClient that was created and delivered from the user
1859+
delete m_debuggerClient;
1860+
m_debuggerClient = nullptr;
1861+
}
1862+
}
1863+
18501864
bool DebuggerC::processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest)
18511865
{
18521866
UNUSED_PARAMETER(state);
@@ -3093,6 +3107,19 @@ bool ContextRef::initDebugger(DebuggerOperationsRef::DebuggerClient* debuggerCli
30933107
#endif /* ESCARGOT_DEBUGGER */
30943108
}
30953109

3110+
bool ContextRef::disableDebugger()
3111+
{
3112+
#ifdef ESCARGOT_DEBUGGER
3113+
Context* context = toImpl(this);
3114+
if (context->debugger()) {
3115+
context->debugger()->deleteClient();
3116+
}
3117+
return true;
3118+
#else
3119+
return false;
3120+
#endif
3121+
}
3122+
30963123
bool ContextRef::initDebuggerRemote(const char* options)
30973124
{
30983125
#ifdef ESCARGOT_DEBUGGER

src/api/EscargotPublic.h

+2
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,7 @@ class ESCARGOT_EXPORT DebuggerOperationsRef {
870870
// Base class for debugger callbacks
871871
class DebuggerClient {
872872
public:
873+
virtual ~DebuggerClient(){};
873874
virtual void parseCompleted(StringRef* source, StringRef* srcName, std::vector<DebuggerOperationsRef::BreakpointLocationsInfo*>& breakpointLocationsVector) = 0;
874875
virtual void parseError(StringRef* source, StringRef* srcName, StringRef* error) = 0;
875876
virtual void codeReleased(WeakCodeRef* weakCodeRef) = 0;
@@ -900,6 +901,7 @@ class ESCARGOT_EXPORT ContextRef {
900901
void throwException(ValueRef* exceptionValue);
901902

902903
bool initDebugger(DebuggerOperationsRef::DebuggerClient* debuggerClient);
904+
bool disableDebugger();
903905
// available options(separator is ';')
904906
// "--port=6501", default for TCP debugger
905907
bool initDebuggerRemote(const char* options);

src/debugger/Debugger.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class InterpretedCodeBlock;
4646

4747
class Debugger : public gc {
4848
public:
49-
// The following code is the sam as in EscargotPublic.h
49+
// The following code is the same as in EscargotPublic.h
5050
class WeakCodeRef;
5151

5252
struct BreakpointLocation {
@@ -108,6 +108,10 @@ class Debugger : public gc {
108108

109109
void clearParsingData()
110110
{
111+
for (size_t i = 0; i < m_breakpointLocationsVector.size(); i++) {
112+
// delete each shared Debugger::BreakpointLocationsInfo here
113+
delete m_breakpointLocationsVector[i];
114+
}
111115
m_breakpointLocationsVector.clear();
112116
}
113117

@@ -163,6 +167,7 @@ class Debugger : public gc {
163167
virtual void consoleOut(String* output) = 0;
164168
virtual String* getClientSource(String** sourceName) = 0;
165169
virtual bool getWaitBeforeExitClient() = 0;
170+
virtual void deleteClient() {}
166171
virtual bool skipSourceCode(String* srcName) const
167172
{
168173
return false;

src/interpreter/ByteCode.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,11 @@ ByteCodeBlock::ByteCodeBlock()
111111
static void clearByteCodeBlock(ByteCodeBlock* self)
112112
{
113113
#ifdef ESCARGOT_DEBUGGER
114-
Debugger* debugger = self->codeBlock()->context()->debugger();
115-
if (debugger != nullptr && self->codeBlock()->markDebugging()) {
116-
debugger->byteCodeReleaseNotification(self);
114+
if (!self->m_isOwnerMayFreed) {
115+
Debugger* debugger = self->codeBlock()->context()->debugger();
116+
if (debugger != nullptr && self->codeBlock()->markDebugging()) {
117+
debugger->byteCodeReleaseNotification(self);
118+
}
117119
}
118120
#endif
119121
self->m_code.clear();

src/interpreter/ByteCodeGenerator.cpp

+12-1
Original file line numberDiff line numberDiff line change
@@ -210,11 +210,22 @@ ByteCodeBreakpointContext::ByteCodeBreakpointContext(Debugger* debugger, Interpr
210210
: m_lastBreakpointLineOffset(0)
211211
, m_lastBreakpointIndexOffset(0)
212212
, m_originSourceLineOffset(codeBlock->script()->originSourceLineOffset())
213-
, m_breakpointLocations()
213+
, m_breakpointLocations(nullptr)
214+
, m_sharedWithDebugger(false)
214215
{
215216
m_breakpointLocations = new Debugger::BreakpointLocationsInfo(reinterpret_cast<Debugger::WeakCodeRef*>(codeBlock));
216217
if (debugger && codeBlock->markDebugging() && addBreakpointLocationsInfoToDebugger) {
217218
debugger->appendBreakpointLocations(m_breakpointLocations);
219+
m_sharedWithDebugger = true;
220+
}
221+
}
222+
223+
ByteCodeBreakpointContext::~ByteCodeBreakpointContext()
224+
{
225+
if (!m_sharedWithDebugger) {
226+
// directly delete each BreakpointLocationsInfo
227+
// because BreakpointLocationsInfo is not shared with Debugger
228+
delete m_breakpointLocations;
218229
}
219230
}
220231
#endif /* ESCARGOT_DEBUGGER */

src/interpreter/ByteCodeGenerator.h

+4
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,12 @@ struct ByteCodeBreakpointContext {
5858
size_t m_originSourceLineOffset; // original source's start line offset
5959
Debugger::BreakpointLocationsInfo* m_breakpointLocations;
6060
bool m_parsingEnabled;
61+
bool m_sharedWithDebugger;
6162

6263
ByteCodeBreakpointContext(Debugger* debugger, InterpretedCodeBlock* codeBlock, bool addBreakpointLocationsInfoToDebugger = true);
64+
~ByteCodeBreakpointContext();
65+
66+
MAKE_STACK_ALLOCATED();
6367
};
6468
#endif /* ESCARGOT_DEBUGGER */
6569

test/cctest/testapi.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@ TEST(FunctionObject, Consturct)
665665
Evaluator::execute(g_context.get(), [](ExecutionStateRef* state, FunctionObjectRef* fn) -> ValueRef* {
666666
ObjectRef* obj = fn->construct(state, 0, nullptr)->asObject();
667667
EXPECT_TRUE(obj->instanceOf(state, fn));
668+
return ValueRef::createUndefined();
668669
},
669670
fn);
670671
}
@@ -2873,6 +2874,8 @@ TEST(Debugger, Basic)
28732874
EXPECT_EQ(debuggerParseErrorCount, 1);
28742875
EXPECT_EQ(debuggerTest->stopAtBreakpointCount, 5);
28752876

2877+
context->disableDebugger();
2878+
28762879
context.release();
28772880
instance.release();
28782881
}
@@ -2985,6 +2988,8 @@ TEST(Debugger, ObjectStore)
29852988
source = StringRef::createFromUTF8(debuggerSourceString2, sizeof(debuggerSourceString2) - 1);
29862989
evalScript(context, source, fileName, false);
29872990

2991+
context->disableDebugger();
2992+
29882993
context.release();
29892994
instance.release();
29902995
}

0 commit comments

Comments
 (0)