fbchisellldbbase.py (146 lines of code) (raw):

#!/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 json import shlex import lldb class FBCommandArgument: # noqa B903 def __init__( self, short="", long="", arg="", type="", help="", default="", boolean=False ): self.shortName = short self.longName = long self.argName = arg self.argType = type self.help = help self.default = default self.boolean = boolean class FBCommand: def name(self): return None def options(self): return [] def args(self): return [] def description(self): return "" def lex(self, commandLine): return shlex.split(commandLine) def run(self, arguments, option): pass def isSuccess(error): # When evaluating a `void` expression, the returned value will indicate an # error. This error is named: kNoResult. This error value does *not* mean # there was a problem. This logic follows what the builtin `expression` # command does. See: https://git.io/vwpjl (UserExpression.h) kNoResult = 0x1001 return error.success or error.value == kNoResult def importModule(frame, module): options = lldb.SBExpressionOptions() options.SetLanguage(lldb.eLanguageTypeObjC) value = frame.EvaluateExpression("@import " + module, options) return isSuccess(value.error) # evaluates expression in Objective-C++ context, so it will work even for # Swift projects def evaluateExpressionValue( expression, printErrors=True, language=lldb.eLanguageTypeObjC_plus_plus, tryAllThreads=False, ): frame = ( lldb.debugger.GetSelectedTarget() .GetProcess() .GetSelectedThread() .GetSelectedFrame() ) options = lldb.SBExpressionOptions() options.SetLanguage(language) # Allow evaluation that contains a @throw/@catch. # By default, ObjC @throw will cause evaluation to be aborted. At the time # of a @throw, it's not known if the exception will be handled by a @catch. # An exception that's caught, should not cause evaluation to fail. options.SetTrapExceptions(False) # Give evaluation more time. options.SetTimeoutInMicroSeconds(5000000) # 5s # Most Chisel commands are not multithreaded. options.SetTryAllThreads(tryAllThreads) value = frame.EvaluateExpression(expression, options) error = value.GetError() # Retry if the error could be resolved by first importing UIKit. if ( error.type == lldb.eErrorTypeExpression and error.value == lldb.eExpressionParseError and importModule(frame, "UIKit") ): value = frame.EvaluateExpression(expression, options) error = value.GetError() if printErrors and not isSuccess(error): print(error) return value def evaluateInputExpression(expression, printErrors=True): # HACK if expression.startswith("(id)"): return evaluateExpressionValue(expression, printErrors=printErrors).GetValue() frame = ( lldb.debugger.GetSelectedTarget() .GetProcess() .GetSelectedThread() .GetSelectedFrame() ) options = lldb.SBExpressionOptions() options.SetTrapExceptions(False) value = frame.EvaluateExpression(expression, options) error = value.GetError() if printErrors and error.Fail(): print(error) return value.GetValue() def evaluateIntegerExpression(expression, printErrors=True): output = evaluateExpression("(int)(" + expression + ")", printErrors).replace( "'", "" ) if output.startswith("\\x"): # Booleans may display as \x01 (Hex) output = output[2:] elif output.startswith("\\"): # Or as \0 (Dec) output = output[1:] return int(output, 0) def evaluateBooleanExpression(expression, printErrors=True): return ( int(evaluateIntegerExpression("(BOOL)(" + expression + ")", printErrors)) != 0 ) def evaluateExpression(expression, printErrors=True): return evaluateExpressionValue(expression, printErrors=printErrors).GetValue() def describeObject(expression, printErrors=True): return evaluateExpressionValue( "(id)(" + expression + ")", printErrors ).GetObjectDescription() def evaluateEffect(expression, printErrors=True): evaluateExpressionValue("(void)(" + expression + ")", printErrors=printErrors) def evaluateObjectExpression(expression, printErrors=True): return evaluateExpression("(id)(" + expression + ")", printErrors) def evaluateCStringExpression(expression, printErrors=True): ret = evaluateExpression(expression, printErrors) process = lldb.debugger.GetSelectedTarget().GetProcess() error = lldb.SBError() ret = process.ReadCStringFromMemory(int(ret, 16), 256, error) if error.Success(): return ret else: if printErrors: print(error) return None RETURN_MACRO = """ #define IS_JSON_OBJ(obj)\ (obj != nil && ((bool)[NSJSONSerialization isValidJSONObject:obj] ||\ (bool)[obj isKindOfClass:[NSString class]] ||\ (bool)[obj isKindOfClass:[NSNumber class]])) #define RETURN(ret) ({\ if (!IS_JSON_OBJ(ret)) {\ (void)[NSException raise:@"Invalid RETURN argument" format:@""];\ }\ NSDictionary *__dict = @{@"return":ret};\ NSData *__data = (id)[NSJSONSerialization dataWithJSONObject:__dict options:0 error:NULL];\ NSString *__str = (id)[[NSString alloc] initWithData:__data encoding:4];\ (char *)[__str UTF8String];}) #define RETURNCString(ret)\ ({NSString *___cstring_ret = [NSString stringWithUTF8String:ret];\ RETURN(___cstring_ret);}) """ def check_expr(expr): return expr.strip().split(";")[-2].find("RETURN") != -1 # evaluate a batch of Objective-C expressions, the last expression # must contain a RETURN marco and it will automatic transform the # Objective-C object to Python object # Example: # >>> fbchisellldbbase.evaluate('NSString *str = @"hello world"; RETURN(@{@"key": str});') # {u'key': u'hello world'} def evaluate(expr): if not check_expr(expr): raise Exception( "Invalid Expression, the last expression not include a RETURN family marco" ) command = "({" + RETURN_MACRO + "\n" + expr + "})" ret = evaluateExpressionValue(command, printErrors=True) if not ret.GetError().Success(): print(ret.GetError()) return None else: process = lldb.debugger.GetSelectedTarget().GetProcess() error = lldb.SBError() ret = process.ReadCStringFromMemory(int(ret.GetValue(), 16), 2 ** 20, error) if not error.Success(): print(error) return None else: ret = json.loads(ret) return ret["return"] def currentLanguage(): return ( lldb.debugger.GetSelectedTarget() .GetProcess() .GetSelectedThread() .GetSelectedFrame() .GetCompileUnit() .GetLanguage() )