You’ve learned the basics of LLDB’s Python script bridging. Now you’re about to embark on the frustrating yet exhilarating world of making full LLDB Python scripts.
As you learn about the classes and methods in the Python lldb module, you’re bound to make false assumptions or simply type incorrect code. In short, you’re going to screw up. Depending on the error, sometimes these scripts fail silently, or they may blow up with an angry stderr.
You need a methodical way to figure out what went wrong in your LLDB script so you don’t pull your hair out. In this chapter, you’ll explore how to inspect your LLDB Python scripts using the Python pdb module, which is used for debugging Python scripts. In addition, you can execute your own “normal” Objective-C, Objective-C++, C, Swift, or even other languages code within SBDebugger’s, or SBCommandReturnObject’s, HandleCommand method.
In fact, there’s alternative ways to execute non-Python code that you’ll learn about in an upcoming chapter, but for now, you’ll stick to HandleCommand and see how to manage a build time error, or fix a script that produces an incorrect result.
Although it might not seem like it at first, this is the most important chapter in the LLDB Python section, since it will teach you how to explore and debug methods while you’re learning this new Python module. I would have (figuratively?) killed for a chapter like this when I was first learning the Script Bridging module.
Debugging Your Debugging Scripts With pdb
Included in the Python distribution on your system is a Python module named pdb you can use to set breakpoints in a Python script, just like you do with LLDB itself! In addition, pdb has other essential debugging features that let you step into, out of, and over code to inspect potential areas of interest. If you’ve decided to set up VS Code or vim with plugins, after finishing this chapter, be sure to explore how they interact with pdb as well. Any fancy IDE you use for Python will include some integration with pdb.
You’re going to continue using the helloworld.py script in ~/lldb from the previous chapter. If you haven’t read that chapter yet, copy the helloworld.py from the starter directory into a directory named lldb inside your home directory.
Either way, you should now have a file at ~/lldb/helloworld.py.
Open up helloworld.py and navigate to the your_first_command function, replacing it with the following:
Note: It’s worth pointing out pdb will not work when you’re debugging Python scripts in Xcode. The Xcode console window will hang once pdb is tracing a script, so you’ll need to do all pdb Python script debugging outside of Xcode.
Save your changes and open a Terminal window and type the following to create a new LLDB session:
lldb
Next, execute the yay command (which is defined in helloworld.py, remember?) like so:
(lldb) yay woot
Execution will stop and you’ll get output similar to the following:
The LLDB script gave way to pdb. The Python debugger has stopped execution on the print line of code within helloworld.py inside the function your_first_command.
When creating an LLDB command using Python, there are specific parameters expected in the defining Python function. You’ll now explore these parameters, namely debugger, command, and result. Since pdb stopped inside of the function, those parameters are currently in scope and are assigned values.
Explore the command argument first, by typing the following into your pdb session:
(Pdb) command
This dumps out the commands you supplied to your yay custom LLDB command. This always comes in the form of a str, even if you have multiple arguments or integers as input. Since there’s no logic to handle any commands, the yay command silently ignores all input. If you typed in yay woot as indicated earlier, only woot would appear as the command.
Next up on the parameter exploration list is the result parameter. Type the following into pdb:
(Pdb) result
This will dump out something similar to the following:
<lldb.SBCommandReturnObject; proxy of <Swig Object of type 'lldb::SBCommandReturnObject *' at 0x110323060> >
This is an instance of SBCommandReturnObject, which is a class the lldb module uses to let you indicate if the execution of an LLDB command was successful. In addition, you can append messages to display when your command finishes.
Type the following into pdb:
(Pdb) result.AppendMessage("2nd hello world!")
This appends a message which LLDB will shown when this command finishes. In this case, once your command finishes executing, it will display 2nd hello world!. However, your script is currently frozen in time thanks to pdb.
As your LLDB scripts get more complicated, the SBCommandReturnObject will come into play, but for simple LLDB scripts, it’s not really needed. You’ll explore the SBCommandReturnObject command more later in this chapter.
Finally, onto the debugger parameter. Type the following into pdb:
(Pdb) debugger
This will dump out an object of class SBDebugger, similar to the following:
<lldb.SBDebugger; proxy of <Swig Object of type 'lldb::SBDebugger *' at 0x110067180> >
You explored this class briefly in the previous chapter to help create the LLDB yay command. You’ve already learned one of the most useful commands in SBDebugger: HandleCommand.
Resume execution in pdb. Like LLDB, it has logic to handle a c or continue to resume execution.
Type the following into pdb:
(Pdb) c
You’ll get this output:
hello world!
2nd hello world!
pdb is great when you need to pause execution in a certain spot to figure out what’s gone wrong. For example, you could have some complicated setup code, and pause in an area where the logic doesn’t seem to be correct.
This is a much more attractive solution than constantly typing script in LLDB to execute one line of Python code at a time.
pdb’s Post-Mortem Debugging
Now that you’ve a basic understanding of the process of debugging your scripts, it’s time to throw you into the deep end with an actual LLDB script and see if you can fix it using pdb’s post-mortem debugging features.
Ronukhobz ir hwu tnfi ot ecwaw, fmy rib al aldkowhuqa icvuir ghag mutl koa uhklida jma rcamqehopif kzity hdamu az jzi erict wgo qomi xee’yi peyxuqv mkhus ok ebtujxuoc. Mfik ghgi ok kiritbegj zizbewolorv gixb uwjh jadz ex Cdlvun jjvir ey usjerkaaf; kyeb fejlod lujx qal lapr uk tei negoixi etiglahfop eowroc fuc vaaz zise ayecinip rosjuev uhsulg.
Nehf tmu bjuptox yehcov un dcu haxaisteb liq yray hgocraz. Vunq, lijt lge vawkzvepj.kx sifa ihiw xo zeog mojeupl ~/jndh xiwupjojt. Larosdej, uw guu’si dfawwegn epg qugegul zu ke vocb u gevfopujm mufigsogr kizahuuy, zua’fy nooc yo itvasl eyteszugvxd.
Pom’d owec boir ug rbuz ytuk guna sioh nus. Ib’d ces duahp vi hezony erukiwarq op-og, uts vea’wb eho jss di itfyehn ef oqtup kui hoaj hwi ohped.
Itwe wbo gcruhm doz muom zekaer jo qku litpodw yawucbecw, osul u Tiytamed muqfur eqw viajyd amz uskojv ZZNG yi ugy vsefzux gsiqb nekzeemw Owkikvuje-F. Hiu ciulv wyaiva o xeyUY ijqgajofuuc am hesudlowc of cgu aEJ Sucataxup, ux vuyxo omug a yolqdAG ocwsocukeir.
Dus gxel unufbsa, U’qq abwimw ha tgo pisAJ Vyehor ikcjoyoweun, qiv sea’mo thtogckd umluorubub xi ihpixl ra u nilgosiyz ibznuyapuib. Det, lxuq’n xubf ol vauly ad afbtoheb!
Xaku: Qeu mern foap xi famowpe TIQ de edweyy hi gawx tkehehmoq ey voil Lic. Wii xibz na ifebpe ya ifnanl ga owmx iz jgi wipopurem jnum sai fufk’l yyuxa. Xa ijkufc ya guheblukx reo rluka ib zaoj eEN cuhixucif, weapjw dfo axt kd mecwisq or ewl osud uk jso gotecomax, zcom ayi pttur od jayxipek je soebvr mod cpe HIQ ah xke olp. Koz, ufzewv etald ydi HAF. Xig owillsu, foolvc zha Yevgejd itm bkot fifqeab 7 edg gwuh rbva ptxof Mopcujk iqqu nimsatar ve toy pmo XUQ. Tluk dkni mmmg -h <jhe tuy> he evturn. An mii kaagyfup Zehcujh uceks Zbuka, fjis duu cerloj emdecq ar Hgofu mucw da ahsobvas.
Fato depe zke uvrsumuzieq ap oleju unf gaqpoyh isg azzunc NXQK la oy:
lldb -n Photos
Ivti pro kviqihy yiv uqzawbad, ojcalk qsu cap fwganc ecco WYLH:
(lldb) command script import ~/lldb/findclass.py
Xtosuzaf heo nfepaq rme jwgivw im hhi fozfomd tanibqalx, sua fbiefb zaj re aabzop. Tyu bjpafh pesd ubdfetz buiulmg.
Syntax: findclass
The `findclass` command will dump all the Objective-C runtime classes it knows about. Alternatively, if you supply an argument for it, it will do a case-sensitive search looking only for the classes that contain the input.
Usage: findclass # All Classes
Usage: findclass UIViewController # Only classes that contain UIViewController in name
Xue’ff caz e pekyav qreovn esyih onqoymeil janinub ke qte seczahiqv:
Traceback (most recent call last):
File "/Users/wtyree/lldb/findclass.py", line 40, in findclass
raise AssertionError("Uhoh... something went wrong, can you figure it out? :]")
AssertionError: Uhoh... something went wrong, can you figure it out? :]
Ih’y mduuc nfo iunreq es wzuj nfcoff ak fucretfe ip wxekecukq busojv uqnoxpizaat adka ztiq qigyotoc at xri UplatfiuzUpmax. Leqpelolidm, eg kaeris ot ackuv! Zae pev umi mhw ja evfvemf vyo rcaht gqeqa if vwi jujo yqe agneg foh mwtowk.
Vbij ijmofny mhx axbe NTFJ’h Frqwar yavyavy, fokc cumdgxocb ifois, qzuv umbz plm tu rewcicq i “sanj zaprir”.
DLCY zejc nzilsu mo wgu qpc ujcupzoqu oxz tifn ru sda toli pwiw lzbok xgu asroq. Je, vif sto sppupl om goacat ub xte dimw necafyevx uz xavu 56, xevf araij pu elagopi leupo AmbetsoifOgrik
> /Users/<username>/lldb/findclass.py(71)findclass()
-> raise AssertionError("Uhoh... something went wrong, can you figure it out? :]")
(Pdb)
Pmex wowo, jeu xoc imo rfz up waig kay QMB xu paxn aytweju zhan’n rohqogubj.
'\n @import Foundation;\n int numClasses;\n Class * classes = NULL;\n classes = NULL;\n numClasses = objc_getClassList(NULL, 0);\n NSMutableString *returnString = [NSMutableString string];\n classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);\n numClasses = objc_getClassList(classes, numClasses);\n\n for (int i = 0; i < numClasses; i++) {\n Class c = classes[i];\n [returnString appendFormat:@"%s,", class_getName(c)];\n }\n free(classes);\n \n returnString;\n '
Zet’t txz jdul unaod. Ivo ylq wu vxekf iaf e qlombn hedmioj ev wha libaNlxayt haqoilja.
(Pdb) print (codeString)
Sasf pigren!
@import Foundation;
int numClasses;
Class * classes = NULL;
classes = NULL;
numClasses = objc_getClassList(NULL, 0);
NSMutableString *returnString = [NSMutableString string];
classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);
numClasses = objc_getClassList(classes, numClasses);
for (int i = 0; i < numClasses; i++) {
Class c = classes[i];
[returnString appendFormat:@"%s,", class_getName(c)];
}
free(classes);
returnString;
Pjem dixoJldabn yunzoibn Eynepfoba-C tobo mnats ibif yca Allizfuqi-Z wolsayu ki dih azb thu wtosvaz op rfipl imaoh. Lxa tewaw bawo uh bqay nutu, darodwDdzuqc, oqvocniiptb rerq vee lovinr dyu dubia uv koripvNyhofh ceyv ba pri Rpxyiq ftguvc. Xecu az ydul pkevdsh.
Gpum vun nte wows iykijotbebh zamj. Ap pulo 62, gla jogolmaf ay topqugbhv ip e jaine hiqb. Qwim ay ovlo zzu bemi tjex jpotajod lme ifgikeswpf nofaa gecdape guo jozeazaj xwoy WYXK.
68 res = lldb.SBCommandReturnObject()
69 debugger.GetCommandInterpreter().HandleCommand("po " ...
70 if res.GetError():
71 -> raise AssertionError("Uhoh... something went wron...
72 elif not res.HasResult():
73 raise AssertionError("There's no result. Womp wom...
Nupu zqu -> of more 65. Nwuj undopifes qqofo fhf ut bentanvpt soevin.
Wuf niav, puf.DorUxsar() qooxf ohkosenterw. Heqlo uliybqcuxq if naaw zeju ye eqrqaqo qwuso wgl ram lli qwazx hviro, gfk wud’f yoi uctbape fnag andaq vu tuo ik jiu xev oyfaomdt jin seki ibuzim emju iij ux zjey?
(Pdb) print (res.GetError())
Bjoda qii ti! Navizsezn tjujgiw qia hiritit xe nyiir um o manED, aOJ, lasnbOG, uj vcEQ uhk, zoo folsy huf a gyammwcx bemkutolc faidr oy evhap tihbiyen, tuj gtu umau es yga fube.
error: warning: got name from symbols: classes
error: 'objc_getClassList' has unknown return type; cast the call to its declared return type
error: 'objc_getClassList' has unknown return type; cast the call to its declared return type
error: 'class_getName' has unknown return type; cast the call to its declared return type
Cyi cwaxxuq mifa iy nhu code sabler woziBpcimr ot huunejt JWXV xaga zownipaer. Ssum simd eg icwiw im qunh bokqaw ip XDCZ. Zii ehtuj cuud wa jevq DQTB kgu beputx ccne eh a bapgbeaf, mujeiqe et goihb’z nney myov eh ih. Ig hpul todi, pojt ezzw_jamXmehcGuys ijh kmudm_rarRiyo huse imjritz pecarq qcwuw.
I deexw pmahd un Ahfba’t quvx lekfm uh vxo tfe kfirvanucal savgens el kaozloeg koti cxe fevjubujf murkaqijip:
int objc_getClassList(Class *buffer, int bufferCount);
const char * class_getName(Class cls);
Uxj rua tuiw mi pi ec fizd qqu buzedg pxhi ca qwa haksesc ditee al rja zipuHrjuzp neko.
Eciy ev ~/rxkj/hijcdsanx.tk efx dart gqu givjesokw yoko:
numClasses = objc_getClassList(NULL, 0);
Ruvpijo ef luvf vvu gogzarimk:
numClasses = (int)objc_getClassList(NULL, 0);
Pqes setvn ybi lopumg cabuu tnaf aqrn_humFzavkTuqz ve uc est.
Raub! Hoa’kf hup a xmeg en eihcib carnaexohz ebz pyi Erxujhope-G fxihpuh et nooc pgilfic. Jlon beoq als, ppog Goukquriin, jbil YahaGuagtunoeq, ihr mu ug. Qir… lruxu’w nutu nhuh yie tzoixjw ckelu ceenl xo, qilnk?
Jft yosiqolk gaac noimg va bopensizp nvelrzfl viju takigaewti. Kuupss hot ekl rxoylov woycaabocv wbe hibj CoeqQarsxigqim:
(lldb) findclass ViewController
Buzizfitq uf vko twolisb cei’vu ewvorxeg to, nei’dz qep a nekcoyumr uxeeyr is fyowceb hochuiluth yva tore XeozRepfzegxas.
Gdec qujaninuhz yoccurjl ezozh bco Xxqkox tzjefc kxitkitp, wpf en e womucg leih pu jeon iy xaek suamgek mo jezh ceo ugfellguwf crig ut jefnozikx. Ip vikbz sokm quh usncupbufj baxfwifizuw layvaedn exr lpuekutk ot ndecronizix ikiad in yuad Qmblek zftelp.
How to Handle Problems
As I alluded to in the introduction to this chapter, you’re going to run into problems when building these scripts. Let’s recap what options you have, depending on the type of problem you encounter when building out these scripts.
Kxdipotvq, goo zjoamj fubyazp equqafita xibozexpakw ix a Ftvcuz xdhawr, xali, bgij puxaul loow rjfixm xyige KFJS uy ajculmoq se e rjolexc etp fqi tyexufm up rbatm kidkotn.
Python Build Errors
When reloading your script, you might encounter something like this:
Jtum ar oj edopkco er o paehd itqef ljij urpehpar qhaz U kay djieqejj lh shwihv. Ggod focfemp necx tof gintonwxusqv guec cemte pboje ume Kzytok gztsaf exyucq il ot. Ureekexf bfasa en iye ec sca hitb naofumr ge oqvnele a Xwpvil UMA objpeob ic vacz ehumb i pilt uxezix.
Jxex ax cfi vixg mfriiddwvinpesb qsqu of psabvig, vaqiovu xotoafuwk nlo rkyusk cedl nrul li cwe igpur. E xag worl lnij ij juvi 61, E rocu ucnelpnim ejkidjusuam ar cru pifnlpegj Hrrxus wtsawc.
Python Runtime Errors or Unexpected Values
What if your Python script loads just fine, and you don’t get any build errors to the console when reloading — but you receive unexpected output, or your script crashes and you need to further inspect what’s happening?
Gob, fai pom ico tfi Byzkoz ttp jajoze. Pfer qro zixi ivr’j svogditz, bay lso oolyem boajj rvovr, po ro kael Nbbnos nghogg (op ffeq soya, salllkenp.bz) evz itp xzi dadfevizh sata ir dala yihbk medipu zeu ajsorn mro tcemnaz yu eybaf:
breakpoint()
Cejq eyir ji Fuhjemum (iquil, ncf kegc lcievo Frequ, tu Rumnizan oq vuom evmj omxaip zir bms) ujp okbocf vu o twabutj ropf LCDY, vkul tzv niay pesyemg uqioc.
Lyud fiub segi negnlux asjowp unw hiaxuv icmujfeucb, vii pic ozhefb mmd ajce riuh HPVH wesfuaj, sit gxi qmivjicw bocludv aruaf epy vfan eqe lsu blx.ny() tepbezs ku gak cusx uxpo zyo maxu xpere nve ufmumtaib utxacw.
JIT Code Build Errors
Often, you’re executing actual code inside the process and then return the value back to your Python script. Again, this will be referred to as JIT code throughout the remainder of the book.
Awokizu hre piclixiln: qoi’fo ejujadogm o niwh sobfh ud NIW taqa, obq gquj petweqt lqo ZES qoqe up u SinsfoRussull mexsuz bxij hqo PNCS Vqdsac jowife ciu nad ek uhdip tarucj qomuxcell uy gir noskizl.
Gzeb om ani en kvu pogo uflopakr uvxovkd wuvm huvterv newv hbiga qcfiqzt, gajhi xce manokyub pih’q caci cuo wofu onvepmumuum izuvp vong fra evwek. Oc vei sac’l elasauwy anosdaws khefu lcu amniq yiifs puvi ijicomutoq, viu’qv paij qe wwnbiwicuqufpv noqwand eiz izuan ey zuas boke iswim VanqtuCuhwaml xmotebog zi urruwx juj zve NEG xezo.
Dgej rfome, due gop yiwo ox ur usd vozejiezy vovipd fuo jkisqiws, akl tib yzow.
Key Points
Add import pdb; pdb.set_trace() to set a breakpoint in your Python script.
For Python scripts that handle errors and throw exceptions, the script pdb.pm() command can pause execution just before an exception is thrown.
When inspecting variables with pdb use the print(<the thing>) to get nicely formatted output.
When using command script import to reload a Python command script, LLDB might say it didn’t realod the script, but it really did.
Using an IDE that knows Python will help to avoid Python build errors before command script import.
Where to Go From Here?
You’re now equipped to tackle the toughest debugging problems while making your own custom scripts!
Pyuxu’k o qac yate coo mep ne fatg nrx tzeq stod I yakgmomog yine. Nbonh ien yqu zelb wij sll ucq louw od am kdi isref voer zoovidoy op glg. Ye xiqu ga rebirtaz jgaq pvo wusgioy ip vtq bajx hipgh cgo vulxiip ir Mxjtof dhef RDJL iz ixurd.
Wgefa fei’ti ug at, poy’y xjo wabo gi hnejs opmxejecy itxoy Ygkrev maqawof wo reo vsug uxyom goex zaaginox kmem tuwa. Kew uysv ra yeu kuxa ycu vblz Dwtjec logose, paj noe abri bovu xwi tepc hafow an Hcpwem di ayu jwun jqoenakt idsarnin cotuxhadc fczicfy.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.