AndroidT(13) -- 根据 native appliction 的crash报告定位源码位置(一)

本文介绍了如何在Android AOSP环境中使用addr2line工具定位native应用崩溃的源代码行数,涉及编译工具链确认、符号表查找以及crash报告的解析。重点在于理解编译过程和如何通过工具链找到正确的符号表来追踪问题根源。

1.概览

  native application在Android中一般指代无需虚拟机参与就可以运行的可执行程序。例如c/c++开发的一众HAL服务,但是像手机应用则不是naitve程序了,因为它是需要art虚拟机参与的。native appliction 在性能上一般是高于需要虚拟机运行的程序的。
  本小节聚焦如何根据 crash 所给的报告来定位在源码中具体的行数,使用的工具就是 addr2line 。但是由AOSP编译的源码并不像pc中那般直接,不论是符号表、编译工具链都不是很好确定,所以在此做个总结。

2.编译工具链的确认 – addr2line

  假设在AOSP环境下单独编译了native 应用程序 logPrintCppStyle

flagstaff@fakeserver:~/AOSPT/test/flagstaff/logging_test/logPrintCppStyle$ mmm . -j23
...
[100% 10/10] Install out_board_t_userdebug/target/product/board/system/bin/logPrintCppStyle

  AOSP编译系统的功能非常的完善,在对应的out目录下可以找到编译过程中的所有日志,对于编译工具链的路径信息可以在文件 verbose.log 中找到

flagstaff@fakeserver:~/AOSPT/out_board_t_userdebug$ gzip -d verbose.log.gz
flagstaff@fakeserver:~/AOSPT/out_board_t_userdebug$ vi verbose.log
[1/6] PWD=/proc/self/cwd ~/AOSPT/prebuilts/clang/host/linux-x86/clang-r450784d/bin/clang++ -c ... 
    -o logPrintCppStyle.o logPrintCppStyle.cpp

  可见AOSP编译系统使用的编译工具连为 clang++,这里也就可以根据clang++所在路径找到一整套的编译工具链了

flagstaff@fakeserver:~/AOSPT/out_board_t_userdebug$ ls ../prebuilts/clang/host/linux-x86/clang-r450784d/bin/
bisect_driver.py  clang-format      ld64.lld        llvm-addr2line   llvm-cxxfilt    llvm-modextract  llvm-readelf     merge-fdata
clang             clang++.real      ld.lld          llvm-ar          llvm-dis        llvm-nm          llvm-readobj     remote_toolchain_inputs
clang++           clang.real        lld             llvm-as          llvm-dwarfdump  llvm-objcopy     llvm-size        sancov
clang
<think>我们需要在shell脚本中分割字符串,提取第个短横线(-)前面的部分。 字符串示例:'arctic_native_k515-userdebug-androidt' 我们想要提取:'arctic_native_k515' 在shell中,我们可以使用多种方法,例如: 1. 使用cut命令:以短横线作为分隔符,取第个字段。 2. 使用参数扩展:利用shell的字符串操作。 方法:cut echo "arctic_native_k515-userdebug-androidt" | cut -d '-' -f1 方法二:参数扩展(适用于变量) str="arctic_native_k515-userdebug-androidt" echo "${str%%-*}" 解释: %% 表示从字符串的末尾开始删除最长的匹配(通配符匹配),这里匹配模式是'*-',即直到最后个短横线(但我们要第个,所以要用%而不是%%,因为%%是最长匹配,而%是最短匹配?不对,我们是从开头到第个短横线,所以应该用%%删除从第个短横线开始到末尾的部分?实际上,我们想要的是第个短横线之前的部分,所以可以用以下两种方式: 方法1:使用${str%%-*} 这个模式会删除最长的以'-'结尾的后缀,也就是删除从最后个短横线到末尾的部分。但我们想要第个短横线前面的部分,所以应该用非贪婪匹配(最短匹配)?不对,因为只有个短横线,所以%%和%效果样。但是如果有多个短横线,我们只想取第个短横线前面的部分,那么我们应该使用${str%%-*}吗?不对,%%会匹配到最后个短横线,这样就会删除从最后个短横线开始往后的部分,剩下的是第个短横线到最后个短横线之间的内容(除了最后个短横线后面的部分),但这不是我们想要的。 重新考虑:我们想要第个短横线前面的部分,所以应该删除第个短横线及其后面的所有内容。因此,我们可以使用${str%%-*}吗?注意,%%是贪婪匹配(从后向前匹配),所以它会删除最长的匹配,也就是最后个短横线及其后面的内容。而%是非贪婪匹配(从后向前最短匹配),但同样也是匹配最后个短横线。 所以,如果我们使用${str%%-*},当字符串中有多个短横线时,得到的是第个短横线到最后个短横线之前的部分?不对,我们来看: str="a-b-c" ${str%%-*} 将得到 "a" ?不对,实际上%%是从字符串结尾开始删除最长的匹配模式,模式是任意字符后跟个短横线(*表示任意多个字符,-表示短横线),所以模式是“任意字符+短横线”,从结尾开始匹配,最长的匹配是“-c”(因为从结尾开始,匹配到第个短横线,但注意模式是任意字符+短横线,所以会匹配到“b-”和“c-”?不对,匹配是从右向左的,模式*-匹配的是从开头到最后个短横线之间的所有内容?不对,模式*-的意思是:任意多个字符(尽可能多)后面跟个短横线,然后删除这部分。所以对于"a-b-c",删除最长的匹配(从右向左)就是删除“a-b-”(因为这是最长的匹配,即从开头到最后个短横线)?不对,模式匹配规则是从右向左的,所以实际上它匹配的是“b-”和“c-”吗?不对。 实际上,${var%%pattern}是从var的结尾开始删除最长的匹配pattern的字符串。pattern是*-,表示任意多个字符(包括空)后面跟个短横线,然后删除。所以它会删除从最后个短横线开始到字符串末尾的部分(包括最后个短横线)?不对,它删除的是从字符串开头到最后个短横线?也不对。 我们做实验: str="a-b-c" echo ${str%%-*} # 输出:a 为什么?因为%%表示删除最长的匹配,模式是*-,即任意多个字符(包括空)后面跟个短横线,从结尾开始匹配,最长的匹配是“a-b-”吗?不是,因为从结尾开始,要匹配个短横线,然后匹配任意多个字符(在短横线后面的部分)?这个模式匹配过程是从右向左的,但模式本身是*-,也就是个短横线前面有任意多个字符(这些字符从短横线左边开始)。 实际上,${str%%-*}的意思是:从字符串的结尾开始,删除最长的匹配*-的子串。*-可以匹配“任意多个字符(包括空)后跟个短横线”的部分,并且这个部分必须出现在字符串的末尾?不对,模式匹配不是必须从末尾开始,而是整个字符串中匹配。 更准确地说:${var%%pattern}中的pattern是通配符模式,不是正则表达式。模式*-表示:任意多个字符(包括零个)后面跟个短横线。而%%会删除最长的匹配(贪婪匹配),所以它会从字符串的末尾开始向左匹配,直到找到第个短横线(其实是从左到右找?)?实际上,通配符匹配是从左到右的,但%%是删除最长后缀,所以它会匹配整个字符串中最后个短横线以及它后面的所有内容吗?不对,看例子: str="a-b-c" ${str%%-*} 输出的是"a" 这是因为,模式*-匹配了从第个短横线开始到最后个短横线之间的部分(包括短横线)?不对,它实际上匹配了“-c”和“-b-c”两个可能,但%%是最长匹配,所以它会匹配最长的,即整个字符串中最后个短横线及其后面的部分?不对,模式是*-,即任意多个字符后面跟个短横线,那么它可以匹配: - 匹配第个短横线前面的部分(即a)吗?不行,因为模式要求后面跟个短横线,所以必须包含短横线。 - 匹配“a-b-”:这个字符串以短横线结尾,前面是任意字符(a-b),符合。 - 匹配“b-”:同样符合。 但是%%会匹配最长的,所以它匹配了“a-b-”,然后把它删除?那么剩下什么?剩下“c”吗?不对,因为整个字符串是“a-b-c”,删除“a-b-”剩下“c”。但我们得到的是“a”,所以显然不是这样。 实际上是:${str%%-*}表示从字符串中删除最长的匹配*-的后缀(后缀是指从某个位置到字符串末尾),所以它从左到右扫描,找到整个字符串中最后个短横线,然后删除从这个短横线到字符串末尾的部分(包括这个短横线)?不对,因为删除后缀应该是从末尾删除。 正确的解释是:${var%%pattern}中pattern是通配符模式,它匹配的是后缀。所以模式*-匹配的是以短横线结尾的任意长度的后缀。然后%%会删除最长匹配的后缀。对于"a-b-c",可能的后缀匹配有: - 匹配后缀“-c”:它是以短横线结尾的后缀吗?是的,它匹配*-(任意字符c后面跟短横线?不对,模式*-是任意多个字符后跟个短横线,而“-c”这个后缀中,最后是c,不是短横线,所以不匹配。 - 匹配后缀“-b-c”:同样,最后是c,不是短横线。 - 匹配后缀“c”:最后是c,不是短横线。 所以实际上没有个后缀匹配?那么就不删除,所以整个字符串不变?但实验结果是“a”。 实际上,模式匹配不是只匹配后缀,而是匹配整个字符串中任意位置的子串?不对,${var%%pattern}是删除后缀,所以模式必须匹配从某个位置到字符串末尾的部分。 重新理解:${var%%pattern}中的pattern是个通配符模式,它用于匹配var的后缀部分。因此,对于var="a-b-c",如果我们设置pattern="-*",那么: ${var%%-*} 这个pattern是"-*"吗?不对,我们给出的pattern是"-*",但这里写的是%%-*,所以pattern就是-*(个短横线后跟任意多个字符,直到末尾)。 那么,后缀匹配: - 后缀“-b-c”:匹配模式-*(个短横线后跟任意字符直到结尾) - 后缀“-c”:也匹配模式-* 但是%%会匹配最长的那个,所以匹配“-b-c”,删除后剩下“a”。 因此,${str%%-*} 的意思就是:删除从第个短横线开始(包括短横线)直到字符串末尾的部分。所以结果就是第个短横线之前的部分。 而如果字符串中没有短横线,则不会删除,返回整个字符串。 所以,在字符串中有多个短横线时,${str%%-*}会删除从第个短横线开始到末尾的部分(注意,是第个短横线开始,因为模式匹配是从左到右的?其实不是,%%是删除最长匹配的后缀,但实际上模式“-*”匹配的是从第个短横线开始到末尾的部分,因为短横线后面的任意字符包括更多的短横线,所以它匹配整个字符串中第个短横线到末尾的部分?不对,匹配的是整个字符串中任意个短横线开始到末尾的部分,而最长匹配就是整个字符串中第个短横线开始到末尾的部分(因为它最长)。 因此,${str%%-*} 就能得到第个短横线前面的部分。 但是,还有种方法:${str%%-*} 和 ${str%-*} 有什么区别? %:删除最短匹配后缀(非贪婪模式) %%:删除最长匹配后缀(贪婪模式) 对于模式“-*”,由于模式中有个星号(任意多个字符),所以无论最短还是最长,匹配的都是从第个短横线到末尾的整个部分。因此,%和%%效果样? 然而,我们再看个例子: str="a-b-c-d" ${str%%-*} => a ${str%-*} => a-b-c (因为%删除最短的后缀匹配,即只删除最后个短横线及其后面的部分,所以剩下a-b-c) 所以,我们要删除第个短横线后面的所有内容,应该用%%还是%?我们需要删除从第个短横线开始到末尾的部分,这个部分正好是匹配模式“-*”(即个短横线后跟任意多个字符)。而使用%%会删除最长的匹配,也就是整个字符串中第个短横线到末尾的部分(因为第个短横线到末尾的部分也是匹配的,而且是最长的)。而%会删除最短的匹配,也就是最后个短横线及其后面的部分(即“-d”),所以剩下的就是“a-b-c”。 因此,我们不应该用%或者%%来删除,因为我们要的是第个短横线前的部分,应该删除第个短横线及其后面的所有内容。所以我们用: ${str%%-*} # 因为匹配整个字符串中第个短横线到末尾的部分(这是最长的匹配后缀),删除后剩下第个短横线之前的部分。 因此,在示例中,str="arctic_native_k515-userdebug-androidt",${str%%-*} 会得到 "arctic_native_k515" 方法三:使用awk echo "arctic_native_k515-userdebug-androidt" | awk -F'-' '{print $1}' 方法四:使用sed echo "arctic_native_k515-userdebug-androidt" | sed 's/-.*//' 在shell脚本中,建议使用参数扩展,因为效率高(不需要启动子进程)。 因此,如果这个字符串在个变量中,我们可以这样写: str="arctic_native_k515-userdebug-androidt" prefix=${str%%-*} 如果字符串是从文件中读取的,或者来自命令输出,那么可以使用cut或awk。 根据问题,我们是在.sh文件中处理,所以如果字符串在变量中,就用参数扩展;如果是从其他地方获取,可以用管道和cut/awk。 问题没有说明字符串的来源,我们假设它在变量中,因为问题描述是“对这个字段进行分割”。 因此,我们可以写: #!/bin/sh str="arctic_native_k515-userdebug-androidt" prefix=${str%%-*} echo "$prefix" 输出:arctic_native_k515 注意:在sh中,参数扩展是POSIX标准,所以兼容性好。 所以,我们给出答案。 </think>在 `.sh` 文件中分割字符串 `arctic_native_k515-userdebug-androidt` 并提取第个 `-` 前的字段,可通过以下方法实现: ### 方法 1:使用参数扩展(推荐) ```bash #!/bin/sh str="arctic_native_k515-userdebug-androidt" prefix="${str%%-*}" # 删除第个"-"及其后的所有字符 echo "$prefix" # 输出: arctic_native_k515 ``` - **说明**: - `${str%%-*}` 使用 `%%` 删除**最长匹配的后缀**(即从第个 `-` 到字符串末尾的部分)[^1]。 - 效率高,无子进程开销,兼容所有 POSIX shell(如 `bash`/`dash`/`zsh`)。 ### 方法 2:使用 `cut` 命令 ```bash #!/bin/sh str="arctic_native_k515-userdebug-androidt" prefix=$(echo "$str" | cut -d '-' -f1) echo "$prefix" # 输出: arctic_native_k515 ``` - **说明**: - `cut -d '-' -f1` 以 `-` 为分隔符,提取第个字段[^2]。 - 简洁易读,但涉及子进程(管道和 `cut`)。 ### 方法 3:使用 `awk` ```bash #!/bin/sh str="arctic_native_k515-userdebug-androidt" prefix=$(echo "$str" | awk -F'-' '{print $1}') echo "$prefix" # 输出: arctic_native_k515 ``` - **说明**: - `awk -F'-'` 设置分隔符为 `-`,`{print $1}` 输出第列。 - 适合复杂文本处理,但效率低于参数扩展。 ### 验证结果 所有方法均输出: `arctic_native_k515` ### 注意事项 1. **变量赋值**:确保字符串用双引号包裹(如 `"$str"`),避免空格或特殊字符引发错误。 2. **效率排序**:参数扩展 > `cut` > `awk`(推荐优先使用参数扩展)。 3. **兼容性**:若需支持旧版 shell(如 `sh`),避免使用非 POSIX 语法(如 `[[ ]]`)。 --- ### 相关问题 1. 如何在 shell 中提取最后个 `-` 后的字段? 2. 如果字符串中有多个 `-`,如何提取第 N 个字段? 3. 如何检查字符串是否包含 `-` 后再进行分割? [^1]: Shell 参数扩展规则:`${var%%pattern}` 删除最长匹配后缀。 [^2]: `cut` 命令通过分隔符拆分字段。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值