diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 65f20210323eb..1cab5393eaa15 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -15,7 +15,7 @@ from rust_types import is_tuple_fields if TYPE_CHECKING: - from lldb import SBValue, SBType, SBTypeStaticField, SBTarget + from lldb import SBValue, SBType, SBTypeStaticField, SBTarget, SBProcess # from lldb.formatters import Logger @@ -289,6 +289,28 @@ def vec_to_string(vec: SBValue) -> str: ) +def read_string( + process: SBProcess, address: int, length: int, error: Optional[SBError] = None +) -> str: + """Reads a string from running process's memory. If `error` is passed in, it will be passed + to the `SBProcess.ReadMemory` call, and will reflect any errors after the function is called. + + If any error or exception occurs, a placeholder byte array of the form "" will + be returned instead.""" + + if error is None: + error = SBError() + try: + data = process.ReadMemory(address, length, error) + if error.Success(): + return '"' + data.decode("utf-8", "replace") + '"' + else: + return f"" + except Exception as e: + print(f"Unable to generate String summary: {e.__cause__}") + return "" + + def StdStringSummaryProvider(valobj: SBValue, dict: LLDBOpaque): inner_vec = ( valobj.GetNonSyntheticValue() @@ -305,16 +327,25 @@ def StdStringSummaryProvider(valobj: SBValue, dict: LLDBOpaque): ) length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned() + capacity = ( + inner_vec.GetChildMemberWithName("buf") + .GetChildMemberWithName("cap") + .GetValueAsUnsigned() + ) if length <= 0: return '""' - error = SBError() + + no_hi_bit_max: int = 1 << ((pointer.GetByteSize() * 8) - 1) + # technically length isn't a NoHighBit, but length should always be <= capacity + if length >= no_hi_bit_max or capacity >= no_hi_bit_max: + return "" + if pointer.GetValueAsUnsigned() == 0: + return "" + process = pointer.GetProcess() - data = process.ReadMemory(pointer.GetValueAsUnsigned(), length, error) - if error.Success(): - return '"' + data.decode("utf8", "replace") + '"' - else: - raise Exception("ReadMemory error: %s", error.GetCString()) + + return read_string(process, pointer.GetValueAsAddress(), length) def StdOsStringSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: @@ -363,15 +394,9 @@ def StdPathSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: data_ptr = valobj.GetChildMemberWithName("data_ptr") start = data_ptr.GetValueAsUnsigned() - error = SBError() process = data_ptr.GetProcess() - data = process.ReadMemory(start, length, error) - if PY3: - try: - data = data.decode(encoding="UTF-8") - except UnicodeDecodeError: - return "%r" % data - return '"%s"' % data + + return read_string(process, start, length) def sequence_formatter(output: str, valobj: SBValue, _dict: LLDBOpaque): @@ -443,6 +468,9 @@ def has_children(self) -> bool: class StdStringSyntheticProvider: def __init__(self, valobj: SBValue, _dict: LLDBOpaque): self.valobj = valobj + ptr_size = valobj.GetTarget().GetAddressByteSize() * 8 + self.no_hi_bit_max = 1 << (ptr_size - 1) + self.update() def update(self): @@ -454,7 +482,25 @@ def update(self): .GetChildMemberWithName("pointer") .GetChildMemberWithName("pointer") ) - self.length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned() + + self.capacity = ( + inner_vec.GetChildMemberWithName("buf") + .GetChildMemberWithName("cap") + .GetValueAsUnsigned() + ) + + # As of 4/18/2026, LLDB cannot accurately determine the difference between Some("") and None + # this just makes sure we're not trying to access data when the string is clearly in an + # invalid state. + if ( + self.capacity >= self.no_hi_bit_max + or self.data_ptr.GetValueAsUnsigned() == 0 + ): + self.capacity = 0 + self.length = 0 + else: + self.length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned() + self.element_type = self.data_ptr.GetType().GetPointeeType() def has_children(self) -> bool: @@ -928,6 +974,8 @@ def __init__(self, valobj: SBValue, _dict: LLDBOpaque): # logger >> "[StdVecSyntheticProvider] for " + str(valobj.GetName()) self.valobj = valobj self.element_type = None + ptr_size = valobj.GetTarget().GetAddressByteSize() * 8 + self.no_hi_bit_max = 1 << (ptr_size - 1) self.update() def num_children(self) -> int: @@ -949,15 +997,19 @@ def get_child_at_index(self, index: int) -> Optional[SBValue]: return element def update(self): - self.length = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() - self.buf = self.valobj.GetChildMemberWithName("buf").GetChildMemberWithName( - "inner" - ) - + buf: SBValue = self.valobj.GetChildMemberWithName("buf") self.data_ptr = unwrap_unique_or_non_null( - self.buf.GetChildMemberWithName("ptr") + buf.GetChildMemberWithName("inner").GetChildMemberWithName("ptr") ) + capacity: int = buf.GetChildMemberWithName("cap").GetValueAsUnsigned() + + if capacity >= self.no_hi_bit_max or self.data_ptr.GetValueAsUnsigned() == 0: + self.capacity = 0 + self.length = 0 + else: + self.length = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() + self.element_type = self.valobj.GetType().GetTemplateArgumentType(0) if not self.element_type.IsValid():