混淆堆棧沒法定位問題?沒準(zhǔn)是堆棧還原問題

最近遇到個(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)有三種配置:

  1. 保留源文件名和行號(hào)
-keepattributes SourceFile,LineNumberTable
  1. 只保留行號(hào),源文件名用一個(gè)固定的string(如SourceFile)代替
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile
  1. 源文件名和行號(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Lua 5.1 參考手冊(cè) by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 14,235評(píng)論 0 38
  • 原文地址:C語言函數(shù)調(diào)用棧(一)C語言函數(shù)調(diào)用棧(二) 0 引言 程序的執(zhí)行過程可看作連續(xù)的函數(shù)調(diào)用。當(dāng)一個(gè)函數(shù)執(zhí)...
    小豬啊嗚閱讀 4,957評(píng)論 1 19
  • 前言 相信做過app的同學(xué)對(duì)代碼混淆應(yīng)該不陌生吧,如果陌生就自行百度,這里不做普及。我們先思考一個(gè)問題,如果我們把...
    IT_xiao小巫閱讀 2,273評(píng)論 3 2
  • 聲明 這篇文章更多的是做一個(gè)整理,內(nèi)容來自于ProGuard官方文檔以及各種博客等,相關(guān)文章的鏈接在參考目錄里,感...
    夷陵小祖閱讀 3,793評(píng)論 0 23
  • “去年元夜時(shí),花市燈如晝。 月到柳梢頭,人約黃昏后。 今年元夜時(shí),月與燈依舊。不見去年人,淚濕春衫袖?!痹?..
    楊博寧閱讀 263評(píng)論 2 0

友情鏈接更多精彩內(nèi)容