forked from facebook/chisel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
FBFindCommands.py
127 lines (104 loc) · 4.83 KB
/
FBFindCommands.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/python
# Copyright (c) 2014, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.
import os
import re
import lldb
import fblldbbase as fb
import fblldbobjcruntimehelpers as objc
import fblldbviewcontrollerhelpers as vcHelpers
def lldbcommands():
return [
FBFindViewControllerCommand(),
FBFindViewCommand(),
FBTapLoggerCommand(),
]
class FBFindViewControllerCommand(fb.FBCommand):
def name(self):
return 'fvc'
def description(self):
return 'Find the view controllers whose class names match classNameRegex and puts the address of first on the clipboard.'
def options(self):
return [
fb.FBCommandArgument(short='-n', long='--name', arg='classNameRegex', type='string', help='The view-controller-class regex to search the view controller hierarchy for.'),
fb.FBCommandArgument(short='-v', long='--view', arg='view', type='UIView', help='This function will print the View Controller that owns this view.')
]
def run(self, arguments, options):
if options.classNameRegex and options.view:
print("Do not set both the --name and --view flags")
elif options.view:
self.findOwningViewController(options.view)
else:
output = vcHelpers.viewControllerRecursiveDescription('(id)[[[UIApplication sharedApplication] keyWindow] rootViewController]')
searchString = options.classNameRegex if options.classNameRegex else arguments[0]
printMatchesInViewOutputStringAndCopyFirstToClipboard(searchString, output)
def findOwningViewController(self, object):
while object:
if self.isViewController(object):
description = fb.evaluateExpressionValue(object).GetObjectDescription()
print("Found the owning view controller.\n{}".format(description))
cmd = 'echo {} | tr -d "\n" | pbcopy'.format(object)
os.system(cmd)
return
else:
object = self.nextResponder(object)
print("Could not find an owning view controller")
@staticmethod
def isViewController(object):
command = '[(id){} isKindOfClass:[UIViewController class]]'.format(object)
isVC = fb.evaluateBooleanExpression(command)
return isVC
@staticmethod
def nextResponder(object):
command = '[((id){}) nextResponder]'.format(object)
nextResponder = fb.evaluateObjectExpression(command)
try:
if int(nextResponder, 0):
return nextResponder
else:
return None
except:
return None
class FBFindViewCommand(fb.FBCommand):
def name(self):
return 'fv'
def description(self):
return 'Find the views whose class names match classNameRegex and puts the address of first on the clipboard.'
def args(self):
return [ fb.FBCommandArgument(arg='classNameRegex', type='string', help='The view-class regex to search the view hierarchy for.') ]
def run(self, arguments, options):
output = fb.evaluateExpressionValue('(id)[[[UIApplication sharedApplication] keyWindow] recursiveDescription]').GetObjectDescription()
printMatchesInViewOutputStringAndCopyFirstToClipboard(arguments[0], output)
def printMatchesInViewOutputStringAndCopyFirstToClipboard(needle, haystack):
first = None
for match in re.finditer('.*<.*(' + needle + ')\\S*: (0x[0-9a-fA-F]*);.*', haystack, re.IGNORECASE):
view = match.groups()[-1]
className = fb.evaluateExpressionValue('(id)[(' + view + ') class]').GetObjectDescription()
print('{} {}'.format(view, className))
if first is None:
first = view
cmd = 'echo %s | tr -d "\n" | pbcopy' % view
os.system(cmd)
class FBTapLoggerCommand(fb.FBCommand):
def name(self):
return 'taplog'
def description(self):
return 'Log tapped view to the console.'
def run(self, arguments, options):
parameterExpr = objc.functionPreambleExpressionForObjectParameterAtIndex(0)
breakpoint = lldb.debugger.GetSelectedTarget().BreakpointCreateByName("-[UIApplication sendEvent:]")
breakpoint.SetCondition('(int)[' + parameterExpr + ' type] == 0 && (int)[[[' + parameterExpr + ' allTouches] anyObject] phase] == 0')
breakpoint.SetOneShot(True)
lldb.debugger.HandleCommand('breakpoint command add -s python -F "sys.modules[\'' + __name__ + '\'].' + self.__class__.__name__ + '.taplog_callback" ' + str(breakpoint.id))
lldb.debugger.SetAsync(True)
lldb.debugger.HandleCommand('continue')
@staticmethod
def taplog_callback(frame, bp_loc, internal_dict):
parameterExpr = objc.functionPreambleExpressionForObjectParameterAtIndex(0)
print fb.describeObject('[[[%s allTouches] anyObject] view]' % (parameterExpr))
# We don't want to proceed event (click on button for example), so we just skip it
lldb.debugger.HandleCommand('thread return')