content: #!/usr/bin/python
  
  # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
  #
  # This source code is licensed under the MIT license found in the
  # LICENSE file in the root directory of this source tree.
  
  import re
  
  import fbchisellldbbase as fb
  import lldb
  
  
  def lldbcommands():
      return [FBPrintInvocation()]
  
  
  class FBPrintInvocation(fb.FBCommand):
      def name(self):
          return "pinvocation"
  
      def description(self):
          return "Print the stack frame, receiver, and arguments of the current invocation. It will fail to print all arguments if any arguments are variadic (varargs).\n\nNOTE: Sadly this is currently only implemented on x86."
  
      def options(self):
          return [
              fb.FBCommandArgument(
                  short="-a",
                  long="--all",
                  arg="all",
                  default=False,
                  boolean=True,
                  help="Specify to print the entire stack instead of just the current frame.",
              )
          ]
  
      def run(self, arguments, options):
          target = lldb.debugger.GetSelectedTarget()
  
          if not re.match(r".*i386.*", target.GetTriple()):
              print("Only x86 is currently supported (32-bit iOS Simulator or Mac OS X).")
              return
  
          thread = target.GetProcess().GetSelectedThread()
  
          if options.all:
              for frame in thread:
                  printInvocationForFrame(frame)
                  print("---------------------------------")
          else:
              frame = thread.GetSelectedFrame()
              printInvocationForFrame(frame)
  
  
  def printInvocationForFrame(frame):
      print(frame)
  
      symbolName = frame.GetSymbol().GetName()
      if not re.match(r"[-+]\s*\[.*\]", symbolName):
          return
  
      self = findArgAtIndexFromStackFrame(frame, 0)
      cmd = findArgAtIndexFromStackFrame(frame, 1)
  
      commandForSignature = (
          "[(id)"
          + self
          + " methodSignatureForSelector:(char *)sel_getName((SEL)"
          + cmd
          + ")]"
      )
      signatureValue = fb.evaluateExpressionValue("(id)" + commandForSignature)
  
      if (
          signatureValue.GetError() is not None
          and str(signatureValue.GetError()) != "success"
      ):
          print(
              "My sincerest apologies. I couldn't find a method signature for the selector."
          )
          return
  
      signature = signatureValue.GetValue()
  
      arg0 = stackStartAddressInSelectedFrame(frame)
      commandForInvocation = (
          "[NSInvocation _invocationWithMethodSignature:(id)"
          + signature
          + " frame:((void *)"
          + str(arg0)
          + ")]"
      )
      invocation = fb.evaluateExpression("(id)" + commandForInvocation)
  
      if invocation:
          prettyPrintInvocation(frame, invocation)
      else:
          print(frame)
  
  
  def stackStartAddressInSelectedFrame(frame):
      # Determine if the %ebp register has already had the
      # stack register pushed into it (always the first instruction)
      frameSymbol = frame.GetSymbolContext(0).GetSymbol()
      frameStartAddress = frameSymbol.GetStartAddress().GetLoadAddress(
          lldb.debugger.GetSelectedTarget()
      )
  
      currentPC = frame.GetPC()
  
      offset = currentPC - frameStartAddress
  
      if offset == 0:
          return int(frame.EvaluateExpression("($esp + 4)").GetValue())
      elif offset == 1:
          return int(frame.EvaluateExpression("($esp + 8)").GetValue())
      else:
          return int(frame.EvaluateExpression("($ebp + 8)").GetValue())
  
  
  def findArgAtIndexFromStackFrame(frame, index):
      return fb.evaluateExpression(
          "*(int *)" + str(findArgAdressAtIndexFromStackFrame(frame, index))
      )
  
  
  def findArgAdressAtIndexFromStackFrame(frame, index):
      arg0 = stackStartAddressInSelectedFrame(frame)
      arg = arg0 + 4 * index
      return arg
  
  
  def prettyPrintInvocation(frame, invocation):
      object = fb.evaluateExpression("(id)[(id)" + invocation + " target]")
      description = fb.evaluateExpressionValue("(id)" + invocation).GetObjectDescription()
      argDescriptions = description.splitlines(True)[4:]
  
      print("NSInvocation: " + invocation)
      print("self: " + fb.evaluateExpression("(id)" + object))
  
      if len(argDescriptions) > 0:
          print(
              "\n" + str(len(argDescriptions)) + " Arguments:"
              if len(argDescriptions) > 1
              else "\nArgument:"
          )
  
          index = 2
          for argDescription in argDescriptions:
              s = re.sub(r"argument [0-9]+: ", "", argDescription)
  
              address = findArgAdressAtIndexFromStackFrame(frame, index)
  
              encoding = s.split(" ")[0]
              description = " ".join(s.split(" ")[1:])
  
              readableString = argumentAsString(frame, address, encoding)
  
              if readableString:
                  print(readableString)
              else:
                  if encoding[0] == "{":
                      encoding = encoding[1:]
                  print(
                      (
                          hex(address) + ", address of " + encoding + " " + description
                      ).strip()
                  )
  
              index += 1
  
  
  def argumentAsString(frame, address, encoding):  # noqa C901
      if encoding[0] == "{":
          encoding = encoding[1:]
  
      encodingMap = {
          "c": "char",
          "i": "int",
          "s": "short",
          "l": "long",
          "q": "long long",
          "C": "unsigned char",
          "I": "unsigned int",
          "S": "unsigned short",
          "L": "unsigned long",
          "Q": "unsigned long long",
          "f": "float",
          "d": "double",
          "B": "bool",
          "v": "void",
          "*": "char *",
          "@": "id",
          "#": "Class",
          ":": "SEL",
      }
  
      pointers = ""
      while encoding[0] == "^":
          pointers += "*"
          encoding = encoding[1:]
  
      type = None
      if encoding in encodingMap:
          type = encodingMap[encoding]
  
      if type and pointers:
          type = type + " " + pointers
  
      if not type:
          # Handle simple structs: {CGPoint=ff}, {CGSize=ff},
          # {CGRect={CGPoint=ff}{CGSize=ff}}
          if encoding[0] == "{":
              encoding = encoding[1:]
  
          type = re.sub(r"=.*", "", encoding)
          if pointers:
              type += " " + pointers
  
      if type:
          value = frame.EvaluateExpression("*(" + type + " *)" + str(address))
  
          if value.GetError() is None or str(value.GetError()) == "success":
              description = None
  
              if encoding == "@":
                  description = value.GetObjectDescription()
  
              if not description:
                  description = value.GetValue()
              if not description:
                  description = value.GetSummary()
              if description:
                  return type + ": " + description
  
      return None
  