Bitcode
什么是
Bitcode?
Bitcode是被編譯程序的一種中間形式的代碼。包含Bitcode并上傳到App Store Connect的App,會在App Store上編譯和鏈接。包含Bitcode可以在不提交新版本App的情況下,允許Apple在將來的時候再次優(yōu)化你的App二進(jìn)制文件。在
Xcode中,默認(rèn)開啟Bitcode。如果你的App支持Bitcode,App使用到的其他二進(jìn)制形式也要支持Bitcode,否則就會報錯。錯誤信息如下:
linker command failed with exit code 1 (use -v to see invocation)
解決錯誤的兩種方案:
- 方案一:將不支持
Bitcode的SDK移除掉,等待第三方更新。- 方案二:將使用
Bitcode的選項(xiàng)設(shè)置為NO:
Bitcode是編譯后生成匯編之前的中間表現(xiàn):
Bitcode底層編譯流程:
包含
Bitcode并上傳到App Store Connect的App,會在App Store上編譯和鏈接:
鏈接時間優(yōu)化(LTO)
Link Time Optimization(LTO)鏈接時間優(yōu)化是指:
- 鏈接階段執(zhí)行模塊間優(yōu)化。
通過整個程序分析和跨模塊優(yōu)化來獲得更好的運(yùn)行時性能的方法。
在編譯階段,
clang將發(fā)出LLVM bitcode而不是目標(biāo)文件。鏈接器識別這些
Bitcode文件,并在鏈接期間調(diào)用LLVM以生成將構(gòu)成可執(zhí)行文件的最終對象。接下來會加載所有輸入的
Bitcode文件,并將它們合并在一起以生成一個模塊。通俗來講,鏈接器將所有目標(biāo)文件拉到一起,并將它們組合到一個程序中。鏈接器可以查看整個程序,因此可以進(jìn)行整個程序的分析和優(yōu)化。通常,鏈接器只有在將程序翻譯成機(jī)器代碼后才能看到該程序。
LLVM的LTO機(jī)制是通過把LLVM IR傳遞給鏈接器,從而可以在鏈接期間執(zhí)行整個程序分析和優(yōu)化。所以,LTO的工作方式是編譯器輸出的目標(biāo)文件不是常規(guī)目標(biāo)文件:它們是LLVM IR文件,僅通過目標(biāo)文件文件擴(kuò)展名偽裝為目標(biāo)文件。
LTO有兩種模式:
Full LTO是將每個單獨(dú)的目標(biāo)文件中的所有LLVM IR代碼組合到一個大的module中,然后對其進(jìn)行優(yōu)化并像往常一樣生成機(jī)器代碼。Thin LTO是將模塊分開,但是根據(jù)需要可以從其他模塊導(dǎo)入相關(guān)功能,并行進(jìn)行優(yōu)化和機(jī)器代碼生成。進(jìn)行
LTO而不是一次全部編譯的優(yōu)點(diǎn)是(部分)編譯與LTO并行進(jìn)行。對于完整的LTO(-flto=full),僅并行執(zhí)行語義分析,而優(yōu)化和機(jī)器代碼生成則在單個線程中完成。對于ThinLTO(-flto=thin),除全局分析步驟外,所有步驟均并行執(zhí)行。因此,ThinLTO比FullLTO或一次編譯快得多。
clang命令使用LTO的編譯鏈接參數(shù):
-flto=<value>:設(shè)置LTO的模式:full或者thin,默認(rèn)full。-lto_library <path>:指定執(zhí)行LTO方式的庫所在位置。當(dāng)執(zhí)行鏈接時間優(yōu)化(LTO)時,鏈接器將自動去鏈接libLTO.dylib,或者從指定路徑鏈接。
在
Xcode Build Setting中的設(shè)置:設(shè)置
Link-Time Optimization
通過案例來分析一下:
打開
a.h文件,寫入以下代碼#ifndef a_h #define a_h #include <stdio.h> extern int foo1(void); extern void foo2(void); extern void foo4(void); #endif /* a_h */打開
a.c文件,寫入以下代碼#include "a.h" static signed int i = 0; void foo2(void) { i = -1; } static int foo3() { foo4(); return 10; } int foo1(void) { int data = 0; if (i < 0) data = foo3(); data = data + 42; return data; }打開
main.c文件,寫入以下代碼#include <stdio.h> #include "a.h" void foo4(void) { printf("Hi\n"); } int main() { return foo1(); }進(jìn)入終端運(yùn)行:
將
a.c編譯生成Bitcode格式文件clang -flto -c a.c -o a.o將
main.c正常編譯成目標(biāo)文件clang -c main.c -o main.o將
a.c和main.c通過LTO方式鏈接到一起clang -flto a.o main.o -o main按照
LTO優(yōu)化方式:
- 鏈接器首先按照順序讀取所有目標(biāo)文件(此時,是
Bitcode文件,僅偽裝成目標(biāo)文件)并收集符號信息- 接下來,鏈接器使用全局符號表解析符號。找到未定義的符號,替換
weak符號等等- 按照解析的結(jié)果,告訴執(zhí)行
LTO的庫文件(默認(rèn)是libLTO.dylib)哪些符號是需要的。緊接著,鏈接器調(diào)用優(yōu)化器和代碼生成器,返回通過合并Bitcode文件并應(yīng)用各種優(yōu)化過程而創(chuàng)建的目標(biāo)文件。然后更新內(nèi)部全局符號表- 鏈接器繼續(xù)運(yùn)行,直到生成可執(zhí)行文件
上述案例中,
LTO整個的優(yōu)化順序?yàn)椋?/p>
- 首先讀取
a.o(Bitcode文件)收集符號信息。鏈接器將foo1()、foo2()、foo4()識別為全局符號- 讀取
main.o(真正的目標(biāo)文件),找到目標(biāo)文件中使用的符號信息。此時,main.o使用了foo1(),定義了foo4()- 鏈接器完成了符號解析過程后,發(fā)現(xiàn)
foo2()未在任何地方使用它將其傳遞給LTO。foo2()一旦可以刪除,意味著發(fā)現(xiàn)foo1()里面調(diào)用foo3()的判斷始終為假,也就是foo3()也沒有使用,也可以刪除- 符號處理完畢后,將處理結(jié)果傳遞給優(yōu)化器和代碼生成器,同時將
a.o合并到main.o中- 修改
main.o的符號表信息。繼續(xù)鏈接,生成可執(zhí)行文件使用
objdump --macho --syms main命令,查看最后生成的可執(zhí)行文件main的符號表信息:SYMBOL TABLE: 0000000100008008 l O __DATA,__data __dyld_private 0000000100000000 g F __TEXT,__text __mh_execute_header 0000000100003f70 g F __TEXT,__text _foo1 0000000100003f30 g F __TEXT,__text _foo4 0000000100003f50 g F __TEXT,__text _main 0000000000000000 *UND* _printf 0000000000000000 *UND* dyld_stub_binder可以看到,鏈接完成之后,我們自己聲明的函數(shù)只剩下:
main、foo1和foo4這個地方有個問題,
foo4函數(shù)并沒有在任何地方使用,為什么沒有把它干掉?因?yàn)?code>LTO優(yōu)化以入口文件需要的符號為準(zhǔn),來向外進(jìn)行解析優(yōu)化。所以,要優(yōu)化掉
foo4,那么就需要使用一個新的功能Dead Code Stripping
Dead Code Stripping
鏈接器的
-dead_strip參數(shù)的作用:簡單來講,就是移除入口函數(shù)或者沒有被導(dǎo)出符號使用到的函數(shù)或者代碼。上述案例中的
foo4正是符合這種情況,所以,通過-dead_strip可以刪除掉無用代碼
創(chuàng)建動態(tài)庫時,可以使用
-mark_dead_strippable_dylib:指明,如果并沒有使用到該動態(tài)庫的符號信息,那么鏈接器將會自動優(yōu)化該動態(tài)庫。不會因?yàn)槁窂絾栴}崩潰
同時,你也可以在
App中使用-dead_strip_dylibs獲得相同的功能
在
Xcode Build Setting中的設(shè)置:設(shè)置
Dead Code Stripping,它會在鏈接過程中進(jìn)行優(yōu)化
YES:刪除掉無用代碼NO:不開啟此項(xiàng)優(yōu)化
Code Generation Options
在
Xcode Build Setting中的設(shè)置:設(shè)置
Optimization Level(編譯器的優(yōu)化程度),它會在編譯時生成目標(biāo)文件時進(jìn)行優(yōu)化
None [-O0]:不優(yōu)化。
在這種設(shè)置下, 編譯器的目標(biāo)是降低編譯消耗,保證調(diào)試時輸出期望的結(jié)果。程序的語句之間是獨(dú)立的:如果在程序的停在某一行的斷點(diǎn)出,我們可以給任何變量賦新值抑或是將程序計(jì)數(shù)器指向方法中的任何一個語句,并且能得到一個和源碼完全一致的運(yùn)行結(jié)果。
Fast [-O1]:大函數(shù)所需的編譯時間和內(nèi)存消耗都會稍微增加。
在這種設(shè)置下,編譯器會嘗試減小代碼文件的大小,減少執(zhí)行時間,但并不執(zhí)行需要大量編譯時間的優(yōu)化。在蘋果的編譯器中,在優(yōu)化過程中,嚴(yán)格別名,塊重排和塊間的調(diào)度都會被默認(rèn)禁止掉。此優(yōu)化級別提供了良好的調(diào)試體驗(yàn),堆棧使用率也提高,并且代碼質(zhì)量優(yōu)于None [-O0]。
Faster [-O2]:編譯器執(zhí)行所有不涉及時間空間交換的所有的支持的優(yōu)化選項(xiàng)。
是更高的性能優(yōu)化Fast [-O1]。在這種設(shè)置下,編譯器不會進(jìn)行循環(huán)展開、函數(shù)內(nèi)聯(lián)或寄存器重命名。和Fast [-O1]項(xiàng)相比,此設(shè)置會增加編譯時間,降低調(diào)試體驗(yàn),并可能導(dǎo)致代碼大小增加,但是會提高生成代碼的性能。
Fastest [-O3]:在開啟Fast [-O1]項(xiàng)支持的所有優(yōu)化項(xiàng)的同時,開啟函數(shù)內(nèi)聯(lián)和寄存器重命名選項(xiàng)。
是更高的性能優(yōu)化Faster [-O2],指示編譯器優(yōu)化所生成代碼的性能,而忽略所生成代碼的大小,有可能會導(dǎo)致二進(jìn)制文件變大。還會降低調(diào)試體驗(yàn)。
Fastest, Smallest [-Os]:在不顯著增加代碼大小的情況下盡量提供高性能。
這個設(shè)置開啟了Fast [-O1]項(xiàng)中的所有不增加代碼大小的優(yōu)化選項(xiàng),并會進(jìn)一步的執(zhí)行可以減小代碼大小的優(yōu)化。增加的代碼大小小于Fastest [-O3]。與Fast [-O1]相比,它還會降低調(diào)試體驗(yàn)。
Fastest, Aggressive Optimizations [-Ofast]:與Fastest, Smallest [-Os]相比該級別還執(zhí)行其他更激進(jìn)的優(yōu)化。
這個設(shè)置開啟了Fastest [-O3]中的所有優(yōu)化選項(xiàng),同時也開啟了可能會打破嚴(yán)格編譯標(biāo)準(zhǔn)的積極優(yōu)化,但并不會影響運(yùn)行良好的代碼。該級別會降低調(diào)試體驗(yàn),并可能導(dǎo)致代碼大小增加。
Smallest, Aggressive Size Optimizations [-Oz]:不使用LTO的情況下減小代碼大小。
與-Os相似,指示編譯器僅針對代碼大小進(jìn)行優(yōu)化,而忽略性能優(yōu)化,這可能會導(dǎo)致代碼變慢。
strip
strip:移除指定符號。在Xcode中默認(rèn)strip是在Archive的時候才會生效,移除對應(yīng)符號
strip命令的使用:
strip -x:除了全局符號都可以移除 (動態(tài)庫使用)strip -S:移除調(diào)試符號(靜態(tài)庫使用)strip:除了間接符號表中使用的符號,其他符號都移除(上架App使用)
在
Xcode Build Setting中的設(shè)置:設(shè)置
Deployment Postprocessing
- 打開后在編譯階段就會運(yùn)行
strip設(shè)置
Strip Debug Symbols During Copy
- 當(dāng)應(yīng)用在編譯階段
copy了某些二進(jìn)制文件時,打開該選項(xiàng)會脫掉該二進(jìn)制的調(diào)試符號。但是不會脫去鏈接的最終產(chǎn)物(可執(zhí)行文件\動態(tài)庫)的符號信息。要脫去鏈接的產(chǎn)物(App的可執(zhí)行文件)的符號信息設(shè)置
Strip Linked Product
- 如果沒有打開
Deployment Postprocessing,則會在Archive處理鏈接的最終產(chǎn)物(可執(zhí)行文件)的符號信息。否則,在鏈接完成之后就會處理符號信息設(shè)置
Strip Style(符號剝離級別),它會在生成可執(zhí)行文件后進(jìn)行優(yōu)化,相當(dāng)于對Mach-O文件進(jìn)行修改
All Symbols:除了間接符號表中使用的符號,其他符號都移除(上架App使用)Non-Global Symbols:除了全局符號都可以移除 (動態(tài)庫使用)Debugging Symbols:移除調(diào)試符號(靜態(tài)庫使用)
Strip Style原理
App:可以剝離除間接符號表以外的所有符號- 動態(tài)庫:可以剝離除全局符號以外的所有符號
- 靜態(tài)庫:靜態(tài)庫是
.o文件的合集,符號都存儲在重定位符號表中。靜態(tài)庫只能剝離調(diào)試符號
Debugging Symbols:剝離.o/靜態(tài)庫的調(diào)試符號
Debugging Symbols:剝離動態(tài)庫/可執(zhí)行文件的調(diào)試符號
All Symbols:剝離除間接符號表以外的所有符號
Non-Global Symbols:剝離除全局符號以外的所有符號
在LLVM項(xiàng)目中調(diào)試strip命令
添加llvm-strip
打開
LLVM項(xiàng)目,打開Edit Scheme...彈窗
選擇
Manage Schemes...
點(diǎn)擊
+添加
Target選擇llvm-strip,填寫Name后點(diǎn)擊OK
此時
llvm-strip添加成功,點(diǎn)擊Close關(guān)閉彈窗
創(chuàng)建替身
在
TAEGETS中搜索strip關(guān)鍵字,點(diǎn)擊llvm-strip,選擇Build Phases
llvm-strip是一個腳本,無法調(diào)試- 腳本作用:判斷
CONFIGURATION如果是Debug,將llvm-objcopy可執(zhí)行文件,鏈接成llvm-strip可執(zhí)行文件,相當(dāng)于生成快捷方式選擇
llvm-strip,先運(yùn)行一次腳本
在
TAEGETS中搜索objcopy關(guān)鍵字,右鍵llvm-objcopy,菜單選擇Duplicate
在
TAEGETS中多出一個llvm-objcopy-copy
將其重命名為
strip
打開鏈接后的目錄
/Volumes/study/Source/llvm/llvm-project/build/Debug/bin,找到llvm-strip可執(zhí)行文件
將其復(fù)制,重命名為
strip
回到項(xiàng)目,在
Products目錄中找到strip,右鍵strip,菜單選擇Show in Finder,此時它自動跟Debug/bin目錄中的strip可執(zhí)行文件關(guān)聯(lián)到一起
添加strip
打開
LLVM項(xiàng)目,打開Edit Scheme...彈窗
選擇
Manage Schemes...
點(diǎn)擊
+添加
Target選擇strip,填寫Name后點(diǎn)擊OK
此時
strip添加成功,點(diǎn)擊Close關(guān)閉彈窗
添加默認(rèn)參數(shù)
打開
Edit Scheme...彈窗,左上角選擇strip,左側(cè)選擇Run,右側(cè)選擇Arguments
在
Arguments Passed On Launch項(xiàng)中,點(diǎn)擊+添加參數(shù)Mach-O路徑
- ?參數(shù): 代表全部符號
-x:non_global-S: 調(diào)試符號參數(shù)添加成功,點(diǎn)擊
Close關(guān)閉彈窗
設(shè)置斷點(diǎn)
在
TAEGETS中搜索strip關(guān)鍵字,點(diǎn)擊strip,選擇Build Phases
展開
Compile Sources項(xiàng),右鍵llvm-nm.cpp文件,菜單選擇Reveal In Project Navigator,將文件顯示在左側(cè)
打開
llvm-nm.cpp代碼,找到main函數(shù)并設(shè)置好斷點(diǎn)
運(yùn)?程序
選擇
strip,但不要直接運(yùn)行,因?yàn)榫幾g會非常耗時
使用
Run Without Building運(yùn)?程序;第?次運(yùn)?時,需要進(jìn)?編譯,以重新?成調(diào)試符號。再次運(yùn)?,當(dāng)代碼沒有改變,則不需要重新編譯,直接運(yùn)?現(xiàn)有可執(zhí)??件
順利進(jìn)入斷點(diǎn),通過下標(biāo)訪問
argv,之前設(shè)置的默認(rèn)參數(shù)已傳入main函數(shù)
BreakPoint
原文:
Command
- breakpoint -- Commands for operating on breakpoints (see 'help b' for shorthand.)
Action
Commands for operating on breakpoints (see 'help b' for shorthand.)
Syntax: breakpoint
The following subcommands are supported:
- clear -- Delete or disable breakpoints matching the specified source file and line.
- command -- Commands for adding, removing and listing LLDB commands executed when a breakpoint is hit.
- delete -- Delete the specified breakpoint(s). If no breakpoints are specified, delete them all.
- disable -- Disable the specified breakpoint(s) without deleting them. If none are specified, disable all breakpoints.
- enable -- Enable the specified disabled breakpoint(s). If no breakpoints
are specified, enable all of them.- list -- List some or all breakpoints at configurable levels of detail.
- modify -- Modify the options on a breakpoint or set of breakpoints in the executable. If no breakpoint is specified, acts on the last created breakpoint. With the exception of -e, -d and -i, passing an empty argument clears the modification.
- name -- Commands to manage name tags for breakpoints
- read -- Read and set the breakpoints previously saved to a file with "breakpoint write".
- set -- Sets a breakpoint or set of breakpoints in the executable.
- write -- Write the breakpoints listed to a file that can be read in with "breakpoint read". If given no arguments, writes all breakpoints.
For more help on any particular subcommand, type 'help <command> <subcommand>'.
option - set
Sets a breakpoint or set of breakpoints in the executable.
Syntax: breakpoint set <cmd-options>
Command Options Usage:
breakpoint set [-DHd] -l <linenum> [-i <count>] [-o <boolean>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-c <expr>] [-G <boolean>] [-C <command>] [-s <shlib-name>] [-f <filename>] [-K <boolean>] [-N <breakpoint-name>] [-R <address>] [-m <boolean>]
breakpoint set [-DHd] -a <address-expression> [-i <count>] [-o <boolean>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-c <expr>] [-G <boolean>] [-C <command>] [-s <shlib-name>] [-N <breakpoint-name>]
breakpoint set [-DHd] -n <function-name> [-i <count>] [-o <boolean>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-c <expr>] [-G <boolean>] [-C <command>] [-s <shlib-name>] [-f <filename>] [-L <source-language>] [-K <boolean>] [-N <breakpoint-name>] [-R <address>]
breakpoint set [-DHd] -F <fullname> [-i <count>] [-o <boolean>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-c <expr>] [-G <boolean>] [-C <command>] [-s <shlib-name>] [-f <filename>] [-L <source-language>] [-K <boolean>] [-N <breakpoint-name>] [-R <address>]
breakpoint set [-DHd] -S <selector> [-i <count>] [-o <boolean>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-c <expr>] [-G <boolean>] [-C <command>] [-s <shlib-name>] [-f <filename>] [-L <source-language>] [-K <boolean>] [-N <breakpoint-name>] [-R <address>]
breakpoint set [-DHd] -M <method> [-i <count>] [-o <boolean>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-c <expr>] [-G <boolean>] [-C <command>] [-s <shlib-name>] [-f <filename>] [-L <source-language>] [-K <boolean>] [-N <breakpoint-name>] [-R <address>]
breakpoint set [-DHd] -r <regular-expression> [-i <count>] [-o <boolean>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-c <expr>] [-G <boolean>] [-C <command>] [-s <shlib-name>] [-f <filename>] [-L <source-language>] [-K <boolean>] [-N <breakpoint-name>] [-R <address>]
breakpoint set [-DHd] -b <function-name> [-i <count>] [-o <boolean>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-c <expr>] [-G <boolean>] [-C <command>] [-s <shlib-name>] [-f <filename>] [-L <source-language>] [-K <boolean>] [-N <breakpoint-name>] [-R <address>]
breakpoint set [-ADHd] -p <regular-expression> [-i <count>] [-o <boolean>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-c <expr>] [-G <boolean>] [-C <command>] [-s <shlib-name>] [-f <filename>] [-X <function-name>] [-N <breakpoint-name>] [-m <boolean>]
breakpoint set [-DHd] -E <source-language> [-i <count>] [-o <boolean>] [-x <thread-index>] [-t <thread-id>] [-T <thread-name>] [-q <queue-name>] [-c <expr>] [-G <boolean>] [-C <command>] [-w <boolean>] [-h <boolean>] [-O <type-name>] [-N <breakpoint-name>]
-A ( --all-files )
All files are searched for source pattern matches.
-C <command> ( --command <command> )
A command to run when the breakpoint is hit, can be provided more
than once, the commands will get run in order left to right.
-D ( --dummy-breakpoints )
Act on Dummy breakpoints - i.e. breakpoints set before a file is
provided, which prime new targets.
-E <source-language> ( --language-exception <source-language> )
Set the breakpoint on exceptions thrown by the specified language
(without options, on throw but not catch.)
-F <fullname> ( --fullname <fullname> )
Set the breakpoint by fully qualified function names. For C++ this
means namespaces and all arguments, and for Objective C this means
a full function prototype with class and selector. Can be repeated
multiple times to make one breakpoint for multiple names.
-G <boolean> ( --auto-continue <boolean> )
The breakpoint will auto-continue after running its commands.
-H ( --hardware )
Require the breakpoint to use hardware breakpoints.
-K <boolean> ( --skip-prologue <boolean> )
sKip the prologue if the breakpoint is at the beginning of a
function. If not set the target.skip-prologue setting is used.
-L <source-language> ( --language <source-language> )
Specifies the Language to use when interpreting the breakpoint's
expression (note: currently only implemented for setting
breakpoints on identifiers). If not set the target.language
setting is used.
-M <method> ( --method <method> )
Set the breakpoint by C++ method names. Can be repeated multiple
times to make one breakpoint for multiple methods.
-N <breakpoint-name> ( --breakpoint-name <breakpoint-name> )
Adds this to the list of names for this breakpoint.
-O <type-name> ( --exception-typename <type-name> )
The breakpoint will only stop if an exception Object of this type
is thrown. Can be repeated multiple times to stop for multiple
object types. If you just specify the type's base name it will
match against that type in all modules, or you can specify the full
type name including modules. Other submatches are not supported at
present.Only supported for Swift at present.
-R <address> ( --address-slide <address> )
Add the specified offset to whatever address(es) the breakpoint
resolves to. At present this applies the offset directly as given,
and doesn't try to align it to instruction boundaries.
-S <selector> ( --selector <selector> )
Set the breakpoint by ObjC selector name. Can be repeated multiple
times to make one breakpoint for multiple Selectors.
-T <thread-name> ( --thread-name <thread-name> )
The breakpoint stops only for the thread whose thread name matches
this argument.
-X <function-name> ( --source-regexp-function <function-name> )
When used with '-p' limits the source regex to source contained in
the named functions. Can be repeated multiple times.
-a <address-expression> ( --address <address-expression> )
Set the breakpoint at the specified address. If the address maps
uniquely to a particular binary, then the address will be converted
to a "file" address, so that the breakpoint will track that
binary+offset no matter where the binary eventually loads.
Alternately, if you also specify the module - with the -s option -
then the address will be treated as a file address in that module,
and resolved accordingly. Again, this will allow lldb to track
that offset on subsequent reloads. The module need not have been
loaded at the time you specify this breakpoint, and will get
resolved when the module is loaded.
-b <function-name> ( --basename <function-name> )
Set the breakpoint by function basename (C++ namespaces and
arguments will be ignored). Can be repeated multiple times to make
one breakpoint for multiple symbols.
-c <expr> ( --condition <expr> )
The breakpoint stops only if this condition expression evaluates to
true.
-d ( --disable )
Disable the breakpoint.
-f <filename> ( --file <filename> )
Specifies the source file in which to set this breakpoint. Note,
by default lldb only looks for files that are #included if they use
the standard include file extensions. To set breakpoints on
.c/.cpp/.m/.mm files that are #included, set
target.inline-breakpoint-strategy to "always".
-h <boolean> ( --on-catch <boolean> )
Set the breakpoint on exception catcH.
-i <count> ( --ignore-count <count> )
Set the number of times this breakpoint is skipped before stopping.
-l <linenum> ( --line <linenum> )
Specifies the line number on which to set this breakpoint.
-m <boolean> ( --move-to-nearest-code <boolean> )
Move breakpoints to nearest code. If not set the
target.move-to-nearest-code setting is used.
-n <function-name> ( --name <function-name> )
Set the breakpoint by function name. Can be repeated multiple
times to make one breakpoint for multiple names
-o <boolean> ( --one-shot <boolean> )
The breakpoint is deleted the first time it stop causes a stop.
-p <regular-expression> ( --source-pattern-regexp <regular-expression> )
Set the breakpoint by specifying a regular expression which is
matched against the source text in a source file or files specified
with the -f option. The -f option can be specified more than once.
If no source files are specified, uses the current "default source
file". If you want to match against all source files, pass the
"--all-files" option.
-q <queue-name> ( --queue-name <queue-name> )
The breakpoint stops only for threads in the queue whose name is
given by this argument.
-r <regular-expression> ( --func-regex <regular-expression> )
Set the breakpoint by function name, evaluating a
regular-expression to find the function name(s).
-s <shlib-name> ( --shlib <shlib-name> )
Set the breakpoint only in this shared library. Can repeat this
option multiple times to specify multiple shared libraries.
-t <thread-id> ( --thread-id <thread-id> )
The breakpoint stops only for the thread whose TID matches this
argument.
-w <boolean> ( --on-throw <boolean> )
Set the breakpoint on exception throW.
-x <thread-index> ( --thread-index <thread-index> )
The breakpoint stops only for the thread whose index matches this
argument.
基礎(chǔ)介紹
從文件中導(dǎo)入斷點(diǎn)
br read -f 【文件路徑】將斷點(diǎn)導(dǎo)出到文件
br write -f 【文件路徑】查看組內(nèi)的斷點(diǎn)列表
br list 【組名稱】啟用組中的斷點(diǎn)
br enable 【組名稱】禁用組中的斷點(diǎn)
br disable 【組名稱】刪除組中的斷點(diǎn)
br delete 【組名稱】通過文件和行號設(shè)置斷點(diǎn)
br set -f 【文件名】 -l 【行號】通過函數(shù)名稱設(shè)置斷點(diǎn),也能為
class的selector設(shè)置斷點(diǎn)br set -n 【函數(shù)名稱】 br set -n -[NSString stringWithFormat:]為
C++函數(shù)設(shè)置斷點(diǎn)br set -M 【函數(shù)名稱】為
OC的selector設(shè)置斷點(diǎn)br set -S 【selector】為指定文件里的
selector設(shè)置斷點(diǎn)br set -f 【文件名】 -S 【selector】為某個
image設(shè)置斷點(diǎn)br set -s 【image名稱】 -n 【函數(shù)名稱】為項(xiàng)目中包含關(guān)鍵字的方法設(shè)置斷點(diǎn)
br set -r 【關(guān)鍵字】
從文件中導(dǎo)入斷點(diǎn)
打開
strip_lldb.m文件,斷點(diǎn)如下:[ {"Breakpoint":{"BKPTOptions":{"AutoContinue":false,"ConditionText":"","EnabledState":false,"IgnoreCount":0,"OneShotState":false},"BKPTResolver":{"Options":{"NameMask":[56],"Offset":0,"SkipPrologue":true,"SymbolNames":["removeSections"]},"Type":"SymbolName"},"Hardware":false,"Names":["strip"],"SearchFilter":{"Options":{"CUList":["MachOObjcopy.cpp"]},"Type":"ModulesAndCU"}}}, {"Breakpoint":{"BKPTOptions":{"AutoContinue":false,"ConditionText":"","EnabledState":false,"IgnoreCount":0,"OneShotState":false},"BKPTResolver":{"Options":{"NameMask":[56],"Offset":0,"SkipPrologue":true,"SymbolNames":["handleArgs"]},"Type":"SymbolName"},"Hardware":false,"Names":["strip"],"SearchFilter":{"Options":{"CUList":["MachOObjcopy.cpp"]},"Type":"ModulesAndCU"}}}, {"Breakpoint":{"BKPTOptions":{"AutoContinue":false,"ConditionText":"","EnabledState":false,"IgnoreCount":0,"OneShotState":false},"BKPTResolver":{"Options":{"NameMask":[56],"Offset":0,"SkipPrologue":true,"SymbolNames":["executeObjcopyOnBinary"]},"Type":"SymbolName"},"Hardware":false,"Names":["strip"],"SearchFilter":{"Options":{"CUList":["MachOObjcopy.cpp"]},"Type":"ModulesAndCU"}}}, {"Breakpoint":{"BKPTOptions":{"AutoContinue":false,"ConditionText":"","EnabledState":false,"IgnoreCount":0,"OneShotState":false},"BKPTResolver":{"Options":{"NameMask":[56],"Offset":0,"SkipPrologue":true,"SymbolNames":["markSymbols"]},"Type":"SymbolName"},"Hardware":false,"Names":["strip"],"SearchFilter":{"Options":{"CUList":["MachOObjcopy.cpp"]},"Type":"ModulesAndCU"}}}, {"Breakpoint":{"BKPTOptions":{"AutoContinue":false,"ConditionText":"","EnabledState":false,"IgnoreCount":0,"OneShotState":false},"BKPTResolver":{"Options":{"NameMask":[56],"Offset":0,"SkipPrologue":true,"SymbolNames":["main"]},"Type":"SymbolName"},"Hardware":false,"Names":["strip"],"SearchFilter":{"Options":{"CUList":["llvm-objcopy.cpp"]},"Type":"ModulesAndCU"}}}, {"Breakpoint":{"BKPTOptions":{"AutoContinue":false,"ConditionText":"","EnabledState":false,"IgnoreCount":0,"OneShotState":false},"BKPTResolver":{"Options":{"NameMask":[56],"Offset":0,"SkipPrologue":true,"SymbolNames":["getDriverConfig"]},"Type":"SymbolName"},"Hardware":false,"Names":["strip"],"SearchFilter":{"Options":{"CUList":["llvm-objcopy.cpp"]},"Type":"ModulesAndCU"}}}, {"Breakpoint":{"BKPTOptions":{"AutoContinue":false,"ConditionText":"","EnabledState":false,"IgnoreCount":0,"OneShotState":false},"BKPTResolver":{"Options":{"NameMask":[56],"Offset":0,"SkipPrologue":true,"SymbolNames":["executeObjcopy"]},"Type":"SymbolName"},"Hardware":false,"Names":["strip"],"SearchFilter":{"Options":{"CUList":["llvm-objcopy.cpp"]},"Type":"ModulesAndCU"}}}, {"Breakpoint":{"BKPTOptions":{"AutoContinue":false,"ConditionText":"","EnabledState":false,"IgnoreCount":0,"OneShotState":false},"BKPTResolver":{"Options":{"NameMask":[56],"Offset":0,"SkipPrologue":true,"SymbolNames":["parseStripOptions"]},"Type":"SymbolName"},"Hardware":false,"Names":["strip"],"SearchFilter":{"Options":{"CUList":["llvm-objcopy.cpp"]},"Type":"ModulesAndCU"}}}, {"Breakpoint":{"BKPTOptions":{"AutoContinue":false,"ConditionText":"","EnabledState":false,"IgnoreCount":0,"OneShotState":false},"BKPTResolver":{"Options":{"NameMask":[56],"Offset":0,"SkipPrologue":true,"SymbolNames":["MachOWriter::write"]},"Type":"SymbolName"},"Hardware":false,"Names":["strip"],"SearchFilter":{"Options":{"CUList":["MachOWriter.cpp"]},"Type":"ModulesAndCU"}}} ]
- 這里記錄了探索
strip命令的關(guān)鍵點(diǎn)使用
br read -f /Users/zang/Zang/Spark/strip_lldb.m命令,將strip_lldb.m文件中的斷點(diǎn)導(dǎo)入項(xiàng)目
- 導(dǎo)入的斷點(diǎn)默認(rèn)是沒有被啟用的
使用
br enable strip命令,將strip組中的斷點(diǎn)全部啟用
此時斷點(diǎn)全部啟用;需要注意的是:
br命令只對本次運(yùn)行有效,當(dāng)項(xiàng)目重新運(yùn)行后,需要再次導(dǎo)入并啟用斷點(diǎn)
將斷點(diǎn)導(dǎo)出到文件
以
main函數(shù)的斷點(diǎn)為例
直接使用
Xcode設(shè)置斷點(diǎn)并導(dǎo)出,文件中FileName會記錄斷點(diǎn)所在文件的絕對路徑,這會造成導(dǎo)出的斷點(diǎn)文件不具通用性
使用
br命令設(shè)置斷點(diǎn),可以解決通用性問題使用
br set -f llvm-objcopy.cpp -l 346 -N cat命令,對llvm-objcopy.cpp文件的第346行設(shè)置斷點(diǎn),加入到cat分組
使用
br set -n removeSections -f MachOObjcopy.cpp -N cat命令,對llvm-objcopy.cpp文件的removeSections函數(shù)設(shè)置斷點(diǎn),加入到cat分組
使用
br list cat命令,查看cat分組下的斷點(diǎn)
使用
br write -f /Users/zang/Zang/Spark/cat.m命令,將斷點(diǎn)導(dǎo)出到cat.m文件[ {"Breakpoint":{"BKPTOptions":{"AutoContinue":false,"ConditionText":"","EnabledState":true,"IgnoreCount":0,"OneShotState":false},"BKPTResolver":{"Options":{"Column":0,"Exact":false,"FileName":"llvm-objcopy.cpp","Inlines":true,"LineNumber":346,"Offset":0,"SkipPrologue":true},"Type":"FileAndLine"},"Hardware":false,"Names":["cat"],"SearchFilter":{"Options":{},"Type":"Unconstrained"}}}, {"Breakpoint":{"BKPTOptions":{"AutoContinue":false,"ConditionText":"","EnabledState":true,"IgnoreCount":0,"OneShotState":false},"BKPTResolver":{"Options":{"NameMask":[56],"Offset":0,"SkipPrologue":true,"SymbolNames":["removeSections"]},"Type":"SymbolName"},"Hardware":false,"Names":["cat"],"SearchFilter":{"Options":{"CUList":["MachOObjcopy.cpp"]},"Type":"ModulesAndCU"}}} ]
查看App Size報告
方式一:
通過
App Store Connect提供準(zhǔn)確的App大小
方式二:
通過
Xcode內(nèi)置報告工具,創(chuàng)建App尺寸報告
Archive App- 通過
Ad Hoc、Development或者Enterprise等分發(fā)方式導(dǎo)出Archive App- 在設(shè)置開發(fā)分發(fā)選項(xiàng)的列表中,選擇
All compatible device variants以進(jìn)行應(yīng)用程序精簡,然后啟用Rebuild from Bitcode- 簽名并且導(dǎo)出
此過程將創(chuàng)建一個包含
App的文件夾,里面有:
Universal IPA,包含多個平臺的資源文件和二進(jìn)制程序Thinned IPA,指定平臺的資源文件和二進(jìn)制程序同時還包含一個
App Thinning Size Report.txt,里面詳細(xì)記錄了App的體積占用情況:
App Thinning Size Report for All Variants of ExampleApp
Variant: ExampleApp.ipa
Supported variant descriptors: [device: iPhone11,4, os-version: 12.0], [device: iPhone9,4, os-version: 12.0], [device: iPhone10,3, os-version: 12.0], [device: iPhone11,6, os-version: 12.0], [device: iPhone10,6, os-version: 12.0], [device: iPhone9,2, os-version: 12.0], [device: iPhone10,5, os-version: 12.0], [device: iPhone11,2, os-version: 12.0], and [device: iPhone10,2, os-version: 12.0]
App + On Demand Resources size: 6.7 MB compressed, 18.6 MB uncompressed
App size: 6.7 MB compressed, 18.6 MB uncompressed
On Demand Resources size: Zero KB compressed, Zero KB uncompressed
// Other Variants of Your App.
方式三:
通過腳本的方式指定輸出
App Size報告:xcodebuild -exportArchive -archivePath iOSApp.xcarchive -exportPath Release/MyApp -exportOptionsPlist OptionsPlist.plist




















































