commands/FBVisualizationCommands.py (194 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 errno import os import time import fbchisellldbbase as fb import fbchisellldbobjecthelpers as objectHelpers import lldb def lldbcommands(): return [FBVisualizeCommand()] def _showImage(commandForImage): imageDirectory = "/tmp/xcode_debug_images/" imageName = time.strftime("%Y-%m-%d-%H-%M-%S", time.gmtime()) + ".png" imagePath = imageDirectory + imageName try: os.makedirs(imageDirectory) except OSError as e: if e.errno == errno.EEXIST and os.path.isdir(imageDirectory): pass else: raise toPNG = "(id)UIImagePNGRepresentation((id){})".format(commandForImage) imageDataAddress = fb.evaluateExpressionValue(toPNG, tryAllThreads=True).GetValue() imageBytesStartAddress = fb.evaluateExpression( "(void *)[(id)" + imageDataAddress + " bytes]" ) imageBytesLength = fb.evaluateExpression( "(NSUInteger)[(id)" + imageDataAddress + " length]" ) address = int(imageBytesStartAddress, 16) length = int(imageBytesLength) if not (address or length): print("Could not get image data.") return process = lldb.debugger.GetSelectedTarget().GetProcess() error = lldb.SBError() mem = process.ReadMemory(address, length, error) if error is not None and str(error) != "success": print(error) else: with open(imagePath, "wb") as imgFile: imgFile.write(mem) os.system("open " + imagePath) def _colorIsCGColorRef(color): color = "(CGColorRef)(" + color + ")" result = fb.evaluateExpressionValue( "(unsigned long)CFGetTypeID({color}) == (unsigned long)CGColorGetTypeID()".format( color=color ) ) if result.GetError() is not None and str(result.GetError()) != "success": print("got error: {}".format(result)) return False else: isCFColor = result.GetValueAsUnsigned() != 0 return isCFColor def _showColor(color): color = "(" + color + ")" colorToUse = color isCF = _colorIsCGColorRef(color) if isCF: colorToUse = "[[UIColor alloc] initWithCGColor:(CGColorRef){}]".format(color) else: isCI = objectHelpers.isKindOfClass(color, "CIColor") if isCI: colorToUse = "[UIColor colorWithCIColor:(CIColor *){}]".format(color) imageSize = 58 fb.evaluateEffect( "UIGraphicsBeginImageContextWithOptions((CGSize)CGSizeMake({imageSize}, {imageSize}), NO, 0.0)".format( imageSize=imageSize ) ) fb.evaluateEffect("[(id){} setFill]".format(colorToUse)) fb.evaluateEffect( "UIRectFill((CGRect)CGRectMake(0.0, 0.0, {imageSize}, {imageSize}))".format( imageSize=imageSize ) ) result = fb.evaluateExpressionValue( "(UIImage *)UIGraphicsGetImageFromCurrentImageContext()" ) if result.GetError() is not None and str(result.GetError()) != "success": print("got error {}".format(result)) print(result.GetError()) else: image = result.GetValue() _showImage(image) fb.evaluateEffect("UIGraphicsEndImageContext()") def _showLayer(layer): layer = "(" + layer + ")" size = "((CGRect)[(id)" + layer + " bounds]).size" width = float(fb.evaluateExpression("(CGFloat)(" + size + ".width)")) height = float(fb.evaluateExpression("(CGFloat)(" + size + ".height)")) if width == 0.0 or height == 0.0: print( "Nothing to see here - the size of this element is {} x {}.".format( width, height ) ) return fb.evaluateEffect("UIGraphicsBeginImageContextWithOptions(" + size + ", NO, 0.0)") fb.evaluateEffect( "[(id)" + layer + " renderInContext:(void *)UIGraphicsGetCurrentContext()]" ) result = fb.evaluateExpressionValue( "(UIImage *)UIGraphicsGetImageFromCurrentImageContext()" ) if result.GetError() is not None and str(result.GetError()) != "success": print(result.GetError()) else: image = result.GetValue() _showImage(image) fb.evaluateEffect("UIGraphicsEndImageContext()") def _showPixelBuffer(target): fb.evaluateExpression("CGImageRef $imageOut = NULL") fb.evaluateExpression( "(OSStatus)VTCreateCGImageFromCVPixelBuffer(" + target + ", NULL, &$imageOut)" ) image = fb.evaluateExpression("[UIImage imageWithCGImage:$imageOut]") _showImage(image) fb.evaluateExpression("CGImageRelease($imageOut)") def _dataIsImage(data): data = "(" + data + ")" result = fb.evaluateExpressionValue("(id)[UIImage imageWithData:" + data + "]") if result.GetError() is not None and str(result.GetError()) != "success": return False else: isImage = result.GetValueAsUnsigned() != 0 return isImage def _dataIsString(data): data = "(" + data + ")" result = fb.evaluateExpressionValue( "(NSString*)[[NSString alloc] initWithData:" + data + " encoding:4]" ) if result.GetError() is not None and str(result.GetError()) != "success": return False else: isString = result.GetValueAsUnsigned() != 0 return isString def _visualize(target): target = fb.evaluateInputExpression(target) if fb.evaluateBooleanExpression( "(unsigned long)CFGetTypeID((CFTypeRef)" + target + ") == (unsigned long)CGImageGetTypeID()" ): _showImage("(id)[UIImage imageWithCGImage:" + target + "]") elif fb.evaluateBooleanExpression( "(unsigned long)CFGetTypeID((CFTypeRef)" + target + ") == (unsigned long)CVPixelBufferGetTypeID()" ): _showPixelBuffer(target) else: if objectHelpers.isKindOfClass(target, "UIImage"): _showImage(target) elif objectHelpers.isKindOfClass(target, "UIView"): _showLayer("[(id)" + target + " layer]") elif objectHelpers.isKindOfClass(target, "CALayer"): _showLayer(target) elif ( objectHelpers.isKindOfClass(target, "UIColor") or objectHelpers.isKindOfClass(target, "CIColor") or _colorIsCGColorRef(target) ): _showColor(target) elif objectHelpers.isKindOfClass(target, "NSData"): if _dataIsImage(target): _showImage("(id)[UIImage imageWithData:" + target + "]") elif _dataIsString(target): print( fb.describeObject( "[[NSString alloc] initWithData:" + target + " encoding:4]" ) ) else: print("Data isn't an image and isn't a string.") else: print( "{} isn't supported. You can visualize UIImage, CGImageRef, UIView, CALayer, NSData, UIColor, CIColor, or CGColorRef.".format( objectHelpers.className(target) ) ) class FBVisualizeCommand(fb.FBCommand): def name(self): return "visualize" def description(self): return "Open a UIImage, CGImageRef, UIView, CALayer, or CVPixelBuffer in Preview.app on your Mac." def args(self): return [ fb.FBCommandArgument( arg="target", type="(id)", help="The object to visualize." ) ] def run(self, arguments, options): _visualize(arguments[0])