Skip to content

Commit 3440518

Browse files
committed
Add new force_gc command.
1 parent 8c775c4 commit 3440518

File tree

4 files changed

+104
-4
lines changed

4 files changed

+104
-4
lines changed

LLDBPlugin/lldb/__init__.pyi

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5292,7 +5292,7 @@ class SBInstruction:
52925292
r"""GetMnemonic(SBInstruction self, SBTarget target) -> char const *"""
52935293
...
52945294

5295-
def GetOperands(self, target):
5295+
def GetOperands(self, target) -> string:
52965296
r"""GetOperands(SBInstruction self, SBTarget target) -> char const *"""
52975297
...
52985298

@@ -5445,7 +5445,7 @@ class SBInstructionList:
54455445
r"""__repr__(SBInstructionList self) -> std::string"""
54465446
...
54475447

5448-
def __iter__(self): # -> Generator[Any, Any, None]:
5448+
def __iter__(self) -> Iterator[SBInstruction]: # -> Generator[Any, Any, None]:
54495449
'''Iterate over all instructions in a lldb.SBInstructionList
54505450
object.'''
54515451
...
@@ -5454,7 +5454,7 @@ class SBInstructionList:
54545454
'''Access len of the instruction list.'''
54555455
...
54565456

5457-
def __getitem__(self, key): # -> None:
5457+
def __getitem__(self, key) -> SBInstruction:
54585458
'''Access instructions by integer index for array access or by lldb.SBAddress to find an instruction that matches a section offset address object.'''
54595459
...
54605460

LLDBPlugin/touchlab_kotlin_lldb/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from .stepping.KonanHook import KonanHook
77
from .types.base import KOTLIN_CATEGORY, KOTLIN_OBJ_HEADER_TYPE, KOTLIN_ARRAY_HEADER_TYPE
88
from .util.log import log
9-
from .commands import FieldTypeCommand, SymbolByNameCommand, TypeByAddressCommand
9+
from .commands import FieldTypeCommand, SymbolByNameCommand, TypeByAddressCommand, GCCollectCommand
1010

1111
from .types.summary import kotlin_object_type_summary, kotlin_objc_class_summary
1212
from .types.proxy import KonanProxyTypeProvider, KonanObjcProxyTypeProvider
@@ -141,6 +141,7 @@ def register_commands(debugger: lldb.SBDebugger):
141141
FieldTypeCommand,
142142
SymbolByNameCommand,
143143
TypeByAddressCommand,
144+
GCCollectCommand,
144145
]
145146

146147
for command in commands_to_register:
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
from typing import Optional
2+
3+
from lldb import SBDebugger, SBExecutionContext, SBCommandReturnObject, SBTarget, SBSymbol, SBInstructionList
4+
from touchlab_kotlin_lldb.util import evaluate, DebuggerException
5+
6+
import re
7+
8+
9+
class GCCollectCommand:
10+
program = 'force_gc'
11+
12+
def __init__(self, debugger, unused):
13+
pass
14+
15+
def __call__(self, debugger: SBDebugger, command, exe_ctx: SBExecutionContext, result: SBCommandReturnObject):
16+
try:
17+
target = debugger.GetSelectedTarget()
18+
schedule_gc_function = self._find_single_function_symbol('kotlin::gcScheduler::GCScheduler::scheduleAndWaitFinalized()', target, result)
19+
deinit_memory_function = self._find_single_function_symbol('DeinitMemory', target, result)
20+
global_data_symbol = self._find_single_symbol("(anonymous namespace)::globalDataInstance", target, result)
21+
22+
gc_scheduler_offset = self._find_gc_scheduler_offset(deinit_memory_function, schedule_gc_function, target)
23+
24+
schedule_gc_function_addr = schedule_gc_function.addr.GetLoadAddress(target)
25+
global_data_addr = global_data_symbol.addr.GetLoadAddress(target)
26+
gc_scheduler_addr = global_data_addr + gc_scheduler_offset
27+
28+
evaluate(
29+
'((void (*)(void*)){:#x})((void*){:#x})',
30+
schedule_gc_function_addr,
31+
gc_scheduler_addr
32+
)
33+
evaluate(
34+
'((void (*)(void*)){:#x})((void*){:#x})',
35+
schedule_gc_function_addr,
36+
gc_scheduler_addr
37+
)
38+
39+
except DebuggerException as e:
40+
result.SetError("{} Please report this to the xcode-kotlin GitHub.".format(e.msg))
41+
return
42+
43+
44+
@staticmethod
45+
def _find_single_function_symbol(symbol_name: str, target: SBTarget, result: SBCommandReturnObject) -> SBSymbol:
46+
functions = target.FindFunctions(symbol_name)
47+
if functions.GetSize() >= 1:
48+
if not functions.GetSize() == 1:
49+
result.AppendWarning("Multiple ({}) symbols found for function {}".format(functions.GetSize(), symbol_name))
50+
return functions[0].GetSymbol()
51+
else:
52+
raise DebuggerException("Could not find symbol for function {}.".format(symbol_name))
53+
54+
@staticmethod
55+
def _find_single_symbol(symbol_name: str, target: SBTarget, result: SBCommandReturnObject) -> SBSymbol:
56+
symbols = target.FindSymbols(symbol_name)
57+
if symbols.GetSize() >= 1:
58+
if not symbols.GetSize() == 1:
59+
result.AppendWarning(
60+
"Multiple ({}) symbols found for function {}".format(symbols.GetSize(), symbol_name))
61+
return symbols[0].GetSymbol()
62+
else:
63+
raise DebuggerException("Could not find symbol for function {}.".format(symbol_name))
64+
65+
@staticmethod
66+
def _find_gc_scheduler_offset(deinit_memory: SBSymbol, schedule_gc: SBSymbol, target: SBTarget) -> int:
67+
instructions = deinit_memory.GetInstructions(target)
68+
load_addr = "{:#x}".format(schedule_gc.addr.GetLoadAddress(target))
69+
70+
previous_branch_instruction_index: int = 0
71+
schedule_gc_branch_instruction_index: Optional[int] = None
72+
for i in range(len(instructions)):
73+
instruction = instructions[i]
74+
if instruction.DoesBranch():
75+
if instruction.GetOperands(target) == load_addr:
76+
schedule_gc_branch_instruction_index = i
77+
break
78+
else:
79+
previous_branch_instruction_index = i
80+
81+
if not schedule_gc_branch_instruction_index:
82+
raise DebuggerException(
83+
"Could not find a branch instruction to {} inside {}.".format(
84+
schedule_gc.GetDisplayName(), deinit_memory.GetDisplayName()))
85+
86+
match_pattern = "\\(anonymous namespace\\)::globalDataInstance\\s+\\+\\s+(\\d+)"
87+
gc_scheduler_offset: Optional[int] = None
88+
for i in range(previous_branch_instruction_index, schedule_gc_branch_instruction_index):
89+
instruction = instructions[i]
90+
match = re.search(match_pattern, instruction.GetComment(target))
91+
if match:
92+
gc_scheduler_offset = int(match.group(1))
93+
break
94+
95+
if not gc_scheduler_offset:
96+
raise DebuggerException("Could not find gc_scheduler offset for globalDataInstance.")
97+
98+
return gc_scheduler_offset

LLDBPlugin/touchlab_kotlin_lldb/commands/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
from .TypeByAddressCommand import TypeByAddressCommand
33
from .SymbolByNameCommand import SymbolByNameCommand
44
from .KonanGlobalsCommand import KonanGlobalsCommand
5+
from .GCCollectCommand import GCCollectCommand

0 commit comments

Comments
 (0)