简体   繁体   中英

LLDB Python access of iOS variables?

As part of debugging a problem that might be related to my UIVIews, I want to write a python script to run from LLDB. I had thought to extract all settings for a view in a breakpoint and all view children, to allow me to compare states. I checked out the WWDC video on the topic and then spent time reading things at lldb.llvm.org/scripting.html, and didn't find them very helpful. A web search for examples led to nothing substantially different from those.

My problem is that I'm trying to figure out how to access iOS variables at my breakpoint. The examples I've seen do things like convert numbers and mimic shell commands. Interesting stuff but not useful for my purposes. I've been reading my way through the help info with "script help(lldb.SBValue)" and the like, but it is slow going as the results are huge and it is not clear what the use patterns are. I feel like one decent example of how to traverse a few iOS objects would help me understand the system. Does anyone know of one or can share a snippet of code?

UPDATE:

I wrote this to help me track down a bug in my UIView use. I want to do a bit more work to refine this to see if I could show the whole view tree, but this was sufficient to solve my problem, so I'll put it here to save others some time.

import lldb
max_depth = 6
filters = {'_view':'UIView *', '_layer':'CALayer *', '_viewFlags':'struct'}

def print_value(var, depth, prefix):
    """ print values and recurse """
    global max_depth
    local_depth = max_depth - depth
    pad = ' ' * local_depth
    name = var.GetName()
    typ = str(var.GetType()).split('\n')[0].split('{')[0].split(':')[0].strip()

    found = name in filters.keys() # only visit filter items children
    if found:
        found = (filters.get(name) == typ)

    value = var.GetValue()
    if value is None or str(value) == '0x00000000':
        value = ''
    else:
        value = ' Val: %s' % value

    if var.GetNumChildren() == 0 and var.IsInScope():
        path = lldb.SBStream()
        var.GetExpressionPath(path)
        path = ' pathData: %s' % path.GetData()
    else:
        path = ''

    print '^' * local_depth, prefix, ' Adr:', var.GetAddress(), ' Name:', name, ' Type:', typ, value, path

    if var.GetNumChildren() > 0:
        if local_depth < 2 or found:
            print pad, var.GetNumChildren(), 'children, to depth', local_depth + 1
            counter = 0
            for subvar in var:
                subprefix = '%d/%d' % (counter, var.GetNumChildren())
                print_value(subvar, depth - 1, subprefix)
                counter += 1

def printvh (debugger, command_line, result, dict):
    """ print view hierarchy """
    global max_depth
    args = command_line.split()
    if len(args) > 0:
        var = lldb.frame.FindVariable(args[0])
        depth = max_depth
        if len(args) > 1:
            depth = int(args[1])
            max_depth = depth
        print_value(var, depth, 'ROOT')
    else:
        print 'pass a variable name and optional depth'

And I added the following to my .lldbinit :

script import os, sys
# So that files in my dir takes precedence.
script sys.path[:0] = [os.path.expanduser("~/lldbpy")]
script import views
command script add -f views.printvh printvh

so that I can just type "printvh self 3" at the LLDB prompt.

Maybe this will help. Here's an example of how to dump simple local variables when a breakpoint is hit. I'm not displaying char* arrays correctly, I'm not sure how I should get the data for these to display it like "frame variable" would display it but I'll figure that out later when I have a free minute.

struct datastore {
  int val1;
  int val2;
  struct {
     int val3;
  } subdata;
  char *name;
};

int main (int argc, char **argv)
{
  struct datastore data = {1, 5, {3}, "a string"};
  return data.val2;
}

Current executable set to 'a.out' (x86_64).
(lldb) br se -l 13
Breakpoint created: 1: file ='a.c', line = 13, locations = 1
(lldb) br comm add -s python
Enter your Python command(s). Type 'DONE' to end.
> def printvar_or_children(var):
>   if var.GetNumChildren() == 0 and var.IsInScope():
>     path = lldb.SBStream()
>     var.GetExpressionPath(path)
>     print '%s: %s' % (path.GetData(), var.GetValue())
>   else: 
>     for subvar in var:
>       printvar_or_children(subvar)
> 
> print 'variables visible at breakpoint %s' % bp_loc
> for var in frame.arguments:
>   printvar_or_children(var)
> for var in frame.locals:
>   printvar_or_children(var)
> 
> DONE
(lldb) r
variables visible at breakpoint 1.1: where = a.out`main + 51 at a.c:13, address = 0x0000000100000f33, resolved, hit count = 1 
argc: 1
*(*(argv)): '/'
data.val1: 1
data.val2: 5
data.subdata.val3: 3
*(data.name): 'a'
Process 84865 stopped
* thread #1: tid = 0x1f03, 0x0000000100000f33 a.out`main + 51 at a.c:13, stop reason = breakpoint 1.1
    frame #0: 0x0000000100000f33 a.out`main + 51 at a.c:13
   10   int main (int argc, char **argv)
   11   {
   12     struct datastore data = {1, 5, {3}, "a string"};
-> 13     return data.val2;
(lldb)

Tip - for sanity's sake I worked on the python over in a side text editor and pasted it into lldb as I experimented.

If you use the frame variable command in lldb to explore your variables at a given stop location, that's the same basic way that you can access them via the SBFrame that is provided to your breakpoint python command in the 'frame' object.

Hope that helps to get you started.

您是否尝试查看存储在以下位置的python LLDB格式模板:

XCode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python/lldb/formatters/objc

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM