parameters: (debugger, command, result, internal_dict)
docstring: A command to diagnose the LLDB NSString data formatter
      invoke as
      (lldb) diagnose-nsstring <expr returning NSString>
      e.g.
      (lldb) diagnose-nsstring @"Hello world"
docstring_summary: A command to diagnose the LLDB NSString data formatter
      invoke as
      (lldb) diagnose-nsstring <expr returning NSString>
      e.g.
      (lldb) diagnose-nsstring
docstring_tokens: A command to diagnose the LLDB NSString data formatter invoke as ( lldb ) diagnose - nsstring <expr returning NSString > e . g . ( lldb ) diagnose - nsstring
function: def diagnose_nsstring_Command_Impl(debugger, command, result, internal_dict):
      """
      A command to diagnose the LLDB NSString data formatter
      invoke as
      (lldb) diagnose-nsstring <expr returning NSString>
      e.g.
      (lldb) diagnose-nsstring @"Hello world"
      """
      target = debugger.GetSelectedTarget()
      process = target.GetProcess()
      thread = process.GetSelectedThread()
      frame = thread.GetSelectedFrame()
      if not target.IsValid() or not process.IsValid():
          return "unable to get target/process - cannot proceed"
      options = lldb.SBExpressionOptions()
      options.SetFetchDynamicValue()
      error = lldb.SBError()
      if frame.IsValid():
          nsstring = frame.EvaluateExpression(command, options)
      else:
          nsstring = target.EvaluateExpression(command, options)
      print(str(nsstring), file=result)
      nsstring_address = nsstring.GetValueAsUnsigned(0)
      if nsstring_address == 0:
          return "unable to obtain the string - cannot proceed"
      expression = "\
  struct $__lldb__notInlineMutable {\
      char* buffer;\
      signed long length;\
      signed long capacity;\
      unsigned int hasGap:1;\
      unsigned int isFixedCapacity:1;\
      unsigned int isExternalMutable:1;\
      unsigned int capacityProvidedExternally:1;\n\
  #if __LP64__\n\
      unsigned long desiredCapacity:60;\n\
  #else\n\
      unsigned long desiredCapacity:28;\n\
  #endif\n\
      void* contentsAllocator;\
  };\
  \
  struct $__lldb__CFString {\
      void* _cfisa;\
      uint8_t _cfinfo[4];\
      uint32_t _rc;\
      union {\
          struct __inline1 {\
              signed long length;\
          } inline1;\
          struct __notInlineImmutable1 {\
              char* buffer;\
              signed long length;\
              void* contentsDeallocator;\
          } notInlineImmutable1;\
          struct __notInlineImmutable2 {\
              char* buffer;\
              void* contentsDeallocator;\
          } notInlineImmutable2;\
          struct $__lldb__notInlineMutable notInlineMutable;\
      } variants;\
  };\
  "
  
      expression = expression + "*(($__lldb__CFString*) %d)" % nsstring_address
      # print expression
      dumped = target.EvaluateExpression(expression, options)
      print(str(dumped), file=result)
  
      little_endian = (target.byte_order == lldb.eByteOrderLittle)
      ptr_size = target.addr_size
  
      info_bits = dumped.GetChildMemberWithName("_cfinfo").GetChildAtIndex(
          0 if little_endian else 3).GetValueAsUnsigned(0)
      is_mutable = (info_bits & 1) == 1
      is_inline = (info_bits & 0x60) == 0
      has_explicit_length = (info_bits & (1 | 4)) != 4
      is_unicode = (info_bits & 0x10) == 0x10
      is_special = (
          nsstring.GetDynamicValue(
              lldb.eDynamicCanRunTarget).GetTypeName() == "NSPathStore2")
      has_null = (info_bits & 8) == 8
  
      print("\nInfo=%d\nMutable=%s\nInline=%s\nExplicit=%s\nUnicode=%s\nSpecial=%s\nNull=%s\n" % \
          (info_bits, "yes" if is_mutable else "no", "yes" if is_inline else "no", "yes" if has_explicit_length else "no", "yes" if is_unicode else "no", "yes" if is_special else "no", "yes" if has_null else "no"), file=result)
  
      explicit_length_offset = 0
      if not has_null and has_explicit_length and not is_special:
          explicit_length_offset = 2 * ptr_size
          if is_mutable and not is_inline:
              explicit_length_offset = explicit_length_offset + ptr_size
          elif is_inline:
              pass
          elif not is_inline and not is_mutable:
              explicit_length_offset = explicit_length_offset + ptr_size
          else:
              explicit_length_offset = 0
  
      if explicit_length_offset == 0:
          print("There is no explicit length marker - skipping this step\n", file=result)
      else:
          explicit_length_offset = nsstring_address + explicit_length_offset
          explicit_length = process.ReadUnsignedFromMemory(
              explicit_length_offset, 4, error)
          print("Explicit length location is at 0x%x - read value is %d\n" % (
              explicit_length_offset, explicit_length), file=result)
  
      if is_mutable:
          location = 2 * ptr_size + nsstring_address
          location = process.ReadPointerFromMemory(location, error)
      elif is_inline and has_explicit_length and not is_unicode and not is_special and not is_mutable:
          location = 3 * ptr_size + nsstring_address
      elif is_unicode:
          location = 2 * ptr_size + nsstring_address
          if is_inline:
              if not has_explicit_length:
                  print("Unicode & Inline & !Explicit is a new combo - no formula for it", file=result)
              else:
                  location += ptr_size
          else:
              location = process.ReadPointerFromMemory(location, error)
      elif is_special:
          location = nsstring_address + ptr_size + 4
      elif is_inline:
          location = 2 * ptr_size + nsstring_address
          if not has_explicit_length:
              location += 1
      else:
          location = 2 * ptr_size + nsstring_address
          location = process.ReadPointerFromMemory(location, error)
      print("Expected data location: 0x%x\n" % (location), file=result)
      print("1K of data around location: %s\n" % read_memory(
          process, location, 1024), file=result)
      print("5K of data around string pointer: %s\n" % read_memory(
          process, nsstring_address, 1024 * 5), file=result)
function_tokens: def diagnose_nsstring_Command_Impl ( debugger , command , result , internal_dict ) : target = debugger . GetSelectedTarget ( ) process = target . GetProcess ( ) thread = process . GetSelectedThread ( ) frame = thread . GetSelectedFrame ( ) if not target . IsValid ( ) or not process . IsValid ( ) : return "unable to get target/process - cannot proceed" options = lldb . SBExpressionOptions ( ) options . SetFetchDynamicValue ( ) error = lldb . SBError ( ) if frame . IsValid ( ) : nsstring = frame . EvaluateExpression ( command , options ) else : nsstring = target . EvaluateExpression ( command , options ) print ( str ( nsstring ) , file = result ) nsstring_address = nsstring . GetValueAsUnsigned ( 0 ) if nsstring_address == 0 : return "unable to obtain the string - cannot proceed" expression = "\
  struct $__lldb__notInlineMutable {\
      char* buffer;\
      signed long length;\
      signed long capacity;\
      unsigned int hasGap:1;\
      unsigned int isFixedCapacity:1;\
      unsigned int isExternalMutable:1;\
      unsigned int capacityProvidedExternally:1;\n\
  #if __LP64__\n\
      unsigned long desiredCapacity:60;\n\
  #else\n\
      unsigned long desiredCapacity:28;\n\
  #endif\n\
      void* contentsAllocator;\
  };\
  \
  struct $__lldb__CFString {\
      void* _cfisa;\
      uint8_t _cfinfo[4];\
      uint32_t _rc;\
      union {\
          struct __inline1 {\
              signed long length;\
          } inline1;\
          struct __notInlineImmutable1 {\
              char* buffer;\
              signed long length;\
              void* contentsDeallocator;\
          } notInlineImmutable1;\
          struct __notInlineImmutable2 {\
              char* buffer;\
              void* contentsDeallocator;\
          } notInlineImmutable2;\
          struct $__lldb__notInlineMutable notInlineMutable;\
      } variants;\
  };\
  " expression = expression + "*(($__lldb__CFString*) %d)" % nsstring_address # print expression dumped = target . EvaluateExpression ( expression , options ) print ( str ( dumped ) , file = result ) little_endian = ( target . byte_order == lldb . eByteOrderLittle ) ptr_size = target . addr_size info_bits = dumped . GetChildMemberWithName ( "_cfinfo" ) . GetChildAtIndex ( 0 if little_endian else 3 ) . GetValueAsUnsigned ( 0 ) is_mutable = ( info_bits & 1 ) == 1 is_inline = ( info_bits & 0x60 ) == 0 has_explicit_length = ( info_bits & ( 1 | 4 ) ) != 4 is_unicode = ( info_bits & 0x10 ) == 0x10 is_special = ( nsstring . GetDynamicValue ( lldb . eDynamicCanRunTarget ) . GetTypeName ( ) == "NSPathStore2" ) has_null = ( info_bits & 8 ) == 8 print ( "\nInfo=%d\nMutable=%s\nInline=%s\nExplicit=%s\nUnicode=%s\nSpecial=%s\nNull=%s\n" % ( info_bits , "yes" if is_mutable else "no" , "yes" if is_inline else "no" , "yes" if has_explicit_length else "no" , "yes" if is_unicode else "no" , "yes" if is_special else "no" , "yes" if has_null else "no" ) , file = result ) explicit_length_offset = 0 if not has_null and has_explicit_length and not is_special : explicit_length_offset = 2 * ptr_size if is_mutable and not is_inline : explicit_length_offset = explicit_length_offset + ptr_size elif is_inline : pass elif not is_inline and not is_mutable : explicit_length_offset = explicit_length_offset + ptr_size else : explicit_length_offset = 0 if explicit_length_offset == 0 : print ( "There is no explicit length marker - skipping this step\n" , file = result ) else : explicit_length_offset = nsstring_address + explicit_length_offset explicit_length = process . ReadUnsignedFromMemory ( explicit_length_offset , 4 , error ) print ( "Explicit length location is at 0x%x - read value is %d\n" % ( explicit_length_offset , explicit_length ) , file = result ) if is_mutable : location = 2 * ptr_size + nsstring_address location = process . ReadPointerFromMemory ( location , error ) elif is_inline and has_explicit_length and not is_unicode and not is_special and not is_mutable : location = 3 * ptr_size + nsstring_address elif is_unicode : location = 2 * ptr_size + nsstring_address if is_inline : if not has_explicit_length : print ( "Unicode & Inline & !Explicit is a new combo - no formula for it" , file = result ) else : location += ptr_size else : location = process . ReadPointerFromMemory ( location , error ) elif is_special : location = nsstring_address + ptr_size + 4 elif is_inline : location = 2 * ptr_size + nsstring_address if not has_explicit_length : location += 1 else : location = 2 * ptr_size + nsstring_address location = process . ReadPointerFromMemory ( location , error ) print ( "Expected data location: 0x%x\n" % ( location ) , file = result ) print ( "1K of data around location: %s\n" % read_memory ( process , location , 1024 ) , file = result ) print ( "5K of data around string pointer: %s\n" % read_memory ( process , nsstring_address , 1024 * 5 ) , file = result )