最近遇到個(gè)問題,使用bugly還原后的堆棧,發(fā)現(xiàn)是一個(gè)空指針,但是那個(gè)地方無論如何也不可能為空。吃完飯回來,又繼續(xù)看了下,發(fā)現(xiàn)被bugly誤導(dǎo)了,不能只看還原出來的最后一行,由于沒有行號(hào),bugly是把所有可能都列出來了,要把上下文調(diào)用關(guān)系給串上才能知道是具體是哪一行crash了。借此我們熟悉下相關(guān)知識(shí)點(diǎn)吧。
mapping.txt說明
為了便于說明,假設(shè)有如下代碼:
package com.wx.demo;
import android.content.Context;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by wx on 11/11/2017.
*/
public class NetManager {
private Context mContext;
private NetManager gManager;
21 Map<String, String> getParams(String param){
22 Map<String, String> params;
23 if(TextUtils.isEmpty(param)){
24 params = null;
25 }else {
26 params = new HashMap<String, String>();
27 }
28 int size = params.size();
29 return params;
30 }
31
32 Map<String, String> getCommentsParams(String param){
33 Map<String, String> params;
34 if(TextUtils.isEmpty(param)){
35 params = null;
36 }else {
37 params = new HashMap<String, String>();
38 }
39 int size = params.size();
40 return params;
41 }
42
43 public List<Object> getFeeds(String param){
44 Map<String, String> params = getParams(param);
45 return new ArrayList<>();
46 }
47
48 public List<Object> getComments(String param){
49 Map<String, String> params = getCommentsParams(param);
50 return new ArrayList<>();
51 }
52}
混淆時(shí)關(guān)于源文件和行號(hào)有三種配置:
- 保留源文件名和行號(hào)
-keepattributes SourceFile,LineNumberTable
- 只保留行號(hào),源文件名用一個(gè)固定的string(如SourceFile)代替
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile
- 源文件名和行號(hào)都不保留
1和2的配置下mapping.txt為:
com.wx.demo.NetManager -> com.wx.demo.con:
com.wx.demo.NetManager gManager -> b
android.content.Context mContext -> e
22:29:java.util.Map getParams(java.lang.String) -> a
33:40:java.util.Map getCommomParams(java.lang.String) -> a
44:45:java.util.List getFeeds(java.lang.String) -> b
49:50:java.util.List getComments(java.lang.String) -> b
這里的數(shù)字對(duì)應(yīng)的是該函數(shù)所處的范圍,如getParams函數(shù)的是22到29行。
3的配置下mapping.txt為:
com.wx.demo.NetManager -> com.wx.demo.con:
com.wx.demo.NetManager gManager -> b
android.content.Context mContext -> e
java.util.Map getParams(java.lang.String) -> a
java.util.Map getCommomParams(java.lang.String) -> a
java.util.List getFeeds(java.lang.String) -> b
java.util.List getComments(java.lang.String) -> b
可以看到,不同的函數(shù)名是可能被混淆成相同的名字的,這在程序中并沒有什么問題,因?yàn)閰?shù)不同,即使函數(shù)名相同也沒問題,但是對(duì)應(yīng)到堆棧,如果行號(hào)沒有保留的話,那么從出錯(cuò)堆棧中就無法一一對(duì)應(yīng)的解析出。
解析crash堆棧
官方是提供了系統(tǒng)工具的,在sdk的<sdk-root>/tools/proguard/路徑下,使用方式:
retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]
例如:
retrace.bat -verbose mapping.txt obfuscated_trace.txt
在保留行號(hào)和源文件名的情況下,假如我們的堆棧是類似這樣的:
java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object java.util.Map.get(java.lang.Object)' on a null object reference
at com.wx.demo.NetManager.b(NetManager.java:28)
at com.wx.demo.NetManager.a(NetManager.java:44)
這里很明顯可以看到是getFeeds在調(diào)用getParams函數(shù)的28行時(shí)發(fā)生了空指針。
在不保留行號(hào)的情況下,堆棧是這樣的:
java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object java.util.Map.get(java.lang.Object)' on a null object reference
at com.wx.demo.NetManager.b(Unknown Source)
at com.wx.demo.NetManager.a(Unknown Source)
這種情況下,由于只有函數(shù)名,而函數(shù)名b和a都對(duì)應(yīng)了源碼的兩個(gè)函數(shù),就沒法知道是getFeeds調(diào)用getParams時(shí)發(fā)生了空指針,還是getComments調(diào)用getCommentsParams時(shí)發(fā)生了空指針了,只能根據(jù)更上層的調(diào)用堆??茨膫€(gè)才是正確的路徑。這種情況下使用retrace.sh解析出來的堆棧會(huì)把每一步可能的堆棧都給列出來,類似這樣:
java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object java.util.Map.get(java.lang.Object)' on a null object reference
at java.util.Map getParams(Unknown Source)
java.util.Map getCommentsParams(java.lang.String)
at java.util.List getFeeds(java.lang.String)(Unknown Source)
java.util.List getComments(java.lang.String)
總結(jié)
本文由一個(gè)堆棧還原問題,把mapping.txt及其還原做了個(gè)介紹,希望對(duì)今后通過堆棧定位問題有幫助。
reference:
https://developer.android.com/studio/build/shrink-code.html#decoding
https://www.guardsquare.com/en/proguard/manual/retrace