Advanced+apple+debugging (12)

Source: Internet
Author: User
Tags python script python list

In the remainder of this section, you will focus on the Python script.
As noted in the previous chapter, the Image Lookup-rn command is being eliminated on the way. It's time to create a nice script to display the content.
Here's what you can now get with the image lookup-rn command:

Picture. png

When you finish this chapter, you will have a clear script called lookup that you can check.
Picture. png

In addition, you will add a set of parameters to the lookup command to add some hints to the new search.
Automation of script creation

This project, which is included in the starter directory of this chapter, is a Python script that allows you to create LLDB scripts more easily. The two scripts are as follows:
? generate_new_script.py: This will create a new outline script with your name and paste it into the same directory as the Generate_new_script.
? lldbinit.py: This script will enumerate all the scripts (files ending in. py) in the directory where it is located, and then try to load them into the lldb. In addition, if there is a. txt extension file under this directory, LLDB will attempt to load the contents of these files by command import.
Paste the two files from the starter directory in this chapter into your ~/lldb/directory.
After putting the files in the correct location, go into your ~/.lldbinit file and add the following line of code:

Command Script Import ~/lldb/lldbinit.py
This will load the lldbinit.py file, and the lldbinit.py file will be loaded into lldb with all the. py files and. txt files in its same directory. This means that from now on, simply adding the script to the ~/LLDB directory will automatically load into lldb when the lldb is started.

Create a Lookup command

After your new tool is set up, open a terminal window. To start a new LLDB instance:

Lldb
As expected, you will quickly see the lldb greeting.
Make sure that the Lldb script that you already exist does not have any compilation errors:

(LLDB) Reload_script
If you output some errors, it's time to try your new command __generate_script (the command implemented in thegenerate_new_script.py file).
In LLD, enter:

(LLDB) __generate_script Lookup
If everything is as we expect, you will get some output similar to the following:

Opening "/users/derekselander/lldb/lookup.py" ...
In addition, a finder window will pop up to show you where the current file is located. What you can do with these scripts is crazy, isn't it?
Keep the Finder window for a few seconds-do not close it. Return to the Lldb terminal window to accommodate the use of the Reload_script command.
Because the lookup.py script is in the same directory as the lldbinit.py script and you have just reloaded the contents of the ~/.lldbinit, you should now try to see if the lookup.py file is working correctly. Enter the following command:

(LLDB) Lookup
You will get output similar to the following:

Hello! The lookup command is working!
Now you can create and use a minimum of two lldb custom commands. Yes, you can set all the content in one command, but I now manually control the loading of my script.

LLDBINIT Directory Structure recommendations

My own Lldbinit file structure may be of reference value. Although this part of the content is not necessary, it is more recommended that you organize all of your lldb's custom scripts and content.
I prefer to keep my ~/.lldbinit file as simple as possible and use a script like lldbinit.py to load everything in a particular directory. Facebook's fblldb.py did the same thing (with the same effect). If you are interested, download it and see it.
I added that directory to version control so it's handy when I need to transfer the same logic to different computers, or I do something wrong and need to roll back.
For example, my actual ~/.lldbinit file (when actually working rather than writing this book) contains only the following:

Command Script import/users/derekselander/lldb_repo/lldb_commands/
lldbinit.py
Command Script import/users/derekselander/chisel/chisel/fblldb.py
Lldb_repo is a public git repository https://github.com/DerekSelander/lldb that contains some lldb scripts that you want to engineer.
I also have Facebook's chisel on version control, so whenever those developers update some content and release some interesting versions, I just need to pull the Chisel warehouse https://github.com/facebook/ The latest version of Chisel I can have everything I need to run lldb the next time, or reload my script through Reload_script.
In my Lldb_commands directory, all of my Python scripts are stored as two text files. A text file is called Cmds.txt and holds all my command regex and Commandalias. Another text file is called Settings.txt, and this file is something I used to supplement some LLDB settings.
For example, the contents of my Settings.txt file at this moment are as follows:

Settings Set Target.skip-prologue false
Settings Set Target.x86-disassembly-flavor Intel
In the previous chapters of this book you have added these settings to your ~/.lldbinit file, but I would prefer to separate these lldb settings from my custom lldb commands so that I can retrieve the commands in the ~/.lldbinit files without losing them.
However, in this book, I chose to install each chapter individually as a single script. This means that you need to manually add the content to your ~/.lldbinit file so you know what's going on. When you finish reading this book, you should look at the new structure implementation, as there are several benefits to this layout as recommended. These benefits are as follows:

Call Reload_script only show this command ~/.lldbinit is loading; It does not show that the child script is loaded. This will, for example, give feedback that lldbinit.py is loaded and will not be fed back to the contents of the lldbinit.py file itself. This makes it easier to create scripts because I often use reload_script as a way to check for any error messages that I use last script. The less output is generated after executing reload_script, the fewer errors you need to check in the console.
As you can see, the less content you have in the ~/.lldbinit file, the easier it will be to switch between different computers, especially after adding its content to version control.
Finally, add a new script as simple as possible with such an implementation. Just add them to the directory where the lldbinit.py file is located and the next time it will be loaded. The alternative is that you manually add the path of the script to the ~/.lldbinit file, and you'll find it annoying if you do it often.
In this chapter you will use this implementation strategy of pasting scripts into the ~/LLDB directory and then loading them into Lldb to save the script ... This one's a better way, right?
Implementing the Lookup command

As you can see in the previous chapter, the foundation behind the lookup command is actually quite simple. The main secret is to use the Sbtarget findglobalfunctions API. After that, all you have to do is format the output in the way you like it.
You will continue to use the allocator project, which can be found in the starter directory in this chapter.
Open the project, build it and run it on the iphone 7 plus simulator. You will use this project to test the query functionality of your new Lookup command in this chapter.
After the project has run, pause the application to lldb.
My memory is a little blurred. Which parameter does this findglobalfunctions need to specify? Enter the following in the LLDB:

(lldb) script Help (Lldb. Sbtarget.findglobalfunctions)
You will get the following output, which shows the signature of the method:

Findglobalfunctions (self, *args) unbound lldb. Sbtarget method
Findglobalfunctions (self, str name, uint32_t max_matches, MatchType
MatchType, Sbsymbolcontextlist
Because he is a Python class, you can ignore the first self parameter. The name of the STR parameter, called name, will be the content of your Lookuo query. Max_matches indicates the maximum number of triggers you want to trigger. If you specify a number of 0, it will return all available matches. The MatchType parameter is a LLDB Python enumeration value that allows you to perform different types of searches, such as regular or non-regular.
Because regular search is the only way to do this, you will use the LLDB enumeration value Lldb.ematchtyperegex. Other enumeration values can be found in the https://lldb.llvm.org/python_reference/_lldb%27- Found on the Module.html#ematchtyperegex. It's time to implement these features on the lookup.py script. Open the ~/lldb/lookup.py with your favorite editor. Find the following code at the end of Handle_command:

Uncomment if you is expecting at least one Argumentclean_command = Shlex.split (args[0]) [0]

Result. Appendmessage (' hello! the lookup command is working! ')
Delete the above code, replace it with the following code, and make sure that your indentation remains the same:

#1
Clean_command = Shlex.split (args[0]) [0]
#2
target = debugger. Getselectedtarget ()
#3
Contextlist = target. Findglobalfunctions (Clean_command, 0, Lldb.ematchtyperegex)
#4
Result. Appendmessage (str (contextlist))
Here is an explanation of the above code:

Contains a clean version of the commands passed to this script, using the same magic you saw in chapter 20th.
Sbtarget instances are crawled by Sbdebugger.
Pass Clean_command to the Findglobalfunctions API and call Findglobalfunctions. You passed in 0 using a regular expression search by setting a limit on the number of results and passing in a match of type Ematchtyperegex.
You are converting contextlist to a python str and then adding it to Sbcommandreturnobject.
Back to Xcode, reload the contents of the script with the LLDB console:
(LLDB) Reload_script
Try running the lookup command. Remember the Dsobjectivecobject class you studied in the previous chapter? Extract all the relevant content through LLDN:

Lookup Dsobjectivecobject
You will get some output that actually looks worse than image lookup-rndsobjectivecobject:

Picture. png

Use the Lldb script command to figure out which API to explore further:

(lldb) Script k = lldb.target.FindGlobalFunctions (' Dsobjectivecobject ', 0, Lldb.ematchtyperegex)
This command repeats what the lookup.py script does and assigns the value of the Sbsymbolcontextlist instance to K. I'm a fan of short variable names when browsing the API name--if you don't notice.
Browse Sbsymbolcontextlist's Documentation:

(LLDB) Gdocumentation sbsymbolcontextlist
This command extracts the search methods implemented by sbsymbolcontextlist or rewritten. There are many ways to do this. But please focus on iter and getitem.

Picture. png

This is a good thing for your script, because it means that the sbsymbolcontextlist is iterative and can be indexed. A few seconds ago, you just passed lldb a sbsymbolcontextlist instance to a variable named K.
In the LLDB console, use the index to crawl the subkeys in the K object.
(lldb) script k[0]
This is equivalent to entering the script K.GetItem(0).
You will get some output similar to the following:

<lldb. Sbsymbolcontext; Proxy of <swig Object of type
' Lldb::sbsymbolcontext * ' at 0x113a83780> >
Very good understanding! The sbsymbolcontextlist holds an array of sbsymbolcontext components.
Use the Print command to get the context of this sbsymbolcontext:

(lldb) script print k[0]
Your output is different from mine, but I get the sbsymbolcontext that represents [Dsobjectivecobject Setlastname:], like this:

Module:file = "/users/derekselander/library/developer/xcode/
deriveddata/ allocator-czsgsdzfgtmanrdjnydkbzdmhifw/build/products/debug-
Iphonesimulator/allocator.app/allocator ", Arch = "x86_64"
Compileunit:id = {0x00000000}, File = "/users/derekselander/ios/dbg/s4-
Custom-lldb-commands/22. Ex 1, improved lookup/projects/final/allocator/
allocator/dsobjectivecobject.m ", language =" Objective-c "
Function:id = {0x100000268}, name = "-[dsobjectivecobject
Setlastname:]", range = [ 0X0000000100001C00-0X0000000100001C37)
Functype:id = {0x100000268}, Decl = dsobjectivecobject.h:33,
Compiler_ Type = "void (NSString *)"
Symbol:id = {0x0000001e}, Range =
[0x0000000100001c00-0x0000000100001c40), name= "-[ Dsobjectivecobject
Setlastname:] "
You will use the property or Getter method to fetch the name of the function from the Sbsymbolcontext.
The simplest way to do this is to grab Sbsymbol by Sbsymbolcontext's symbol property. The Sbsymbol here contains a Name property that returns a Python string that makes you happy.
Verify this logic in your LLDB console:

(lldb) script print K[0].symbol.name
In my case, I received the following content:

-[dsobjectivecobject Setlastname:]
This information is enough to build your script. You will iterate over a subkey in the Sbsymbolcontextlist and print out the name of the function it finds.
Go back to the head of the lookup.py script and modify the contents of the Handle_command function. Locate the following code:

#3
Contextlist = target. Findglobalfunctions (Clean_command, 0, Lldb.ematchtyperegex)
#4
Result. Appendmessage (str (contextlist))
Replace with the following code (indent to correct!):

Contextlist = target. Findglobalfunctions (Clean_command, 0,
Lldb.ematchtyperegex)
Output = '
For context in Contextlist:
Output + = Context.symbol.name + ' \ n '
Result. Appendmessage (Output)
Now you are iterating through all the Sbsymbolcontext returned in Sbsymbolcontextlist, extracting the name of the function and dividing it with two newline characters.
Go back to Xcode and reload your script:

(LLDB) Reload_script
Then test your updated lookup command in LLDB:

(LLDB) Lookup Dsobjectivecobject
You will get some more beautiful output than before:

-[dsobjectivecobject setlastname:]
-[dsobjectivecobject. cxx_destruct]
-[dsobjectivecobject setFirstName :]
-[dsobjectivecobject Eyecolor]
-[dsobjectivecobject init]
-[dsobjectivecobject lastName]
-[ Dsobjectivecobject Seteyecolor:]
-[dsobjectivecobject FirstName]
That's all good stuff, but I want to see where these functions are in the process. I want to combine all the functions into a specific module (a sbmodule), and when they are printed they are separated by the number of times the module's name and the module are called. The
returns to the lookup.py file. Now you're going to create two new functions. The
first function is called Generatefunctiondictionary, which takes a sbbreakpointcontextlist parameter and generates a dictionary of Python lists. These dictionaries contain keys for each module. The value in the dictionary will be the Python list of each called Sbsymbolcontext. The
second function, called Generateoutput, will parse the dictionary you just created along the options you receive from the Optionparser instance. This method will return a string and print to the console.
below the Handle_command function in the lookup.py script, start implementing the Generatemoduledictionary function as follows:

def generatemoduledictionary (contextlist):
Mdict = {}
For context in Contextlist: #1
Key = Context.module.file.fullpath
#2
If not key in mdict:
Mdict[key] = []
#3
Mdict[key].append (context)
Return mdict
Here's an explanation of the code:

Starting with Sbsymbolcontext, you're crawling sbmodule (module), then Sbfilespec (file), and then FullPath's Python string and assigning it to a variable named key. Fetching FullPath is important (instead, say, Sbfilespec's basename attribute, as there may be multiple modules for the same basename here).
The mdict variable is ready to save all the symbols found, separated by modules. The key in the dictionary will be the name of the module, and value will be an array of symbols found in that module. In this line, you are checking that the dictionary already contains a list of the modules. If not, an empty array is set to the key on the module.
You are adding a Sbsymbolcontext instance to the appropriate list for this module. You can safely assume that each key in the mdict variable will have at least one or more sbsymbolcontext instances.
Note: An easier way to get a unique key is to use SBModule the str() method (and each of the classes in the Lldb Python module). When you call Python print on one of the objects will be called when the However, if you only rely on __str__() you will not learn all these classes, properties and methods in the process
Under the Generatemoduledictionary function, implement the Generateoutput function:

def generateoutput (mdict, Options, target): #1
Output = '
Separator = ' + ' \ n ' #2
For key in Mdict:
#3
Count = Len (Mdict[key])
FirstItem = mdict[key][0]
#4
ModuleName = FirstItem.module.file.basename
Output + = ' {0}{1} hits in {2}\n{0} '. Format (separator,
#5
For context in Mdict[key]:
query = '
Query + = Context.symbol.name
Query + = ' \ n '
Output + = Query
Return output
Here's an explanation of the code:

The output variable will be the returned containing all the content strings passed to Sbcommandreturnobject.
Enumerates all the keys found in the Mdict dictionary.
This will fetch the number of array lining items and the first subkey in each of the arrays. You will use this information later to query the module's name.
You are grabbing the module name so that it can be applied to the head output of each segment.
This will iterate over the search Sbsymbolcontext in the Python array and add the name to the output variable.
There is one last thing to do before you test the script.
Add the code in the Handle_command function so that it can use the two new methods that you just created. Locate the following code:
Output = '
For context in Contextlist:
Output + = Context.symbol.name + ' \ n '
Use the following code instead:

mdict = Generatemoduledictionary (contextlist)
Output = Generateoutput (mdict, options, target)
You know what to do. back to Xcode; Reload the contents of the LLDB:

(LLDB) Reload_script
Check your newly improved lookup command:

(LLDB) Lookup Dsobjectivecobject
You will get some output similar to the following:

8 hits in Allocator

-[dsobjectivecobject Setlastname:]
-[dsobjectivecobject. Cxx_destruct]
-[dsobjectivecobject Setfirstname:]
-[dsobjectivecobject Eyecolor]
-[dsobjectivecobject Init]
-[dsobjectivecobject LastName]
-[dsobjectivecobject Seteyecolor:]
-[dsobjectivecobject FirstName]
Cool. Then look at all the objective-c methods that start with one initwith and contain two parameters.

(LLDB) Lookup Initwith (\w+\:) {2,2}]
You will get all the public and private modules loaded into the allocator process.

Add options for Lookup

You have to keep the options good and simple and implement only two options that do not require additional parameters.
You need to implement the following:
? Add load addresses to each query. This is ideal if you want to know the actual position of the function in memory.
? Simply provide an introduction to a module. Do not generate a function name, only the number of each module call is listed.
__generate_script added placeholders for the Generateoptionparser method found at the bottom of the lookup.py file. In the Generateoptionparser function, change the function so that it contains the following code:

Def generateoptionparser ():
Usage = "Usage:%prog [options] Code_to_query"
Parser = Optparse. Optionparser (usage=usage, prog= "lookup")
Parser.add_option ("-L", "--load_address",
Action= "Store_true",
Default=false,
Dest= "Load_address",
help= "Show The load addresses for a particular hit")
Parser.add_option ("-S", "--module_summary",
Action= "Store_true",
Default=false,
Dest= "Module_summary",
Help= "Only show the amount of queries in the module")
return parser
There is no need to read this code in depth because you have learned these things in the previous chapters. You have created two supported options,-S, or--module_summary and-l, or--load_address.
First you want to implement the option to load the address. In the Generateoutput function, find the code for loop iteration Sbsymbolcontext with context in Mdict[key]: begins.
Modify the For loop as follows:

For context in Mdict[key]:
query = '
#1
If options.load_address:
#2
Start = context.symbol.addr.GetLoadAddress (target) end = Context.symbol.end_addr. Getloadaddress (target) #3
Starthex = ' 0x ' + format (start, ' 012x ')
Endhex = ' 0x ' + format (end, ' 012x ')
Query + = ' [{}-{}]\n '. Format (Starthex, Endhex)
Query + = Context.symbol.name
Query + = ' \ n '
Output + = Query
Here's what this code does:

You added a conditional sentence to determine if the load_address option was set. If it is set, this will add content to the output.
This is from Sbsymbolcontext to Sbsymbol (symbol attribute) to sbaddress (addr or end_addr) and then gets the python long through getloadaddress. There's actually a load_addr variable for sbaddress, but I've found a bug in time, so I've already used the default GETLOADADDRESSAPI instead. This method expects sbtarget as an input parameter.
After you have the start and end expressions of the Python long, you have already said that they are nicely formatted and combined with the Python format function. If you need to make up 0, note that it should be 12 digits in length and format it as hexadecimal.
Save your work and re-view the Xcode and Lldb consoles. Reload.
(LLDB) Reload_script
Test your new options:

(LLDB) Lookup-l Dsobjectivecobject
You will get some output similar to the following:

8 hits in Allocator

[0X0001099D2C00-0X0001099D2C40]
-[dsobjectivecobject Setlastname:]
[0x0001099d2c40-0x0001099d2cae]
-[dsobjectivecobject. Cxx_destruct]
Set a breakpoint on one of the addresses in this list to see if it matches the function.
To do this, replace the following address with the address in your list:

(LLDB) B 0x0001099d2c00
Breakpoint 3:where = Allocator '-[dsobjectivecobject setlastname:] at
dsobjectivecobject.h:33, address = 0x00000001099d2c00
Well done! You have implemented a multi-option command!
Take a look at the generateoutput at the end. Find the following line of code:

ModuleName = FirstItem.module.file.basename
After this line of code, add the following:

If options.module_summary:
Output + = ' {} hits in {}\n '. Format (count, ModuleName)
Continue
This code simply adds the number of times each module is called and skips to add the actual symbol.
That's it. No more code. Save and then return to Xcode to reload your script:

(LLDB) Reload_script
Test your module_summary:

(LLDB) Lookup-s viewwillappear
You will get some code similar to the following:

Hits in UIKit
1 hits in WebKit
4 hits in Allocator
That's all! You've already done it! You have created a more powerful script from scratch. You will use this script to search for code in later chapters. The summary option is a powerful tool when you develop a broad search scope and then want to narrow the scope further.

Why are we learning this?

There are more options to add to the lookup command. You can create a-s or-swift_only selection by following the Sbsymbolcontext sbfunction (via the Function property)
Go to access the GetLanguage () API.
Once you've implemented it, you should also add a-m or--module option to filter out the contents of a module.
If you want to see what other options are available, you can see the Lookup command I implemented in https://github.com/DerekSelander/LLDB/blob/master/lldb_commands/lookup.py.
Enjoy the added options now!

Advanced+apple+debugging (12)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.