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])