├── LICENSE.html ├── README.md ├── bin ├── acrash-report.bat ├── acrash-report.jar └── acrash-report.sh ├── build ├── build.command └── build.sh ├── doc ├── ArgParser.html ├── CrashGroup.html ├── CrashItem.html ├── CrashParser.html ├── CrashType.html ├── FileHelper.html ├── Main.html ├── allclasses-frame.html ├── allclasses-noframe.html ├── constant-values.html ├── deprecated-list.html ├── help-doc.html ├── index-all.html ├── index.html ├── overview-tree.html ├── package-frame.html ├── package-list ├── package-summary.html ├── package-tree.html ├── script.js └── stylesheet.css ├── screenshot └── weixin.jpg └── src ├── ArgParser.java ├── CrashGroup.java ├── CrashItem.java ├── CrashParser.java ├── CrashType.java ├── FileHelper.java └── Main.java /README.md: -------------------------------------------------------------------------------- 1 | AndroidCrashReport 2 | ================= 3 | AndroidCrashReport用于分析Android崩溃日志(java层和c层的都支持),统计日志崩溃信息,对日志整理归类,最终形成一个以崩溃触发函数分类,并按照崩溃次数从大到小排列的崩溃报表。 4 | 通过此报表我们可以清楚的看到,哪种类型的崩溃发生次数最多,崩溃的概率有多大。以方便我们对发生次数多,崩溃概率大的BUG,优先处理。 5 | 6 | AndroidCrashReport架构图 7 | ----------------- 8 | 无 9 | 10 | AndroidCrashReport主要功能 11 | ----------------------------------- 12 | 1. 统计java日志,生成报表 13 | 2. 根据符号表自动解析C层日志,统计C层日志,生成报表 14 | 15 | AndroidCrashReport Limitations 16 | ----------------------------------- 17 | 1. 报表以txt文件格式的方式展现,不支持图表 18 | 2. 分析C日志的耗时较长。 19 | 20 | AndroidCrashReport工作原理 21 | ------------------------------------------------------------------------------------ 22 | 1. 从崩溃的文件中读取一条崩溃日志 23 | 2. 判断日志的类型,如果是C层的崩溃(以*** ***开头的),则执行步骤3.否则认为日志是java层的崩溃,执行步骤4 24 | 3. 调用Android NDK安装包下的ndk-stack命令,将日志解析成带符号的崩溃日志。(因为SO一般都设置了隐藏符号表,这时崩溃日志里面看不到崩溃的地址,看不到函数名,和文件的行号) 25 | 4. 分析日志的格式,生成日志的ID(ID相同的日志,具有相同崩溃信息),提取触发崩溃的函数,并用此函数名作为keyword 26 | ID相同表示崩溃日志完全一样,即崩溃时的调用堆栈完全一样,keyword相同只表示,触发崩溃的函数是一样的,但调用堆栈有可能不一样 27 | ID用于标识一个崩溃,而keyword用于标识一类崩溃 28 | 5. 用ID进行初次统计,计算得到每个崩溃发生的次数 29 | 6. 用keyword再次统计,计算得到每类崩溃发生的次数 30 | 31 | 32 | 安装(编译脚本在build目录下) 33 | ---------------------------------- 34 | ``` 35 | 环境:jdk1.7以上 36 | Linux: 37 | cd build 38 | sh build.sh 39 | Mac: 40 | 双击build.command 41 | Windows: 42 | 双击build.bat 43 | 44 | 安装后会在bin目录下生成如下可执行文件: 45 | acrash-report.jar 可执行jar包, 46 | acrash-report.bat windows下使用此批处理文件对acrash-report.jar进行了包装 47 | acrash-report.sh Linux和Mac下使用此Shell脚本对acrash-report.jar进行了包装 48 | 49 | 50 | ``` 51 | 52 | acrash-report命令使用方法 53 | --------------------------------------- 54 | Usage: 55 | acrash-report -sym [-dump ] 56 | 57 | -dump Contains full path to the file containing the crash dump. 58 | -sym Contains full path to the root directory for symbols. 59 | This is an optional parameter. If ommited, C crash will ignore. 60 | 注意:如果需要编译C层崩溃,请将Android ndk目录下的ndk-stack命令加入PATH环境变量。 61 | 62 | 报表文件说明(acrash-report命令生成) 63 | --------------------------------------------------------------------------------- 64 | ``` 65 | XX_crash_report 报表目录 66 | c_report.txt C层崩溃报表,此报表中只列出每种崩溃中崩溃次数最多的两个崩溃。 67 | c_detail_report.txt C层崩溃详细报表,此报表列出每种崩溃的所有崩溃。 68 | java_report.txt java层崩溃报表,此报表中只列出每种崩溃中崩溃次数最多的两个崩溃。 69 | java_detail_report.txt java层崩溃详细报表,此报表列出每种崩溃的所有崩溃。 70 | invalid.txt 无效的崩溃日志,将会输出在此文件。 71 | ``` 72 | 73 | 崩溃日志格式 74 | ------------------------------------------------------- 75 | 格式1: 每条崩溃占一行,行内使用\\n进行分隔。 76 | 格式2: 每条崩溃占多行,但两条日志之间需要有一个空行分隔。 77 | 78 | 引用 79 | ----------------------------- 80 | 无 81 | 82 | 83 | 注意 84 | ----------------------------- 85 | AndroidCrashReport工具非常简单,近期也不会对AndroidCrashReport做出大的修改 86 | 如果有小的bug或者建议,请告知我,我会第一时间修复。 87 | 88 | ![Weixin QR Code](screenshot/weixin.jpg) 89 | -------------------------------------------------------------------------------- /bin/acrash-report.bat: -------------------------------------------------------------------------------- 1 | cd ${0%/*} 2 | java -jar acrash-report.jar $@ 3 | -------------------------------------------------------------------------------- /bin/acrash-report.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ireaderlab/AndroidCrashReport/a6d09cbf2033488a3d8686da4a6f427644f03d89/bin/acrash-report.jar -------------------------------------------------------------------------------- /bin/acrash-report.sh: -------------------------------------------------------------------------------- 1 | cd `dirname $0` 2 | java -jar acrash-report.jar $@ 3 | -------------------------------------------------------------------------------- /build/build.command: -------------------------------------------------------------------------------- 1 | sh ${0%/*}/build.sh -------------------------------------------------------------------------------- /build/build.sh: -------------------------------------------------------------------------------- 1 | cd `dirname $0` 2 | cd .. 3 | rm -rf bin 4 | mkdir bin 5 | 6 | rm -rf output 7 | mkdir output 8 | javac -d output src/*.java 9 | echo "Main-Class: Main" > output/Manifest.mf 10 | cd output 11 | jar -cvfm ../bin/acrash-report.jar Manifest.mf *.class 12 | cd .. 13 | rm -rf output 14 | 15 | echo 'cd `dirname $0`' > bin/acrash-report.sh 16 | echo 'java -jar acrash-report.jar $@' >> bin/acrash-report.sh 17 | echo 'cd ${0%/*}' > bin/acrash-report.bat 18 | echo 'java -jar acrash-report.jar $@' >> bin/acrash-report.bat 19 | -------------------------------------------------------------------------------- /doc/ArgParser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ArgParser 8 | 9 | 10 | 11 | 12 | 13 | 29 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 48 |
49 | 91 | 92 | 93 |
94 |

类 ArgParser

95 |
96 |
97 |
    98 |
  • java.lang.Object
  • 99 |
  • 100 |
      101 |
    • ArgParser
    • 102 |
    103 |
  • 104 |
105 |
106 |
    107 |
  • 108 |
    109 |
    110 |
    public class ArgParser
    111 | extends java.lang.Object
    112 |
    此类用来处理命令行参数。
    113 |
  • 114 |
115 |
116 |
117 |
    118 |
  • 119 | 120 |
      121 |
    • 122 | 123 | 124 |

      构造器概要

      125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 |
      构造器 
      构造器和说明
      ArgParser() 
      134 |
    • 135 |
    136 | 137 |
      138 |
    • 139 | 140 | 141 |

      方法概要

      142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 165 | 166 |
      All Methods Instance Methods Concrete Methods 
      限定符和类型方法和说明
      java.lang.StringgetDumpParam() 
      intgetMaxParam() 
      java.lang.StringgetSymParam() 
      booleanparse(java.lang.String[] args) 163 |
      解析命令行参数,并验证命令行参数。
      164 |
      167 |
        168 |
      • 169 | 170 | 171 |

        从类继承的方法 java.lang.Object

        172 | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • 173 |
      174 |
    • 175 |
    176 |
  • 177 |
178 |
179 |
180 |
    181 |
  • 182 | 183 |
      184 |
    • 185 | 186 | 187 |

      构造器详细资料

      188 | 189 | 190 | 191 |
        192 |
      • 193 |

        ArgParser

        194 |
        public ArgParser()
        195 |
      • 196 |
      197 |
    • 198 |
    199 | 200 |
      201 |
    • 202 | 203 | 204 |

      方法详细资料

      205 | 206 | 207 | 208 |
        209 |
      • 210 |

        parse

        211 |
        public boolean parse(java.lang.String[] args)
        212 |
        解析命令行参数,并验证命令行参数。
        213 |
        214 |
        参数:
        215 |
        args - 命令行参数
        216 |
        返回:
        217 |
        成功返回true,否则返回false
        218 |
        219 |
      • 220 |
      221 | 222 | 223 | 224 |
        225 |
      • 226 |

        getSymParam

        227 |
        public java.lang.String getSymParam()
        228 |
        229 |
        返回:
        230 |
        返回SO符号表的路径
        231 |
        232 |
      • 233 |
      234 | 235 | 236 | 237 |
        238 |
      • 239 |

        getDumpParam

        240 |
        public java.lang.String getDumpParam()
        241 |
        242 |
        返回:
        243 |
        返回崩溃日志路径
        244 |
        245 |
      • 246 |
      247 | 248 | 249 | 250 |
        251 |
      • 252 |

        getMaxParam

        253 |
        public int getMaxParam()
        254 |
        255 |
        返回:
        256 |
        返回最多分析多少条日志
        257 |
        258 |
      • 259 |
      260 |
    • 261 |
    262 |
  • 263 |
264 |
265 |
266 | 267 | 268 |
269 | 270 | 271 | 272 | 273 | 274 | 275 | 283 |
284 | 326 | 327 | 328 | 329 | -------------------------------------------------------------------------------- /doc/CrashGroup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | CrashGroup 8 | 9 | 10 | 11 | 12 | 13 | 29 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 48 |
49 | 91 | 92 | 93 |
94 |

类 CrashGroup

95 |
96 |
97 |
    98 |
  • java.lang.Object
  • 99 |
  • 100 |
      101 |
    • CrashGroup
    • 102 |
    103 |
  • 104 |
105 |
106 |
    107 |
  • 108 |
    109 |
    110 |
    public class CrashGroup
    111 | extends java.lang.Object
    112 |
    此类表示一个崩溃组,代表一类崩溃。一个崩溃组里面的崩溃具有相同的崩溃关键字和崩溃类型。
    113 |
  • 114 |
115 |
116 |
117 |
    118 |
  • 119 | 120 |
      121 |
    • 122 | 123 | 124 |

      构造器概要

      125 | 126 | 127 | 128 | 129 | 130 | 131 | 134 | 135 |
      构造器 
      构造器和说明
      CrashGroup(CrashItem item) 132 |
      构造一个崩溃组。
      133 |
      136 |
    • 137 |
    138 | 139 |
      140 |
    • 141 | 142 | 143 |

      方法概要

      144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 155 | 156 | 157 | 158 | 161 | 162 | 163 | 164 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 185 | 186 | 187 | 188 | 191 | 192 | 193 | 194 | 197 | 198 |
      All Methods Instance Methods Concrete Methods 
      限定符和类型方法和说明
      booleanaddCrash(CrashItem item) 153 |
      添加一个CrashItem。
      154 |
      CrashItemfindSameCrash(CrashItem item) 159 |
      在组内寻找一个相同的CrashItem。
      160 |
      CrashItemget(int index) 165 |
      获得一个崩溃。
      166 |
      intgetCrashCount() 
      CrashTypegetCrashType() 
      java.lang.StringgetKeyword() 
      booleanmatchCrash(CrashItem item) 183 |
      crashItem是否可以添加到该组。
      184 |
      intsize() 189 |
      获得崩溃的数量。
      190 |
      voidsort() 195 |
      按照崩溃次数降序排列。
      196 |
      199 |
        200 |
      • 201 | 202 | 203 |

        从类继承的方法 java.lang.Object

        204 | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • 205 |
      206 |
    • 207 |
    208 |
  • 209 |
210 |
211 |
212 |
    213 |
  • 214 | 215 |
      216 |
    • 217 | 218 | 219 |

      构造器详细资料

      220 | 221 | 222 | 223 |
        224 |
      • 225 |

        CrashGroup

        226 |
        public CrashGroup(CrashItem item)
        227 |
        构造一个崩溃组。
        228 |
        229 |
        参数:
        230 |
        item - 第一个CrashItem,不能为null。
        231 |
        232 |
      • 233 |
      234 |
    • 235 |
    236 | 237 |
      238 |
    • 239 | 240 | 241 |

      方法详细资料

      242 | 243 | 244 | 245 |
        246 |
      • 247 |

        size

        248 |
        public int size()
        249 |
        获得崩溃的数量。
        250 |
        251 |
        返回:
        252 |
        返回崩溃组里面的CrashItem的数量。
        253 |
        254 |
      • 255 |
      256 | 257 | 258 | 259 |
        260 |
      • 261 |

        get

        262 |
        public CrashItem get(int index)
        263 |
        获得一个崩溃。
        264 |
        265 |
        参数:
        266 |
        index - 索引。
        267 |
        返回:
        268 |
        返回一个CrashItem
        269 |
        270 |
      • 271 |
      272 | 273 | 274 | 275 |
        276 |
      • 277 |

        matchCrash

        278 |
        public boolean matchCrash(CrashItem item)
        279 |
        crashItem是否可以添加到该组。
        280 |
        281 |
        参数:
        282 |
        item - 要匹配的CrashItem。
        283 |
        返回:
        284 |
        如果item能添加到组内,返回true,否则返回false.
        285 |
        286 |
      • 287 |
      288 | 289 | 290 | 291 |
        292 |
      • 293 |

        addCrash

        294 |
        public boolean addCrash(CrashItem item)
        295 |
        添加一个CrashItem。
        296 |
        297 |
        参数:
        298 |
        item - 一个Crashitem。
        299 |
        返回:
        300 |
        成功添加返回true,否则返回false。
        301 |
        302 |
      • 303 |
      304 | 305 | 306 | 307 |
        308 |
      • 309 |

        findSameCrash

        310 |
        public CrashItem findSameCrash(CrashItem item)
        311 |
        在组内寻找一个相同的CrashItem。
        312 |
        313 |
        参数:
        314 |
        item - 要寻找的CrashItem。
        315 |
        返回:
        316 |
        如果找到,返回该对象,否则返回null。
        317 |
        318 |
      • 319 |
      320 | 321 | 322 | 323 |
        324 |
      • 325 |

        getKeyword

        326 |
        public java.lang.String getKeyword()
        327 |
        328 |
        返回:
        329 |
        返回崩溃关键词。
        330 |
        331 |
      • 332 |
      333 | 334 | 335 | 336 |
        337 |
      • 338 |

        getCrashType

        339 |
        public CrashType getCrashType()
        340 |
        341 |
        返回:
        342 |
        返回崩溃类型。
        343 |
        344 |
      • 345 |
      346 | 347 | 348 | 349 |
        350 |
      • 351 |

        getCrashCount

        352 |
        public int getCrashCount()
        353 |
        354 |
        返回:
        355 |
        返回所有的CrashItem的崩溃次数累加之和。
        356 |
        357 |
      • 358 |
      359 | 360 | 361 | 362 |
        363 |
      • 364 |

        sort

        365 |
        public void sort()
        366 |
        按照崩溃次数降序排列。
        367 |
      • 368 |
      369 |
    • 370 |
    371 |
  • 372 |
373 |
374 |
375 | 376 | 377 |
378 | 379 | 380 | 381 | 382 | 383 | 384 | 392 |
393 | 435 | 436 | 437 | 438 | -------------------------------------------------------------------------------- /doc/CrashItem.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | CrashItem 8 | 9 | 10 | 11 | 12 | 13 | 29 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 48 |
49 | 91 | 92 | 93 |
94 |

类 CrashItem

95 |
96 |
97 |
    98 |
  • java.lang.Object
  • 99 |
  • 100 |
      101 |
    • CrashItem
    • 102 |
    103 |
  • 104 |
105 |
106 |
    107 |
  • 108 |
    109 |
    110 |
    public class CrashItem
    111 | extends java.lang.Object
    112 |
    此类表示一个崩溃日志,一个崩溃日志有两种类型:java或者C。 113 | 一个崩溃日志可以是单行的(行内用\\n分隔),也可以是多行的。 114 | 对于一个崩溃日志的字符串,有三种状态: 115 | 原始状态:崩溃日志是单行的,日志包含\\n,\\t等转义字符。 116 | 中间状态:替换掉原始状态的转义字符。 117 | 符号状态:日志包含明确的崩溃函数的名称和源文件行号。 118 | 对于c日志,符号状态意味着调用“ndk-stack”命令将中间状态的日志解析之后的结果。 119 | 对于java日志,符号状态和中间状态相同,因为java不需要解析符号表。
    120 |
  • 121 |
122 |
123 |
124 |
    125 |
  • 126 | 127 |
      128 |
    • 129 | 130 | 131 |

      构造器概要

      132 | 133 | 134 | 135 | 136 | 137 | 138 | 142 | 143 |
      构造器 
      构造器和说明
      CrashItem(java.lang.String crashString, 139 | boolean isMutiline) 140 |
      构造一个崩溃日志
      141 |
      144 |
    • 145 |
    146 | 147 |
      148 |
    • 149 | 150 | 151 |

      方法概要

      152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 163 | 164 | 165 | 166 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 207 | 208 |
      All Methods Instance Methods Concrete Methods 
      限定符和类型方法和说明
      voidaddCrashCount() 161 |
      增加崩溃次数。
      162 |
      booleanequals(java.lang.Object o) 167 |
      比较两个崩溃是否相等。
      168 |
      intgetCrashCount() 
      java.lang.StringgetCrashId() 
      CrashTypegetCrashType() 
      java.lang.StringgetKeyword() 
      java.lang.StringgetMidCrashString() 
      java.lang.StringgetOriginCrashString() 
      java.lang.StringgetSymbolCrashString() 
      booleanisValid() 
      voidsetCSymbolCrashLines(java.util.ArrayList<java.lang.String> crashLines) 205 |
      设置C崩溃符号字符串,确定崩溃ID,崩溃关键词.
      206 |
      209 |
        210 |
      • 211 | 212 | 213 |

        从类继承的方法 java.lang.Object

        214 | clone, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • 215 |
      216 |
    • 217 |
    218 |
  • 219 |
220 |
221 |
222 |
    223 |
  • 224 | 225 |
      226 |
    • 227 | 228 | 229 |

      构造器详细资料

      230 | 231 | 232 | 233 |
        234 |
      • 235 |

        CrashItem

        236 |
        public CrashItem(java.lang.String crashString,
        237 |                  boolean isMutiline)
        238 |
        构造一个崩溃日志
        239 |
        240 |
        参数:
        241 |
        crashString - 崩溃的原始字符串。
        242 |
        isMutiline - 崩溃日志是否是多行的,对于多行日志,原始字符串和中间字符串是一样的。
        243 |
        244 |
      • 245 |
      246 |
    • 247 |
    248 | 249 |
      250 |
    • 251 | 252 | 253 |

      方法详细资料

      254 | 255 | 256 | 257 |
        258 |
      • 259 |

        setCSymbolCrashLines

        260 |
        public void setCSymbolCrashLines(java.util.ArrayList<java.lang.String> crashLines)
        261 |
        设置C崩溃符号字符串,确定崩溃ID,崩溃关键词.
        262 |
        263 |
        参数:
        264 |
        crashLines - 符号字符串,"ndk-stack"解析后的字符串。
        265 |
        266 |
      • 267 |
      268 | 269 | 270 | 271 |
        272 |
      • 273 |

        getMidCrashString

        274 |
        public java.lang.String getMidCrashString()
        275 |
        276 |
        返回:
        277 |
        返回中间字符串。
        278 |
        279 |
      • 280 |
      281 | 282 | 283 | 284 |
        285 |
      • 286 |

        getSymbolCrashString

        287 |
        public java.lang.String getSymbolCrashString()
        288 |
        289 |
        返回:
        290 |
        返回符号字符串。
        291 |
        292 |
      • 293 |
      294 | 295 | 296 | 297 |
        298 |
      • 299 |

        getOriginCrashString

        300 |
        public java.lang.String getOriginCrashString()
        301 |
        302 |
        返回:
        303 |
        返回原始字符串。
        304 |
        305 |
      • 306 |
      307 | 308 | 309 | 310 |
        311 |
      • 312 |

        isValid

        313 |
        public boolean isValid()
        314 |
        315 |
        返回:
        316 |
        日志是否是有效的。
        317 |
        318 |
      • 319 |
      320 | 321 | 322 | 323 |
        324 |
      • 325 |

        addCrashCount

        326 |
        public void addCrashCount()
        327 |
        增加崩溃次数。
        328 |
      • 329 |
      330 | 331 | 332 | 333 |
        334 |
      • 335 |

        getCrashCount

        336 |
        public int getCrashCount()
        337 |
        338 |
        返回:
        339 |
        获得崩溃次数。
        340 |
        341 |
      • 342 |
      343 | 344 | 345 | 346 |
        347 |
      • 348 |

        getCrashId

        349 |
        public java.lang.String getCrashId()
        350 |
        351 |
        返回:
        352 |
        返回崩溃ID。
        353 |
        354 |
      • 355 |
      356 | 357 | 358 | 359 |
        360 |
      • 361 |

        getKeyword

        362 |
        public java.lang.String getKeyword()
        363 |
        364 |
        返回:
        365 |
        返回崩溃关键字。
        366 |
        367 |
      • 368 |
      369 | 370 | 371 | 372 |
        373 |
      • 374 |

        getCrashType

        375 |
        public CrashType getCrashType()
        376 |
        377 |
        返回:
        378 |
        返回崩溃类型。
        379 |
        380 |
      • 381 |
      382 | 383 | 384 | 385 |
        386 |
      • 387 |

        equals

        388 |
        public boolean equals(java.lang.Object o)
        389 |
        比较两个崩溃是否相等。
        390 |
        391 |
        覆盖:
        392 |
        equals 在类中 java.lang.Object
        393 |
        返回:
        394 |
        如果崩溃ID一样返回true,否则返回false。
        395 |
        另请参阅:
        396 |
        Object.equals(java.lang.Object)
        397 |
        398 |
      • 399 |
      400 |
    • 401 |
    402 |
  • 403 |
404 |
405 |
406 | 407 | 408 |
409 | 410 | 411 | 412 | 413 | 414 | 415 | 423 |
424 | 466 | 467 | 468 | 469 | -------------------------------------------------------------------------------- /doc/CrashParser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | CrashParser 8 | 9 | 10 | 11 | 12 | 13 | 29 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 48 |
49 | 91 | 92 | 93 |
94 |

类 CrashParser

95 |
96 |
97 |
    98 |
  • java.lang.Object
  • 99 |
  • 100 |
      101 |
    • CrashParser
    • 102 |
    103 |
  • 104 |
105 |
106 |
    107 |
  • 108 |
    109 |
    110 |
    public class CrashParser
    111 | extends java.lang.Object
    112 |
  • 113 |
114 |
115 |
116 |
    117 |
  • 118 | 119 |
      120 |
    • 121 | 122 | 123 |

      构造器概要

      124 | 125 | 126 | 127 | 128 | 129 | 130 | 135 | 136 |
      构造器 
      构造器和说明
      CrashParser(java.lang.String dumpFilePath, 131 | java.lang.String symbolFilePath, 132 | int crashMax) 133 |
      构建一个崩溃解析器。
      134 |
      137 |
    • 138 |
    139 | 140 |
      141 |
    • 142 | 143 | 144 |

      方法概要

      145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 156 | 157 |
      All Methods Instance Methods Concrete Methods 
      限定符和类型方法和说明
      voidparseCrash() 154 |
      解析日志,一条日志有可能是单行的(以\\n分隔),也有可能是多行的。
      155 |
      158 |
        159 |
      • 160 | 161 | 162 |

        从类继承的方法 java.lang.Object

        163 | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • 164 |
      165 |
    • 166 |
    167 |
  • 168 |
169 |
170 |
171 |
    172 |
  • 173 | 174 |
      175 |
    • 176 | 177 | 178 |

      构造器详细资料

      179 | 180 | 181 | 182 |
        183 |
      • 184 |

        CrashParser

        185 |
        public CrashParser(java.lang.String dumpFilePath,
        186 |                    java.lang.String symbolFilePath,
        187 |                    int crashMax)
        188 |
        构建一个崩溃解析器。
        189 |
        190 |
        参数:
        191 |
        dumpFilePath - dump文件路径。
        192 |
        symbolFilePath - SO符号表路径。
        193 |
        crashMax - 最多从mDumpFilePath文件读取多少条崩溃。
        194 |
        195 |
      • 196 |
      197 |
    • 198 |
    199 | 200 |
      201 |
    • 202 | 203 | 204 |

      方法详细资料

      205 | 206 | 207 | 208 |
        209 |
      • 210 |

        parseCrash

        211 |
        public void parseCrash()
        212 |                 throws java.io.IOException,
        213 |                        java.lang.InterruptedException
        214 |
        解析日志,一条日志有可能是单行的(以\\n分隔),也有可能是多行的。 215 | 解析器每次读取一行日志,判断日志类型.
        216 |
        217 |
        抛出:
        218 |
        java.io.IOException - 如果发生IO异常
        219 |
        java.lang.InterruptedException - 如果发生异常
        220 |
        221 |
      • 222 |
      223 |
    • 224 |
    225 |
  • 226 |
227 |
228 |
229 | 230 | 231 |
232 | 233 | 234 | 235 | 236 | 237 | 238 | 246 |
247 | 289 | 290 | 291 | 292 | -------------------------------------------------------------------------------- /doc/CrashType.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | CrashType 8 | 9 | 10 | 11 | 12 | 13 | 29 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 48 |
49 | 91 | 92 | 93 |
94 |

枚举 CrashType

95 |
96 |
97 |
    98 |
  • java.lang.Object
  • 99 |
  • 100 |
      101 |
    • java.lang.Enum<CrashType>
    • 102 |
    • 103 |
        104 |
      • CrashType
      • 105 |
      106 |
    • 107 |
    108 |
  • 109 |
110 |
111 |
    112 |
  • 113 |
    114 |
    所有已实现的接口:
    115 |
    java.io.Serializable, java.lang.Comparable<CrashType>
    116 |
    117 |
    118 |
    119 |
    public enum CrashType
    120 | extends java.lang.Enum<CrashType>
    121 |
    此枚举表示一个崩溃类型。
    122 |
  • 123 |
124 |
125 |
126 |
    127 |
  • 128 | 129 |
      130 |
    • 131 | 132 | 133 |

      枚举常量概要

      134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 |
      枚举常量 
      枚举常量和说明
      C_CRASH 
      JAVA_CRASH 
      146 |
    • 147 |
    148 | 149 |
      150 |
    • 151 | 152 | 153 |

      方法概要

      154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 165 | 166 | 167 | 168 | 172 | 173 |
      All Methods Static Methods Concrete Methods 
      限定符和类型方法和说明
      static CrashTypevalueOf(java.lang.String name) 163 |
      返回带有指定名称的该类型的枚举常量。
      164 |
      static CrashType[]values() 169 |
      按照声明该枚举类型的常量的顺序, 返回 170 | 包含这些常量的数组。
      171 |
      174 |
        175 |
      • 176 | 177 | 178 |

        从类继承的方法 java.lang.Enum

        179 | clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
      • 180 |
      181 |
        182 |
      • 183 | 184 | 185 |

        从类继承的方法 java.lang.Object

        186 | getClass, notify, notifyAll, wait, wait, wait
      • 187 |
      188 |
    • 189 |
    190 |
  • 191 |
192 |
193 |
194 |
    195 |
  • 196 | 197 | 222 | 223 |
      224 |
    • 225 | 226 | 227 |

      方法详细资料

      228 | 229 | 230 | 231 |
        232 |
      • 233 |

        values

        234 |
        public static CrashType[] values()
        235 |
        按照声明该枚举类型的常量的顺序, 返回 236 | 包含这些常量的数组。该方法可用于迭代 237 | 常量, 如下所示: 238 |
        239 | for (CrashType c : CrashType.values())
        240 |     System.out.println(c);
        241 | 
        242 |
        243 |
        返回:
        244 |
        按照声明该枚举类型的常量的顺序返回的包含这些常量的数组
        245 |
        246 |
      • 247 |
      248 | 249 | 250 | 251 |
        252 |
      • 253 |

        valueOf

        254 |
        public static CrashType valueOf(java.lang.String name)
        255 |
        返回带有指定名称的该类型的枚举常量。 256 | 字符串必须与用于声明该类型的枚举常量的 257 | 标识符完全匹配。(不允许有多余 258 | 的空格字符。)
        259 |
        260 |
        参数:
        261 |
        name - 要返回的枚举常量的名称。
        262 |
        返回:
        263 |
        返回带有指定名称的枚举常量
        264 |
        抛出:
        265 |
        java.lang.IllegalArgumentException - 如果该枚举类型没有带有指定名称的常量
        266 |
        java.lang.NullPointerException - 如果参数为空值
        267 |
        268 |
      • 269 |
      270 |
    • 271 |
    272 |
  • 273 |
274 |
275 |
276 | 277 | 278 |
279 | 280 | 281 | 282 | 283 | 284 | 285 | 293 |
294 | 336 | 337 | 338 | 339 | -------------------------------------------------------------------------------- /doc/FileHelper.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | FileHelper 8 | 9 | 10 | 11 | 12 | 13 | 29 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 48 |
49 | 91 | 92 | 93 |
94 |

类 FileHelper

95 |
96 |
97 |
    98 |
  • java.lang.Object
  • 99 |
  • 100 |
      101 |
    • FileHelper
    • 102 |
    103 |
  • 104 |
105 |
106 |
    107 |
  • 108 |
    109 |
    110 |
    public class FileHelper
    111 | extends java.lang.Object
    112 |
    此类用来帮助操作文件
    113 |
  • 114 |
115 |
116 |
117 |
    118 |
  • 119 | 120 |
      121 |
    • 122 | 123 | 124 |

      构造器概要

      125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 |
      构造器 
      构造器和说明
      FileHelper() 
      134 |
    • 135 |
    136 | 137 |
      138 |
    • 139 | 140 | 141 |

      方法概要

      142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 153 | 154 | 155 | 156 | 159 | 160 |
      All Methods Static Methods Concrete Methods 
      限定符和类型方法和说明
      static voiddelete(java.lang.String filePath) 151 |
      删除一个文件,文件夹(文件夹可以非空)。
      152 |
      static java.lang.StringgetFileName(java.lang.String path) 157 |
      获得一个文件路径的文件名。
      158 |
      161 |
        162 |
      • 163 | 164 | 165 |

        从类继承的方法 java.lang.Object

        166 | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • 167 |
      168 |
    • 169 |
    170 |
  • 171 |
172 |
173 |
174 |
    175 |
  • 176 | 177 |
      178 |
    • 179 | 180 | 181 |

      构造器详细资料

      182 | 183 | 184 | 185 |
        186 |
      • 187 |

        FileHelper

        188 |
        public FileHelper()
        189 |
      • 190 |
      191 |
    • 192 |
    193 | 194 |
      195 |
    • 196 | 197 | 198 |

      方法详细资料

      199 | 200 | 201 | 202 |
        203 |
      • 204 |

        delete

        205 |
        public static void delete(java.lang.String filePath)
        206 |
        删除一个文件,文件夹(文件夹可以非空)。
        207 |
        208 |
        参数:
        209 |
        filePath - 文件路径。
        210 |
        211 |
      • 212 |
      213 | 214 | 215 | 216 |
        217 |
      • 218 |

        getFileName

        219 |
        public static java.lang.String getFileName(java.lang.String path)
        220 |
        获得一个文件路径的文件名。
        221 |
        222 |
        参数:
        223 |
        path - 文件路径。
        224 |
        返回:
        225 |
        返回文件名。
        226 |
        227 |
      • 228 |
      229 |
    • 230 |
    231 |
  • 232 |
233 |
234 |
235 | 236 | 237 |
238 | 239 | 240 | 241 | 242 | 243 | 244 | 252 |
253 | 295 | 296 | 297 | 298 | -------------------------------------------------------------------------------- /doc/Main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Main 8 | 9 | 10 | 11 | 12 | 13 | 29 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 48 |
49 | 91 | 92 | 93 |
94 |

类 Main

95 |
96 |
97 |
    98 |
  • java.lang.Object
  • 99 |
  • 100 |
      101 |
    • Main
    • 102 |
    103 |
  • 104 |
105 |
106 |
    107 |
  • 108 |
    109 |
    110 |
    public class Main
    111 | extends java.lang.Object
    112 |
  • 113 |
114 |
115 |
116 |
    117 |
  • 118 | 119 |
      120 |
    • 121 | 122 | 123 |

      构造器概要

      124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 |
      构造器 
      构造器和说明
      Main() 
      133 |
    • 134 |
    135 | 136 |
      137 |
    • 138 | 139 | 140 |

      方法概要

      141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 |
      All Methods Static Methods Concrete Methods 
      限定符和类型方法和说明
      static voidmain(java.lang.String[] args) 
      152 |
        153 |
      • 154 | 155 | 156 |

        从类继承的方法 java.lang.Object

        157 | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
      • 158 |
      159 |
    • 160 |
    161 |
  • 162 |
163 |
164 |
165 |
    166 |
  • 167 | 168 |
      169 |
    • 170 | 171 | 172 |

      构造器详细资料

      173 | 174 | 175 | 176 |
        177 |
      • 178 |

        Main

        179 |
        public Main()
        180 |
      • 181 |
      182 |
    • 183 |
    184 | 185 |
      186 |
    • 187 | 188 | 189 |

      方法详细资料

      190 | 191 | 192 | 193 |
        194 |
      • 195 |

        main

        196 |
        public static void main(java.lang.String[] args)
        197 |                  throws java.io.IOException,
        198 |                         java.lang.InterruptedException
        199 |
        200 |
        抛出:
        201 |
        java.io.IOException
        202 |
        java.lang.InterruptedException
        203 |
        204 |
      • 205 |
      206 |
    • 207 |
    208 |
  • 209 |
210 |
211 |
212 | 213 | 214 |
215 | 216 | 217 | 218 | 219 | 220 | 221 | 229 |
230 | 272 | 273 | 274 | 275 | -------------------------------------------------------------------------------- /doc/allclasses-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 所有类 8 | 9 | 10 | 11 | 12 | 13 |

所有类

14 |
15 | 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /doc/allclasses-noframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 所有类 8 | 9 | 10 | 11 | 12 | 13 |

所有类

14 |
15 | 24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /doc/constant-values.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 常量字段值 8 | 9 | 10 | 11 | 12 | 13 | 23 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 42 |
43 | 70 | 71 |
72 |

常量字段值

73 |

目录

74 |
75 | 76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 91 |
92 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /doc/deprecated-list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 已过时的列表 8 | 9 | 10 | 11 | 12 | 13 | 23 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 42 |
43 | 70 | 71 |
72 |

已过时的 API

73 |

目录

74 |
75 | 76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 91 |
92 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /doc/help-doc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | API 帮助 8 | 9 | 10 | 11 | 12 | 13 | 23 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 42 |
43 | 70 | 71 |
72 |

此 API 文档的组织方式

73 |
此 API (应用程序编程接口) 文档包含对应于导航栏中的项目的页面, 如下所述。
74 |
75 |
76 |
    77 |
  • 78 |

    程序包

    79 |

    每个程序包都有一个页面, 其中包含它的类和接口的列表及其概要。此页面可以包含六个类别:

    80 |
      81 |
    • 接口 (斜体)
    • 82 |
    • 83 |
    • 枚举
    • 84 |
    • 异常错误
    • 85 |
    • 错误
    • 86 |
    • 注释类型
    • 87 |
    88 |
  • 89 |
  • 90 |

    类/接口

    91 |

    每个类, 接口, 嵌套类和嵌套接口都有各自的页面。其中每个页面都由三部分 (类/接口说明, 概要表, 以及详细的成员说明) 组成:

    92 |
      93 |
    • 类继承图
    • 94 |
    • 直接子类
    • 95 |
    • 所有已知子接口
    • 96 |
    • 所有已知实现类
    • 97 |
    • 类/接口声明
    • 98 |
    • 类/接口说明
    • 99 |
    100 |
      101 |
    • 嵌套类概要
    • 102 |
    • 字段概要
    • 103 |
    • 构造器概要
    • 104 |
    • 方法概要
    • 105 |
    106 |
      107 |
    • 字段详细资料
    • 108 |
    • 构造器详细资料
    • 109 |
    • 方法详细资料
    • 110 |
    111 |

    每个概要条目都包含该项目的详细说明的第一句。概要条目按字母顺序排列, 而详细说明则按其在源代码中出现的顺序排列。这样保持了程序员所建立的逻辑分组。

    112 |
  • 113 |
  • 114 |

    注释类型

    115 |

    每个注释类型都有各自的页面, 其中包含以下部分:

    116 |
      117 |
    • 注释类型声明
    • 118 |
    • 注释类型说明
    • 119 |
    • 必需元素概要
    • 120 |
    • 可选元素概要
    • 121 |
    • 元素详细资料
    • 122 |
    123 |
  • 124 |
  • 125 |

    枚举

    126 |

    每个枚举都有各自的页面, 其中包含以下部分:

    127 |
      128 |
    • 枚举声明
    • 129 |
    • 枚举说明
    • 130 |
    • 枚举常量概要
    • 131 |
    • 枚举常量详细资料
    • 132 |
    133 |
  • 134 |
  • 135 |

    树 (类分层结构)

    136 |

    对于所有程序包, 有一个类分层结构页面, 以及每个程序包的分层结构。每个分层结构页面都包含类的列表和接口的列表。从java.lang.Object开始, 按继承结构对类进行排列。接口不从java.lang.Object继承。

    137 |
      138 |
    • 查看“概览”页面时, 单击 "树" 将显示所有程序包的分层结构。
    • 139 |
    • 查看特定程序包, 类或接口页面时, 单击 "树" 将仅显示该程序包的分层结构。
    • 140 |
    141 |
  • 142 |
  • 143 |

    已过时的 API

    144 |

    已过时的 API 页面列出了所有已过时的 API。一般由于进行了改进并且通常提供了替代的 API, 所以建议不要使用已过时的 API。在将来的实现过程中, 可能会删除已过时的 API。

    145 |
  • 146 |
  • 147 |

    索引

    148 |

    索引 包含按字母顺序排列的所有类, 接口, 构造器, 方法和字段的列表。

    149 |
  • 150 |
  • 151 |

    上一个/下一个

    152 |

    这些链接使您可以转至下一个或上一个类, 接口, 程序包或相关页面。

    153 |
  • 154 |
  • 155 |

    框架/无框架

    156 |

    这些链接用于显示和隐藏 HTML 框架。所有页面均具有有框架和无框架两种显示方式。

    157 |
  • 158 |
  • 159 |

    所有类

    160 |

    所有类链接显示所有类和接口 (除了非静态嵌套类型)。

    161 |
  • 162 |
  • 163 |

    序列化表格

    164 |

    每个可序列化或可外部化的类都有其序列化字段和方法的说明。此信息对重新实现者有用, 而对使用 API 的开发者则没有什么用处。尽管导航栏中没有链接, 但您可以通过下列方式获取此信息: 转至任何序列化类, 然后单击类说明的 "另请参阅" 部分中的 "序列化表格"。

    165 |
  • 166 |
  • 167 |

    常量字段值

    168 |

    常量字段值页面列出了静态最终字段及其值。

    169 |
  • 170 |
171 | 此帮助文件适用于使用标准 doclet 生成的 API 文档。
172 | 173 |
174 | 175 | 176 | 177 | 178 | 179 | 180 | 188 |
189 | 216 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /doc/index-all.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 索引 8 | 9 | 10 | 11 | 12 | 13 | 23 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 42 |
43 | 70 | 71 |
A C D E F G I M P S V  72 | 73 | 74 |

A

75 |
76 |
addCrash(CrashItem) - 类 中的方法CrashGroup
77 |
78 |
添加一个CrashItem。
79 |
80 |
addCrashCount() - 类 中的方法CrashItem
81 |
82 |
增加崩溃次数。
83 |
84 |
ArgParser - <Unnamed>中的类
85 |
86 |
此类用来处理命令行参数。
87 |
88 |
ArgParser() - 类 的构造器ArgParser
89 |
 
90 |
91 | 92 | 93 | 94 |

C

95 |
96 |
CrashGroup - <Unnamed>中的类
97 |
98 |
此类表示一个崩溃组,代表一类崩溃。
99 |
100 |
CrashGroup(CrashItem) - 类 的构造器CrashGroup
101 |
102 |
构造一个崩溃组。
103 |
104 |
CrashItem - <Unnamed>中的类
105 |
106 |
此类表示一个崩溃日志,一个崩溃日志有两种类型:java或者C。
107 |
108 |
CrashItem(String, boolean) - 类 的构造器CrashItem
109 |
110 |
构造一个崩溃日志
111 |
112 |
CrashParser - <Unnamed>中的类
113 |
 
114 |
CrashParser(String, String, int) - 类 的构造器CrashParser
115 |
116 |
构建一个崩溃解析器。
117 |
118 |
CrashType - <Unnamed>中的枚举
119 |
120 |
此枚举表示一个崩溃类型。
121 |
122 |
123 | 124 | 125 | 126 |

D

127 |
128 |
delete(String) - 类 中的静态方法FileHelper
129 |
130 |
删除一个文件,文件夹(文件夹可以非空)。
131 |
132 |
133 | 134 | 135 | 136 |

E

137 |
138 |
equals(Object) - 类 中的方法CrashItem
139 |
140 |
比较两个崩溃是否相等。
141 |
142 |
143 | 144 | 145 | 146 |

F

147 |
148 |
FileHelper - <Unnamed>中的类
149 |
150 |
此类用来帮助操作文件
151 |
152 |
FileHelper() - 类 的构造器FileHelper
153 |
 
154 |
findSameCrash(CrashItem) - 类 中的方法CrashGroup
155 |
156 |
在组内寻找一个相同的CrashItem。
157 |
158 |
159 | 160 | 161 | 162 |

G

163 |
164 |
get(int) - 类 中的方法CrashGroup
165 |
166 |
获得一个崩溃。
167 |
168 |
getCrashCount() - 类 中的方法CrashGroup
169 |
 
170 |
getCrashCount() - 类 中的方法CrashItem
171 |
 
172 |
getCrashId() - 类 中的方法CrashItem
173 |
 
174 |
getCrashType() - 类 中的方法CrashGroup
175 |
 
176 |
getCrashType() - 类 中的方法CrashItem
177 |
 
178 |
getDumpParam() - 类 中的方法ArgParser
179 |
 
180 |
getFileName(String) - 类 中的静态方法FileHelper
181 |
182 |
获得一个文件路径的文件名。
183 |
184 |
getKeyword() - 类 中的方法CrashGroup
185 |
 
186 |
getKeyword() - 类 中的方法CrashItem
187 |
 
188 |
getMaxParam() - 类 中的方法ArgParser
189 |
 
190 |
getMidCrashString() - 类 中的方法CrashItem
191 |
 
192 |
getOriginCrashString() - 类 中的方法CrashItem
193 |
 
194 |
getSymbolCrashString() - 类 中的方法CrashItem
195 |
 
196 |
getSymParam() - 类 中的方法ArgParser
197 |
 
198 |
199 | 200 | 201 | 202 |

I

203 |
204 |
isValid() - 类 中的方法CrashItem
205 |
 
206 |
207 | 208 | 209 | 210 |

M

211 |
212 |
Main - <Unnamed>中的类
213 |
 
214 |
Main() - 类 的构造器Main
215 |
 
216 |
main(String[]) - 类 中的静态方法Main
217 |
 
218 |
matchCrash(CrashItem) - 类 中的方法CrashGroup
219 |
220 |
crashItem是否可以添加到该组。
221 |
222 |
223 | 224 | 225 | 226 |

P

227 |
228 |
parse(String[]) - 类 中的方法ArgParser
229 |
230 |
解析命令行参数,并验证命令行参数。
231 |
232 |
parseCrash() - 类 中的方法CrashParser
233 |
234 |
解析日志,一条日志有可能是单行的(以\\n分隔),也有可能是多行的。
235 |
236 |
237 | 238 | 239 | 240 |

S

241 |
242 |
setCSymbolCrashLines(ArrayList<String>) - 类 中的方法CrashItem
243 |
244 |
设置C崩溃符号字符串,确定崩溃ID,崩溃关键词.
245 |
246 |
size() - 类 中的方法CrashGroup
247 |
248 |
获得崩溃的数量。
249 |
250 |
sort() - 类 中的方法CrashGroup
251 |
252 |
按照崩溃次数降序排列。
253 |
254 |
255 | 256 | 257 | 258 |

V

259 |
260 |
valueOf(String) - 枚举 中的静态方法CrashType
261 |
262 |
返回带有指定名称的该类型的枚举常量。
263 |
264 |
values() - 枚举 中的静态方法CrashType
265 |
266 |
按照声明该枚举类型的常量的顺序, 返回 267 | 包含这些常量的数组。
268 |
269 |
270 | A C D E F G I M P S V 
271 | 272 |
273 | 274 | 275 | 276 | 277 | 278 | 279 | 287 |
288 | 315 | 316 | 317 | 318 | -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 生成的文档 (无标题) 8 | 60 | 61 | 62 | 63 | 64 | 65 | <noscript> 66 | <div>您的浏览器已禁用 JavaScript。</div> 67 | </noscript> 68 | <h2>框架预警</h2> 69 | <p>请使用框架功能查看此文档。如果看到此消息, 则表明您使用的是不支持框架的 Web 客户机。链接到<a href="ArgParser.html">非框架版本</a>。</p> 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /doc/overview-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 类分层结构 8 | 9 | 10 | 11 | 12 | 13 | 23 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 42 |
43 | 70 | 71 |
72 |

所有程序包的分层结构

73 |
74 |
75 |

类分层结构

76 | 88 |

枚举分层结构

89 |
    90 |
  • java.lang.Object 91 |
      92 |
    • java.lang.Enum<E> (implements java.lang.Comparable<T>, java.io.Serializable) 93 | 96 |
    • 97 |
    98 |
  • 99 |
100 |
101 | 102 |
103 | 104 | 105 | 106 | 107 | 108 | 109 | 117 |
118 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /doc/package-frame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <Unnamed> 8 | 9 | 10 | 11 | 12 | 13 |

<Unnamed>

14 |
15 |

16 | 24 |

枚举

25 | 28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /doc/package-list: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /doc/package-summary.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 | 31 |
32 | 59 | 60 |
61 |

程序包 <Unnamed>

62 |
63 |
64 |
    65 |
  • 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 78 | 79 | 80 | 81 | 84 | 85 | 86 | 87 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 100 | 101 | 102 | 103 | 104 | 105 | 106 |
    类概要 
    说明
    ArgParser 76 |
    此类用来处理命令行参数。
    77 |
    CrashGroup 82 |
    此类表示一个崩溃组,代表一类崩溃。
    83 |
    CrashItem 88 |
    此类表示一个崩溃日志,一个崩溃日志有两种类型:java或者C。
    89 |
    CrashParser 
    FileHelper 98 |
    此类用来帮助操作文件
    99 |
    Main 
    107 |
  • 108 |
  • 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 121 | 122 | 123 |
    枚举概要 
    枚举说明
    CrashType 119 |
    此枚举表示一个崩溃类型。
    120 |
    124 |
  • 125 |
126 |
127 | 128 |
129 | 130 | 131 | 132 | 133 | 134 | 135 | 143 |
144 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /doc/package-tree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 类分层结构 8 | 9 | 10 | 11 | 12 | 13 | 23 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 42 |
43 | 70 | 71 |
72 |

程序包<Unnamed>的分层结构

73 |
74 |
75 |

类分层结构

76 | 88 |

枚举分层结构

89 |
    90 |
  • java.lang.Object 91 |
      92 |
    • java.lang.Enum<E> (implements java.lang.Comparable<T>, java.io.Serializable) 93 | 96 |
    • 97 |
    98 |
  • 99 |
100 |
101 | 102 |
103 | 104 | 105 | 106 | 107 | 108 | 109 | 117 |
118 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /doc/script.js: -------------------------------------------------------------------------------- 1 | function show(type) 2 | { 3 | count = 0; 4 | for (var key in methods) { 5 | var row = document.getElementById(key); 6 | if ((methods[key] & type) != 0) { 7 | row.style.display = ''; 8 | row.className = (count++ % 2) ? rowColor : altColor; 9 | } 10 | else 11 | row.style.display = 'none'; 12 | } 13 | updateTabs(type); 14 | } 15 | 16 | function updateTabs(type) 17 | { 18 | for (var value in tabs) { 19 | var sNode = document.getElementById(tabs[value][0]); 20 | var spanNode = sNode.firstChild; 21 | if (value == type) { 22 | sNode.className = activeTableTab; 23 | spanNode.innerHTML = tabs[value][1]; 24 | } 25 | else { 26 | sNode.className = tableTab; 27 | spanNode.innerHTML = "" + tabs[value][1] + ""; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /doc/stylesheet.css: -------------------------------------------------------------------------------- 1 | /* Javadoc style sheet */ 2 | /* 3 | Overall document style 4 | */ 5 | 6 | @import url('resources/fonts/dejavu.css'); 7 | 8 | body { 9 | background-color:#ffffff; 10 | color:#353833; 11 | font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; 12 | font-size:14px; 13 | margin:0; 14 | } 15 | a:link, a:visited { 16 | text-decoration:none; 17 | color:#4A6782; 18 | } 19 | a:hover, a:focus { 20 | text-decoration:none; 21 | color:#bb7a2a; 22 | } 23 | a:active { 24 | text-decoration:none; 25 | color:#4A6782; 26 | } 27 | a[name] { 28 | color:#353833; 29 | } 30 | a[name]:hover { 31 | text-decoration:none; 32 | color:#353833; 33 | } 34 | pre { 35 | font-family:'DejaVu Sans Mono', monospace; 36 | font-size:14px; 37 | } 38 | h1 { 39 | font-size:20px; 40 | } 41 | h2 { 42 | font-size:18px; 43 | } 44 | h3 { 45 | font-size:16px; 46 | font-style:italic; 47 | } 48 | h4 { 49 | font-size:13px; 50 | } 51 | h5 { 52 | font-size:12px; 53 | } 54 | h6 { 55 | font-size:11px; 56 | } 57 | ul { 58 | list-style-type:disc; 59 | } 60 | code, tt { 61 | font-family:'DejaVu Sans Mono', monospace; 62 | font-size:14px; 63 | padding-top:4px; 64 | margin-top:8px; 65 | line-height:1.4em; 66 | } 67 | dt code { 68 | font-family:'DejaVu Sans Mono', monospace; 69 | font-size:14px; 70 | padding-top:4px; 71 | } 72 | table tr td dt code { 73 | font-family:'DejaVu Sans Mono', monospace; 74 | font-size:14px; 75 | vertical-align:top; 76 | padding-top:4px; 77 | } 78 | sup { 79 | font-size:8px; 80 | } 81 | /* 82 | Document title and Copyright styles 83 | */ 84 | .clear { 85 | clear:both; 86 | height:0px; 87 | overflow:hidden; 88 | } 89 | .aboutLanguage { 90 | float:right; 91 | padding:0px 21px; 92 | font-size:11px; 93 | z-index:200; 94 | margin-top:-9px; 95 | } 96 | .legalCopy { 97 | margin-left:.5em; 98 | } 99 | .bar a, .bar a:link, .bar a:visited, .bar a:active { 100 | color:#FFFFFF; 101 | text-decoration:none; 102 | } 103 | .bar a:hover, .bar a:focus { 104 | color:#bb7a2a; 105 | } 106 | .tab { 107 | background-color:#0066FF; 108 | color:#ffffff; 109 | padding:8px; 110 | width:5em; 111 | font-weight:bold; 112 | } 113 | /* 114 | Navigation bar styles 115 | */ 116 | .bar { 117 | background-color:#4D7A97; 118 | color:#FFFFFF; 119 | padding:.8em .5em .4em .8em; 120 | height:auto;/*height:1.8em;*/ 121 | font-size:11px; 122 | margin:0; 123 | } 124 | .topNav { 125 | background-color:#4D7A97; 126 | color:#FFFFFF; 127 | float:left; 128 | padding:0; 129 | width:100%; 130 | clear:right; 131 | height:2.8em; 132 | padding-top:10px; 133 | overflow:hidden; 134 | font-size:12px; 135 | } 136 | .bottomNav { 137 | margin-top:10px; 138 | background-color:#4D7A97; 139 | color:#FFFFFF; 140 | float:left; 141 | padding:0; 142 | width:100%; 143 | clear:right; 144 | height:2.8em; 145 | padding-top:10px; 146 | overflow:hidden; 147 | font-size:12px; 148 | } 149 | .subNav { 150 | background-color:#dee3e9; 151 | float:left; 152 | width:100%; 153 | overflow:hidden; 154 | font-size:12px; 155 | } 156 | .subNav div { 157 | clear:left; 158 | float:left; 159 | padding:0 0 5px 6px; 160 | text-transform:uppercase; 161 | } 162 | ul.navList, ul.subNavList { 163 | float:left; 164 | margin:0 25px 0 0; 165 | padding:0; 166 | } 167 | ul.navList li{ 168 | list-style:none; 169 | float:left; 170 | padding: 5px 6px; 171 | text-transform:uppercase; 172 | } 173 | ul.subNavList li{ 174 | list-style:none; 175 | float:left; 176 | } 177 | .topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { 178 | color:#FFFFFF; 179 | text-decoration:none; 180 | text-transform:uppercase; 181 | } 182 | .topNav a:hover, .bottomNav a:hover { 183 | text-decoration:none; 184 | color:#bb7a2a; 185 | text-transform:uppercase; 186 | } 187 | .navBarCell1Rev { 188 | background-color:#F8981D; 189 | color:#253441; 190 | margin: auto 5px; 191 | } 192 | .skipNav { 193 | position:absolute; 194 | top:auto; 195 | left:-9999px; 196 | overflow:hidden; 197 | } 198 | /* 199 | Page header and footer styles 200 | */ 201 | .header, .footer { 202 | clear:both; 203 | margin:0 20px; 204 | padding:5px 0 0 0; 205 | } 206 | .indexHeader { 207 | margin:10px; 208 | position:relative; 209 | } 210 | .indexHeader span{ 211 | margin-right:15px; 212 | } 213 | .indexHeader h1 { 214 | font-size:13px; 215 | } 216 | .title { 217 | color:#2c4557; 218 | margin:10px 0; 219 | } 220 | .subTitle { 221 | margin:5px 0 0 0; 222 | } 223 | .header ul { 224 | margin:0 0 15px 0; 225 | padding:0; 226 | } 227 | .footer ul { 228 | margin:20px 0 5px 0; 229 | } 230 | .header ul li, .footer ul li { 231 | list-style:none; 232 | font-size:13px; 233 | } 234 | /* 235 | Heading styles 236 | */ 237 | div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { 238 | background-color:#dee3e9; 239 | border:1px solid #d0d9e0; 240 | margin:0 0 6px -8px; 241 | padding:7px 5px; 242 | } 243 | ul.blockList ul.blockList ul.blockList li.blockList h3 { 244 | background-color:#dee3e9; 245 | border:1px solid #d0d9e0; 246 | margin:0 0 6px -8px; 247 | padding:7px 5px; 248 | } 249 | ul.blockList ul.blockList li.blockList h3 { 250 | padding:0; 251 | margin:15px 0; 252 | } 253 | ul.blockList li.blockList h2 { 254 | padding:0px 0 20px 0; 255 | } 256 | /* 257 | Page layout container styles 258 | */ 259 | .contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { 260 | clear:both; 261 | padding:10px 20px; 262 | position:relative; 263 | } 264 | .indexContainer { 265 | margin:10px; 266 | position:relative; 267 | font-size:12px; 268 | } 269 | .indexContainer h2 { 270 | font-size:13px; 271 | padding:0 0 3px 0; 272 | } 273 | .indexContainer ul { 274 | margin:0; 275 | padding:0; 276 | } 277 | .indexContainer ul li { 278 | list-style:none; 279 | padding-top:2px; 280 | } 281 | .contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { 282 | font-size:12px; 283 | font-weight:bold; 284 | margin:10px 0 0 0; 285 | color:#4E4E4E; 286 | } 287 | .contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { 288 | margin:5px 0 10px 0px; 289 | font-size:14px; 290 | font-family:'DejaVu Sans Mono',monospace; 291 | } 292 | .serializedFormContainer dl.nameValue dt { 293 | margin-left:1px; 294 | font-size:1.1em; 295 | display:inline; 296 | font-weight:bold; 297 | } 298 | .serializedFormContainer dl.nameValue dd { 299 | margin:0 0 0 1px; 300 | font-size:1.1em; 301 | display:inline; 302 | } 303 | /* 304 | List styles 305 | */ 306 | ul.horizontal li { 307 | display:inline; 308 | font-size:0.9em; 309 | } 310 | ul.inheritance { 311 | margin:0; 312 | padding:0; 313 | } 314 | ul.inheritance li { 315 | display:inline; 316 | list-style:none; 317 | } 318 | ul.inheritance li ul.inheritance { 319 | margin-left:15px; 320 | padding-left:15px; 321 | padding-top:1px; 322 | } 323 | ul.blockList, ul.blockListLast { 324 | margin:10px 0 10px 0; 325 | padding:0; 326 | } 327 | ul.blockList li.blockList, ul.blockListLast li.blockList { 328 | list-style:none; 329 | margin-bottom:15px; 330 | line-height:1.4; 331 | } 332 | ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { 333 | padding:0px 20px 5px 10px; 334 | border:1px solid #ededed; 335 | background-color:#f8f8f8; 336 | } 337 | ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { 338 | padding:0 0 5px 8px; 339 | background-color:#ffffff; 340 | border:none; 341 | } 342 | ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { 343 | margin-left:0; 344 | padding-left:0; 345 | padding-bottom:15px; 346 | border:none; 347 | } 348 | ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { 349 | list-style:none; 350 | border-bottom:none; 351 | padding-bottom:0; 352 | } 353 | table tr td dl, table tr td dl dt, table tr td dl dd { 354 | margin-top:0; 355 | margin-bottom:1px; 356 | } 357 | /* 358 | Table styles 359 | */ 360 | .overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary { 361 | width:100%; 362 | border-left:1px solid #EEE; 363 | border-right:1px solid #EEE; 364 | border-bottom:1px solid #EEE; 365 | } 366 | .overviewSummary, .memberSummary { 367 | padding:0px; 368 | } 369 | .overviewSummary caption, .memberSummary caption, .typeSummary caption, 370 | .useSummary caption, .constantsSummary caption, .deprecatedSummary caption { 371 | position:relative; 372 | text-align:left; 373 | background-repeat:no-repeat; 374 | color:#253441; 375 | font-weight:bold; 376 | clear:none; 377 | overflow:hidden; 378 | padding:0px; 379 | padding-top:10px; 380 | padding-left:1px; 381 | margin:0px; 382 | white-space:pre; 383 | } 384 | .overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, 385 | .useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link, 386 | .overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, 387 | .useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, 388 | .overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, 389 | .useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, 390 | .overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, 391 | .useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited { 392 | color:#FFFFFF; 393 | } 394 | .overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, 395 | .useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span { 396 | white-space:nowrap; 397 | padding-top:5px; 398 | padding-left:12px; 399 | padding-right:12px; 400 | padding-bottom:7px; 401 | display:inline-block; 402 | float:left; 403 | background-color:#F8981D; 404 | border: none; 405 | height:16px; 406 | } 407 | .memberSummary caption span.activeTableTab span { 408 | white-space:nowrap; 409 | padding-top:5px; 410 | padding-left:12px; 411 | padding-right:12px; 412 | margin-right:3px; 413 | display:inline-block; 414 | float:left; 415 | background-color:#F8981D; 416 | height:16px; 417 | } 418 | .memberSummary caption span.tableTab span { 419 | white-space:nowrap; 420 | padding-top:5px; 421 | padding-left:12px; 422 | padding-right:12px; 423 | margin-right:3px; 424 | display:inline-block; 425 | float:left; 426 | background-color:#4D7A97; 427 | height:16px; 428 | } 429 | .memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab { 430 | padding-top:0px; 431 | padding-left:0px; 432 | padding-right:0px; 433 | background-image:none; 434 | float:none; 435 | display:inline; 436 | } 437 | .overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, 438 | .useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd { 439 | display:none; 440 | width:5px; 441 | position:relative; 442 | float:left; 443 | background-color:#F8981D; 444 | } 445 | .memberSummary .activeTableTab .tabEnd { 446 | display:none; 447 | width:5px; 448 | margin-right:3px; 449 | position:relative; 450 | float:left; 451 | background-color:#F8981D; 452 | } 453 | .memberSummary .tableTab .tabEnd { 454 | display:none; 455 | width:5px; 456 | margin-right:3px; 457 | position:relative; 458 | background-color:#4D7A97; 459 | float:left; 460 | 461 | } 462 | .overviewSummary td, .memberSummary td, .typeSummary td, 463 | .useSummary td, .constantsSummary td, .deprecatedSummary td { 464 | text-align:left; 465 | padding:0px 0px 12px 10px; 466 | width:100%; 467 | } 468 | th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th, 469 | td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{ 470 | vertical-align:top; 471 | padding-right:0px; 472 | padding-top:8px; 473 | padding-bottom:3px; 474 | } 475 | th.colFirst, th.colLast, th.colOne, .constantsSummary th { 476 | background:#dee3e9; 477 | text-align:left; 478 | padding:8px 3px 3px 7px; 479 | } 480 | td.colFirst, th.colFirst { 481 | white-space:nowrap; 482 | font-size:13px; 483 | } 484 | td.colLast, th.colLast { 485 | font-size:13px; 486 | } 487 | td.colOne, th.colOne { 488 | font-size:13px; 489 | } 490 | .overviewSummary td.colFirst, .overviewSummary th.colFirst, 491 | .overviewSummary td.colOne, .overviewSummary th.colOne, 492 | .memberSummary td.colFirst, .memberSummary th.colFirst, 493 | .memberSummary td.colOne, .memberSummary th.colOne, 494 | .typeSummary td.colFirst{ 495 | width:25%; 496 | vertical-align:top; 497 | } 498 | td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { 499 | font-weight:bold; 500 | } 501 | .tableSubHeadingColor { 502 | background-color:#EEEEFF; 503 | } 504 | .altColor { 505 | background-color:#FFFFFF; 506 | } 507 | .rowColor { 508 | background-color:#EEEEEF; 509 | } 510 | /* 511 | Content styles 512 | */ 513 | .description pre { 514 | margin-top:0; 515 | } 516 | .deprecatedContent { 517 | margin:0; 518 | padding:10px 0; 519 | } 520 | .docSummary { 521 | padding:0; 522 | } 523 | 524 | ul.blockList ul.blockList ul.blockList li.blockList h3 { 525 | font-style:normal; 526 | } 527 | 528 | div.block { 529 | font-size:14px; 530 | font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; 531 | } 532 | 533 | td.colLast div { 534 | padding-top:0px; 535 | } 536 | 537 | 538 | td.colLast a { 539 | padding-bottom:3px; 540 | } 541 | /* 542 | Formatting effect styles 543 | */ 544 | .sourceLineNo { 545 | color:green; 546 | padding:0 30px 0 0; 547 | } 548 | h1.hidden { 549 | visibility:hidden; 550 | overflow:hidden; 551 | font-size:10px; 552 | } 553 | .block { 554 | display:block; 555 | margin:3px 10px 2px 0px; 556 | color:#474747; 557 | } 558 | .deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink, 559 | .overrideSpecifyLabel, .packageHierarchyLabel, .paramLabel, .returnLabel, 560 | .seeLabel, .simpleTagLabel, .throwsLabel, .typeNameLabel, .typeNameLink { 561 | font-weight:bold; 562 | } 563 | .deprecationComment, .emphasizedPhrase, .interfaceName { 564 | font-style:italic; 565 | } 566 | 567 | div.block div.block span.deprecationComment, div.block div.block span.emphasizedPhrase, 568 | div.block div.block span.interfaceName { 569 | font-style:normal; 570 | } 571 | 572 | div.contentContainer ul.blockList li.blockList h2{ 573 | padding-bottom:0px; 574 | } 575 | -------------------------------------------------------------------------------- /screenshot/weixin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ireaderlab/AndroidCrashReport/a6d09cbf2033488a3d8686da4a6f427644f03d89/screenshot/weixin.jpg -------------------------------------------------------------------------------- /src/ArgParser.java: -------------------------------------------------------------------------------- 1 | import java.io.File; 2 | import java.io.IOException; 3 | /** 4 | * 此类用来处理命令行参数。 5 | */ 6 | public class ArgParser { 7 | /** 8 | * SO符号表的路径 9 | */ 10 | private String mSymParam; 11 | /** 12 | * 崩溃日志路径 13 | */ 14 | private String mDumpParam; 15 | /** 16 | * 最多分析多少条日志 17 | */ 18 | private int mMaxParam; 19 | 20 | /** 21 | * 解析命令行参数,并验证命令行参数。 22 | * @param args 命令行参数 23 | * @return 成功返回true,否则返回false 24 | */ 25 | public boolean parse(String[] args) { 26 | for (int i = 0; i < args.length; i++) { 27 | if (args[i].equals("-sym")) { 28 | if (i + 1 < args.length) { 29 | mSymParam = args[i + 1]; 30 | i++; 31 | } else { 32 | printLoseValue(args[i]); 33 | return false; 34 | } 35 | } else if (args[i].equals("-dump")) { 36 | if (i + 1 < args.length) { 37 | mDumpParam = args[i + 1]; 38 | i++; 39 | } else { 40 | printLoseValue(args[i]); 41 | return false; 42 | } 43 | } else if (args[i].equals("-max")) { 44 | if (i + 1 < args.length) { 45 | try { 46 | mMaxParam = Integer.parseInt(args[i + 1]); 47 | } catch (NumberFormatException e) { 48 | System.err.println("error: invalid value for \"max\" parameter."); 49 | return false; 50 | } 51 | i++; 52 | } else { 53 | printLoseValue(args[i]); 54 | return false; 55 | } 56 | } else { 57 | System.err.println(String.format("error: invalid parameter \"%s\",please see usage bellow:", args[i])); 58 | printUsage(); 59 | return false; 60 | } 61 | } 62 | 63 | if (mDumpParam == null) { 64 | System.err.println("error: lose parameter \"-dump\"."); 65 | printUsage(); 66 | return false; 67 | } 68 | 69 | if (mSymParam != null) { 70 | try { 71 | Runtime.getRuntime().exec("ndk-stack"); 72 | } catch (IOException e) { 73 | System.err 74 | .println("error:\"ndk-stack\" command not found,please add \"ndk-stack\" to path environment."); 75 | return false; 76 | } 77 | } 78 | 79 | if (mSymParam != null) { 80 | if (!new File(mSymParam).exists()) { 81 | System.err.println(String.format("error: can not find file:%s", mSymParam)); 82 | printUsage(); 83 | return false; 84 | } 85 | } 86 | 87 | if (mDumpParam != null) { 88 | if (!new File(mDumpParam).exists()) { 89 | System.err.println(String.format("error: can not find file:%s", mDumpParam)); 90 | printUsage(); 91 | return false; 92 | } 93 | } 94 | return true; 95 | } 96 | 97 | /** 98 | * 某个参数未指定值时,输出提示信息 99 | */ 100 | private void printLoseValue(String param) { 101 | System.err.println(String.format("error: lose value for \"%s\" paramter,please see usage bellow:", param)); 102 | printUsage(); 103 | } 104 | 105 | /** 106 | * 输出帮助文档 107 | */ 108 | private void printUsage() { 109 | System.out.println("Usage:"); 110 | System.out.println(" acrash-report -sym [-dump ]"); 111 | System.out.println(); 112 | System.out.println(" -dump Contains full path to the file containing the crash dump."); 113 | System.out.println(" -sym Contains full path to the root directory for symbols."); 114 | System.out.println(" This is an optional parameter. If ommited, C crash will ignore."); 115 | } 116 | 117 | /** 118 | * @return 返回SO符号表的路径 119 | */ 120 | public String getSymParam() { 121 | return mSymParam; 122 | } 123 | 124 | /** 125 | * @return 返回崩溃日志路径 126 | */ 127 | public String getDumpParam() { 128 | return mDumpParam; 129 | } 130 | 131 | /** 132 | * @return 返回最多分析多少条日志 133 | */ 134 | public int getMaxParam() { 135 | return mMaxParam; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/CrashGroup.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.Comparator; 3 | import java.util.Collections; 4 | 5 | /** 6 | * 此类表示一个崩溃组,代表一类崩溃。一个崩溃组里面的崩溃具有相同的崩溃关键字和崩溃类型。 7 | */ 8 | public class CrashGroup { 9 | /** 10 | * 崩溃列表 11 | */ 12 | private ArrayList mCrashItems = new ArrayList(); 13 | 14 | /** 15 | * 构造一个崩溃组。 16 | * @param item 第一个CrashItem,不能为null。 17 | */ 18 | public CrashGroup(CrashItem item) { 19 | mCrashItems.add(item); 20 | } 21 | 22 | /** 23 | * 获得崩溃的数量。 24 | * @return 返回崩溃组里面的CrashItem的数量。 25 | */ 26 | public int size() { 27 | return mCrashItems.size(); 28 | } 29 | 30 | /** 31 | * 获得一个崩溃。 32 | * @param index 索引。 33 | * @return 返回一个CrashItem 34 | */ 35 | public CrashItem get(int index) { 36 | return mCrashItems.get(index); 37 | } 38 | 39 | /** 40 | * crashItem是否可以添加到该组。 41 | * @param item 要匹配的CrashItem。 42 | * @return 如果item能添加到组内,返回true,否则返回false. 43 | */ 44 | public boolean matchCrash(CrashItem item) { 45 | if (!item.getKeyword().equals(getKeyword())) { 46 | return false; 47 | } 48 | if (item.getCrashType() != getCrashType()) { 49 | return false; 50 | } 51 | return true; 52 | } 53 | 54 | /** 55 | * 添加一个CrashItem。 56 | * @param item 一个Crashitem。 57 | * @return 成功添加返回true,否则返回false。 58 | */ 59 | public boolean addCrash(CrashItem item) { 60 | if (!matchCrash(item)) { 61 | return false; 62 | } 63 | CrashItem findItem = findSameCrash(item); 64 | if (findItem != null) { 65 | findItem.addCrashCount(); 66 | } else { 67 | mCrashItems.add(item); 68 | } 69 | return true; 70 | } 71 | 72 | /** 73 | * 在组内寻找一个相同的CrashItem。 74 | * @param item 要寻找的CrashItem。 75 | * @return 如果找到,返回该对象,否则返回null。 76 | */ 77 | public CrashItem findSameCrash(CrashItem item) { 78 | for (int i = 0; i < mCrashItems.size(); i++) { 79 | if (mCrashItems.get(i).equals(item)) { 80 | return mCrashItems.get(i); 81 | } 82 | } 83 | return null; 84 | } 85 | 86 | /** 87 | * @return 返回崩溃关键词。 88 | */ 89 | public String getKeyword() { 90 | return mCrashItems.get(0).getKeyword(); 91 | } 92 | 93 | /** 94 | * @return 返回崩溃类型。 95 | */ 96 | public CrashType getCrashType() { 97 | return mCrashItems.get(0).getCrashType(); 98 | } 99 | 100 | /** 101 | * @return 返回所有的CrashItem的崩溃次数累加之和。 102 | */ 103 | public int getCrashCount() { 104 | int count = 0; 105 | for (int i = 0; i < mCrashItems.size(); i++) { 106 | count += mCrashItems.get(i).getCrashCount(); 107 | } 108 | return count; 109 | } 110 | 111 | /** 112 | * 按照崩溃次数降序排列。 113 | */ 114 | public void sort() { 115 | Collections.sort(mCrashItems,new Comparator() { 116 | @Override 117 | public int compare(CrashItem o1, CrashItem o2) { 118 | return o2.getCrashCount() - o1.getCrashCount(); 119 | } 120 | 121 | }); 122 | } 123 | } -------------------------------------------------------------------------------- /src/CrashItem.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.regex.Matcher; 3 | import java.util.regex.Pattern; 4 | /** 5 | * 此类表示一个崩溃日志,一个崩溃日志有两种类型:java或者C。 6 | * 一个崩溃日志可以是单行的(行内用\\n分隔),也可以是多行的。 7 | * 对于一个崩溃日志的字符串,有三种状态: 8 | * 原始状态:崩溃日志是单行的,日志包含\\n,\\t等转义字符。 9 | * 中间状态:替换掉原始状态的转义字符。 10 | * 符号状态:日志包含明确的崩溃函数的名称和源文件行号。 11 | * 对于c日志,符号状态意味着调用“ndk-stack”命令将中间状态的日志解析之后的结果。 12 | * 对于java日志,符号状态和中间状态相同,因为java不需要解析符号表。 13 | */ 14 | public class CrashItem { 15 | /** 16 | * 崩溃次数 17 | */ 18 | private int mCrashCount = 1; 19 | /** 20 | * 原始的crash字符串 21 | */ 22 | private String mOriginCrashString; 23 | /** 24 | * 崩溃中间字符串 25 | */ 26 | private String mMidCrashString; 27 | /** 28 | * 崩溃符号字符串 29 | */ 30 | private String mSymbolCrashString; 31 | /** 32 | * 崩溃ID 33 | */ 34 | private String mCrashId; 35 | /** 36 | * 崩溃关键词 37 | */ 38 | private String mKeyword; 39 | /** 40 | * 是否是有效的崩溃日志 41 | */ 42 | private boolean mValid; 43 | /** 44 | * 崩溃类型 45 | */ 46 | private CrashType mCrashType; 47 | 48 | /** 49 | * 构造一个崩溃日志 50 | * @param crashString 崩溃的原始字符串。 51 | * @param isMutiline 崩溃日志是否是多行的,对于多行日志,原始字符串和中间字符串是一样的。 52 | */ 53 | public CrashItem(String crashString, boolean isMutiline) { 54 | mOriginCrashString = crashString; 55 | if (crashString.startsWith("*** *** ***")) { 56 | mCrashType = CrashType.C_CRASH; 57 | } else { 58 | mCrashType = CrashType.JAVA_CRASH; 59 | } 60 | 61 | if (mCrashType == CrashType.JAVA_CRASH) { 62 | crashString = crashString.replaceAll("\\\\t", "\t"); 63 | } 64 | String separator = isMutiline ? "\\n" : "\\\\n"; 65 | String[] lines = crashString.split(separator); 66 | mMidCrashString = ""; 67 | for (String line : lines) { 68 | mMidCrashString += "\t"; 69 | mMidCrashString += line; 70 | mMidCrashString += "\n"; 71 | } 72 | 73 | if (mCrashType == CrashType.JAVA_CRASH) { 74 | setJavaSymbolCrashLines(lines); 75 | } 76 | } 77 | 78 | /** 79 | * 设置java崩溃符号字符串,确定崩溃ID,崩溃关键词. 80 | * @param lines 符号字符串 81 | */ 82 | private void setJavaSymbolCrashLines(String[] lines) { 83 | if (lines.length < 2) { 84 | return; 85 | } 86 | if (!lines[1].startsWith("\tat ")) { 87 | return; 88 | } 89 | mCrashId = mMidCrashString; 90 | mKeyword = lines[1]; 91 | mSymbolCrashString = mMidCrashString; 92 | mValid = true; 93 | } 94 | 95 | /** 96 | * 设置C崩溃符号字符串,确定崩溃ID,崩溃关键词. 97 | * @param crashLines 符号字符串,"ndk-stack"解析后的字符串。 98 | */ 99 | public void setCSymbolCrashLines(ArrayList crashLines) { 100 | if (mCrashType != CrashType.C_CRASH) { 101 | throw new IllegalStateException(); 102 | } 103 | if (crashLines.size() < 4) { 104 | return; 105 | } 106 | 107 | makeCrashID(crashLines); 108 | makeCrashKeyword(crashLines); 109 | 110 | mSymbolCrashString = ""; 111 | for (int i = 0; i < crashLines.size(); i++) { 112 | mSymbolCrashString += "\t"; 113 | mSymbolCrashString += crashLines.get(i); 114 | mSymbolCrashString += "\n"; 115 | } 116 | mValid = true; 117 | } 118 | 119 | /** 120 | * @return 返回中间字符串。 121 | */ 122 | public String getMidCrashString() { 123 | return mMidCrashString; 124 | } 125 | 126 | /** 127 | * @return 返回符号字符串。 128 | */ 129 | public String getSymbolCrashString() { 130 | return mSymbolCrashString; 131 | } 132 | 133 | /** 134 | * @return 返回原始字符串。 135 | */ 136 | public String getOriginCrashString() { 137 | return mOriginCrashString; 138 | } 139 | 140 | /** 141 | * @return 日志是否是有效的。 142 | */ 143 | public boolean isValid() { 144 | return mValid; 145 | } 146 | 147 | /** 148 | * 增加崩溃次数。 149 | */ 150 | public void addCrashCount() { 151 | mCrashCount++; 152 | } 153 | 154 | /** 155 | * @return 获得崩溃次数。 156 | */ 157 | public int getCrashCount() { 158 | return mCrashCount; 159 | } 160 | 161 | /** 162 | * @return 返回崩溃ID。 163 | */ 164 | public String getCrashId() { 165 | return mCrashId; 166 | } 167 | 168 | /** 169 | * @return 返回崩溃关键字。 170 | */ 171 | public String getKeyword() { 172 | return mKeyword; 173 | } 174 | 175 | /** 176 | * @return 返回崩溃类型。 177 | */ 178 | public CrashType getCrashType() { 179 | return mCrashType; 180 | } 181 | 182 | /** 183 | * 比较两个崩溃是否相等。 184 | * @return 如果崩溃ID一样返回true,否则返回false。 185 | * @see java.lang.Object#equals(java.lang.Object) 186 | */ 187 | public boolean equals(Object o) { 188 | CrashItem item = (CrashItem) o; 189 | return item.mCrashId.equals(mCrashId); 190 | } 191 | 192 | /** 193 | * 分析崩溃日志,创建C崩溃日志的CrashID。 194 | * @param crashLines 崩溃符号字符串。 195 | */ 196 | private void makeCrashID(ArrayList crashLines) { 197 | // Stack frame #00 pc 00033960 198 | // /data/app-lib/com.chaozh.iReaderFree-1/libUiControl.so 199 | String regex = "Stack\\s+frame\\s+#\\d{2,}\\s+pc\\s+[\\d|a-f|A-F]{8,}\\s+/\\S+"; 200 | Pattern pattern = Pattern.compile(regex); 201 | mCrashId = ""; 202 | for (int i = 4; i < crashLines.size(); i++) { 203 | String line = crashLines.get(i); 204 | Matcher matcher = pattern.matcher(line); 205 | if (matcher.find()) { 206 | String soPath = line.substring(matcher.start(), matcher.end()); 207 | String soName = FileHelper.getFileName(soPath); 208 | int index = matcher.start() + soPath.length() - soName.length(); 209 | String lineCrashId = line.substring(index); 210 | mCrashId += lineCrashId; 211 | } else { 212 | mCrashId += line; 213 | } 214 | mCrashId += "\n"; 215 | } 216 | 217 | } 218 | 219 | /** 220 | * 分析崩溃日志,创建崩溃日志关键词。 221 | * @param crashLines 崩溃符号字符串。 222 | */ 223 | private void makeCrashKeyword(ArrayList crashLines) { 224 | // Stack frame #01 pc 0008e159 /data/libUiControl.so (Native Method): 225 | // Routine MobiInputStream::readContent(void*, unsigned int) at 226 | // /VMobiInputStream.cpp:147 (discriminator 1) 227 | String regex = "Routine\\s+(.*)\\s+at\\s*(.+):"; 228 | Pattern pattern = Pattern.compile(regex); 229 | 230 | mKeyword = ""; 231 | for (int i = 4; i < crashLines.size(); i++) { 232 | String line = crashLines.get(i); 233 | Matcher matcher = pattern.matcher(line); 234 | if (matcher.find()) { 235 | String funName = line.substring(matcher.start(1), matcher.end(1)); 236 | String filePath = line.substring(matcher.start(2), matcher.end(2)); 237 | String fileName = FileHelper.getFileName(filePath); 238 | mKeyword = funName + " " + fileName; 239 | break; 240 | } 241 | } 242 | } 243 | } -------------------------------------------------------------------------------- /src/CrashParser.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.File; 3 | import java.io.FileInputStream; 4 | import java.io.FileWriter; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.InputStreamReader; 8 | import java.io.PrintWriter; 9 | import java.io.Reader; 10 | import java.util.ArrayList; 11 | import java.util.Comparator; 12 | import java.util.Collections; 13 | 14 | public class CrashParser { 15 | /** 16 | * dump文件路径 17 | */ 18 | private String mDumpFilePath; 19 | /** 20 | * SO符号表路径 21 | */ 22 | private String mSymbolFilePath; 23 | /** 24 | * 统计报告中间文件输出路径 25 | */ 26 | private String mMiddleFilePath; 27 | /** 28 | * 统计报告输出路径 29 | */ 30 | private String mReportFilePath; 31 | /** 32 | * 详细的统计报告输出路径 33 | */ 34 | private String mDetailReportFilePath; 35 | /** 36 | * 无效日志路径 37 | */ 38 | private String mInvalidFilePath; 39 | /** 40 | * 统计报告中间文件输出流 41 | */ 42 | private PrintWriter mMidWriter; 43 | /** 44 | * 统计报告输出流 45 | */ 46 | private PrintWriter mReportWriter; 47 | /** 48 | * 详细的统计报告输出流 49 | */ 50 | private PrintWriter mDetailReportWriter; 51 | /** 52 | * 暂时禁止输出报告,和mReportWriter配合使用 53 | */ 54 | private boolean mDisableReportPrint; 55 | /** 56 | * java崩溃日志的总数量 57 | */ 58 | private int mJavaCrashCount; 59 | /** 60 | * C崩溃日志的总数量 61 | */ 62 | private int mCCrashCount; 63 | /** 64 | * 最多从mDumpFilePath文件读取多少条崩溃 65 | */ 66 | private int mCrashMax; 67 | /** 68 | * 一个崩溃日志为多行格式时,累加每行到此字符串 69 | */ 70 | private String mMutilineCrashString; 71 | /** 72 | * 崩溃组列表 73 | */ 74 | private ArrayList mCrashGroups = new ArrayList(); // 崩溃组列表 75 | /** 76 | * 无效的崩溃日志列表 77 | */ 78 | private ArrayList mInvalidItems = new ArrayList(); // 无效的崩溃日志列表 79 | /** 80 | * 统计报告是否输出到System.out 81 | */ 82 | private boolean mEnableStdOut; // 统计报告是否输出到System.out 83 | /** 84 | * mSymbolFilePath为null,却出现了C崩溃日志 85 | */ 86 | private boolean mWarnCCrash; // mSymbolFilePath为null,却出现了C崩溃日志 87 | /** 88 | * 报告输出目录 89 | */ 90 | private String mOutputDir; // 报告输出目录 91 | 92 | /** 93 | * 构建一个崩溃解析器。 94 | * @param dumpFilePath dump文件路径。 95 | * @param symbolFilePath SO符号表路径。 96 | * @param crashMax 最多从mDumpFilePath文件读取多少条崩溃。 97 | */ 98 | public CrashParser(String dumpFilePath, String symbolFilePath, int crashMax) { 99 | mSymbolFilePath = symbolFilePath; 100 | mDumpFilePath = dumpFilePath; 101 | mCrashMax = crashMax; 102 | 103 | File file = new File(mDumpFilePath); 104 | String dumpDir = file.getParent(); 105 | String dumpName = file.getName(); 106 | int index = dumpName.lastIndexOf("."); 107 | if (index != -1) { 108 | dumpName = dumpName.substring(0, index); 109 | } 110 | mOutputDir = new File(dumpDir, dumpName + "_crash_report").getAbsolutePath(); 111 | FileHelper.delete(mOutputDir); 112 | new File(mOutputDir).mkdirs(); 113 | } 114 | 115 | /** 116 | * 解析日志,一条日志有可能是单行的(以\\n分隔),也有可能是多行的。 117 | * 解析器每次读取一行日志,判断日志类型. 118 | * @throws java.io.IOException 如果发生IO异常 119 | * @throws java.lang.InterruptedException 如果发生异常 120 | */ 121 | public void parseCrash() throws IOException, InterruptedException { 122 | InputStream fin; 123 | Reader fReader; 124 | BufferedReader bReader; 125 | fin = new FileInputStream(mDumpFilePath); 126 | String encode = decideFileEncode(mDumpFilePath); 127 | if (encode == null) { 128 | encode = "utf-8"; 129 | } else if (encode == "unicode") { 130 | fin.skip(2); 131 | } else { 132 | fin.skip(3); 133 | } 134 | fReader = new InputStreamReader(fin, encode); 135 | bReader = new BufferedReader(fReader); 136 | String line; 137 | mMutilineCrashString = ""; 138 | while ((line = bReader.readLine()) != null) { 139 | if (line.contains("\\n")) { 140 | finishMutilineCrashString(); 141 | CrashItem crashItem = new CrashItem(line, false); 142 | addCrash(crashItem); 143 | } else { 144 | if (line.startsWith("*** *** ***") || line.isEmpty()) { 145 | finishMutilineCrashString(); 146 | } 147 | if (!line.isEmpty()) { 148 | mMutilineCrashString += line; 149 | mMutilineCrashString += "\n"; 150 | } 151 | } 152 | if (mCrashMax != 0 && mJavaCrashCount + mCCrashCount >= mCrashMax) { 153 | break; 154 | } 155 | } 156 | 157 | if (mCrashMax == 0 || mJavaCrashCount + mCCrashCount < mCrashMax) { 158 | finishMutilineCrashString(); 159 | } 160 | bReader.close(); 161 | 162 | sortCrash(); 163 | printCrash(CrashType.JAVA_CRASH); 164 | printCrash(CrashType.C_CRASH); 165 | printInvalidCrash(); 166 | 167 | if (mWarnCCrash) { 168 | System.out.println("warn: dump file contain c crash,but \"-sym\" param not special."); 169 | } 170 | } 171 | 172 | /** 173 | * 根据文件头,判断文件编码。 174 | * @param filePath 判断文件编码 175 | * @return 返回文件编码。 176 | */ 177 | private String decideFileEncode(String filePath) throws IOException { 178 | byte buff[] = new byte[3]; 179 | FileInputStream in = null; 180 | try { 181 | in = new FileInputStream(filePath); 182 | int readBytes = in.read(buff); 183 | if (readBytes >= 2 && buff[0] == (byte) 0xFF && buff[1] == (byte) 0xFE) { 184 | return "unicode"; 185 | } else if (readBytes >= 3 && buff[0] == (byte) 0xEF && buff[1] == (byte) 0xBB && buff[2] == (byte) 0xBF) { 186 | return "utf-8"; 187 | } 188 | } catch (IOException e) { 189 | e.printStackTrace(); 190 | } finally { 191 | if (in != null) { 192 | in.close(); 193 | } 194 | } 195 | return null; 196 | } 197 | 198 | /** 199 | * 完成多行崩溃。 200 | */ 201 | private void finishMutilineCrashString() throws IOException, InterruptedException { 202 | if (mMutilineCrashString.isEmpty()) { 203 | return; 204 | } 205 | CrashItem crashItem = new CrashItem(mMutilineCrashString, true); 206 | addCrash(crashItem); 207 | mMutilineCrashString = ""; 208 | } 209 | 210 | /** 211 | * 添加一个崩溃。 212 | * @param crashItem 一个CrashItem. 213 | */ 214 | private void addCrash(CrashItem crashItem) throws IOException, InterruptedException { 215 | if (crashItem.getCrashType() == CrashType.JAVA_CRASH) { 216 | mJavaCrashCount++; 217 | } else { 218 | if (mSymbolFilePath == null) { 219 | mWarnCCrash = true; 220 | return; 221 | } 222 | mCCrashCount++; 223 | } 224 | System.out.println(String.format("正在解析第%d个日志...", mJavaCrashCount + mCCrashCount)); 225 | decodeCrash(crashItem); 226 | if (!crashItem.isValid()) { 227 | mInvalidItems.add(crashItem); 228 | return; 229 | } 230 | 231 | CrashGroup crashGroup = findCrashGroup(crashItem); 232 | if (crashGroup == null) { 233 | mCrashGroups.add(new CrashGroup(crashItem)); 234 | } else { 235 | crashGroup.addCrash(crashItem); 236 | } 237 | } 238 | 239 | /** 240 | * 找到CrashItem的在的崩溃组 241 | * @param item 一个CrashItem. 242 | * @return 如果找到,返回组,否则,返回null。 243 | */ 244 | private CrashGroup findCrashGroup(CrashItem item) { 245 | for (int i = 0; i < mCrashGroups.size(); i++) { 246 | if (mCrashGroups.get(i).matchCrash(item)) { 247 | return mCrashGroups.get(i); 248 | } 249 | } 250 | return null; 251 | } 252 | 253 | /** 254 | * 排序崩溃。 255 | */ 256 | private void sortCrash() { 257 | Collections.sort(mCrashGroups,new Comparator() { 258 | @Override 259 | public int compare(CrashGroup o1, CrashGroup o2) { 260 | int o1Count = o1.getCrashCount(); 261 | int o2Count = o2.getCrashCount(); 262 | if (o1.getKeyword().isEmpty()) { 263 | o1Count = 0; 264 | } 265 | if (o2.getKeyword().isEmpty()) { 266 | o2Count = 0; 267 | } 268 | return o2Count - o1Count; 269 | } 270 | }); 271 | 272 | for (int i = 0; i < mCrashGroups.size(); i++) { 273 | CrashGroup group = mCrashGroups.get(i); 274 | group.sort(); 275 | } 276 | } 277 | 278 | /** 279 | * 调用ndk-stack,解析崩溃日志。 280 | */ 281 | private void decodeCrash(CrashItem item) throws IOException, InterruptedException { 282 | if (item.getCrashType() != CrashType.C_CRASH) { 283 | return; 284 | } 285 | String command = String.format("ndk-stack -sym %s", mSymbolFilePath); 286 | Process proc = Runtime.getRuntime().exec(command); 287 | proc.getOutputStream().write(item.getMidCrashString().getBytes("utf-8")); 288 | proc.getOutputStream().close(); 289 | 290 | BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream(), "utf-8")); 291 | String line = null; 292 | ArrayList crashLines = new ArrayList(); 293 | while ((line = reader.readLine()) != null) { 294 | crashLines.add(line); 295 | } 296 | item.setCSymbolCrashLines(crashLines); 297 | } 298 | 299 | /** 300 | * 输出信息。 301 | */ 302 | private void printMessage(String msg) { 303 | if (mMidWriter != null) { 304 | mMidWriter.println(msg); 305 | } 306 | if (mReportWriter != null) { 307 | if (!mDisableReportPrint) { 308 | mReportWriter.println(msg); 309 | } 310 | } 311 | if (mDetailReportWriter != null) { 312 | mDetailReportWriter.println(msg); 313 | } 314 | if (mEnableStdOut) { 315 | System.out.println(msg); 316 | } 317 | } 318 | 319 | /** 320 | * 创建崩溃日志输出流。 321 | */ 322 | private void createPrintStream(CrashType crashType) throws IOException { 323 | String des = crashType == CrashType.JAVA_CRASH ? "java" : "c"; 324 | // mMiddleFilePath= new File(mOutputDir, des + 325 | // "_mid.txt").getAbsolutePath(); 326 | mReportFilePath = new File(mOutputDir, des + "_report.txt").getAbsolutePath(); 327 | mDetailReportFilePath = new File(mOutputDir, des + "_detail_report.txt").getAbsolutePath(); 328 | if (mMiddleFilePath != null) { 329 | mMidWriter = new PrintWriter(new FileWriter(mMiddleFilePath)); 330 | } 331 | if (mReportFilePath != null) { 332 | mReportWriter = new PrintWriter(new FileWriter(mReportFilePath)); 333 | } 334 | if (mDetailReportFilePath != null) { 335 | mDetailReportWriter = new PrintWriter(new FileWriter(mDetailReportFilePath)); 336 | } 337 | } 338 | 339 | /** 340 | * 输出崩溃报表。 341 | */ 342 | private void printCrash(CrashType crashType) throws IOException { 343 | ArrayList crashGroups = new ArrayList<>(); 344 | for (int i = 0; i < mCrashGroups.size(); i++) { 345 | CrashGroup crashGroup = mCrashGroups.get(i); 346 | if (crashGroup.getCrashType() == crashType) { 347 | crashGroups.add(crashGroup); 348 | } 349 | } 350 | 351 | if (crashGroups.isEmpty()) { 352 | return; 353 | } 354 | 355 | createPrintStream(crashType); 356 | String carshTypeDes = crashType == CrashType.JAVA_CRASH ? "java" : "C"; 357 | int crashCount = (crashType == CrashType.JAVA_CRASH ? mJavaCrashCount : mCCrashCount); 358 | String msg = String.format("本次共分析%d个%s日志,分析后得到%d种崩溃", crashCount, carshTypeDes, crashGroups.size()); 359 | printMessage(msg); 360 | printMessage(""); 361 | 362 | for (int i = 0; i < crashGroups.size(); i++) { 363 | CrashGroup group = crashGroups.get(i); 364 | msg = String.format( 365 | "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<开始崩溃组<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"); 366 | printMessage(msg); 367 | msg = String.format("崩溃编号:%d", i + 1); 368 | printMessage(msg); 369 | msg = String.format("崩溃触发函数:%s", group.getKeyword()); 370 | printMessage(msg); 371 | msg = String.format("崩溃总次数:%d", group.getCrashCount()); 372 | printMessage(msg); 373 | printMessage("崩溃关键函数:"); 374 | printMessage("崩溃分析:"); 375 | msg = ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"; 376 | printMessage(msg); 377 | for (int j = 0; j < group.size(); j++) { 378 | if (j >= 2) { 379 | mDisableReportPrint = true; 380 | } 381 | CrashItem crashItem = group.get(j); 382 | 383 | msg = String.format("\t崩溃次数:%d", crashItem.getCrashCount()); 384 | printMessage(msg); 385 | if (mMidWriter != null) { 386 | mMidWriter.println(crashItem.getMidCrashString()); 387 | } 388 | if (mReportWriter != null) { 389 | if (j < 2) { 390 | mReportWriter.println(crashItem.getSymbolCrashString()); 391 | } 392 | } 393 | if (mDetailReportWriter != null) { 394 | mDetailReportWriter.println(crashItem.getSymbolCrashString()); 395 | } 396 | if (mEnableStdOut) { 397 | System.out.println(crashItem.getSymbolCrashString()); 398 | } 399 | 400 | mDisableReportPrint = false; 401 | } 402 | if (mReportWriter != null) { 403 | if (group.size() > 2) { 404 | mReportWriter.print("\t..."); 405 | } 406 | } 407 | printMessage("\n\n"); 408 | } 409 | if (mMidWriter != null) { 410 | mMidWriter.close(); 411 | } 412 | if (mReportWriter != null) { 413 | mReportWriter.close(); 414 | } 415 | if (mDetailReportWriter != null) { 416 | mDetailReportWriter.close(); 417 | } 418 | } 419 | 420 | /** 421 | * 输出无效的日志。 422 | */ 423 | private void printInvalidCrash() throws IOException { 424 | if (mInvalidItems.size() == 0) { 425 | return; 426 | } 427 | mInvalidFilePath = new File(mOutputDir, "invalid.txt").getAbsolutePath(); 428 | PrintWriter invalidWriter = new PrintWriter(new FileWriter(mInvalidFilePath)); 429 | invalidWriter.println(String.format("共有%d个无效日志.\n", mInvalidItems.size())); 430 | for (int i = 0; i < mInvalidItems.size(); i++) { 431 | CrashItem crashItem = mInvalidItems.get(i); 432 | invalidWriter.println(crashItem.getOriginCrashString()); 433 | } 434 | invalidWriter.close(); 435 | } 436 | } -------------------------------------------------------------------------------- /src/CrashType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 此枚举表示一个崩溃类型。 3 | */ 4 | public enum CrashType { 5 | JAVA_CRASH, // java崩溃 6 | C_CRASH, // C崩溃 7 | } 8 | -------------------------------------------------------------------------------- /src/FileHelper.java: -------------------------------------------------------------------------------- 1 | import java.io.File; 2 | import java.util.Stack; 3 | /** 4 | * 此类用来帮助操作文件 5 | */ 6 | public class FileHelper { 7 | /** 8 | * 删除一个文件,文件夹(文件夹可以非空)。 9 | * @param filePath 文件路径。 10 | */ 11 | public static void delete(String filePath) { 12 | Stack fileStack = new Stack<>(); 13 | fileStack.push(new File(filePath)); 14 | do { 15 | File file = fileStack.peek(); 16 | if (file.isFile()) { 17 | file.delete(); 18 | fileStack.pop(); 19 | } else if (file.isDirectory()) { 20 | File [] subFiles = file.listFiles(); 21 | if (subFiles == null || subFiles.length == 0) { 22 | file.delete(); 23 | fileStack.pop(); 24 | } else { 25 | for (File sFile : subFiles) { 26 | fileStack.push(sFile); 27 | } 28 | } 29 | } else { 30 | fileStack.pop(); 31 | } 32 | } while(!fileStack.empty()); 33 | } 34 | 35 | /** 36 | * 获得一个文件路径的文件名。 37 | * @param path 文件路径。 38 | * @return 返回文件名。 39 | */ 40 | public static String getFileName(String path) { 41 | String[] parts = path.split("/"); 42 | return parts[parts.length - 1]; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Main.java: -------------------------------------------------------------------------------- 1 | import java.io.IOException; 2 | 3 | public class Main { 4 | public static void main(String[] args) throws IOException, InterruptedException { 5 | // 分析命令行参数 6 | ArgParser argParser = new ArgParser(); 7 | if (!argParser.parse(args)) { 8 | return; 9 | } 10 | 11 | // 解析日志,输出报表。 12 | CrashParser crashParser = new CrashParser(argParser.getDumpParam(), argParser.getSymParam(), 13 | argParser.getMaxParam()); 14 | long st = System.currentTimeMillis(); 15 | crashParser.parseCrash(); 16 | long et = System.currentTimeMillis(); 17 | long min = (et - st) / 1000 / 60; 18 | long sec = (et - st) / 1000 % 60; 19 | System.out.println(String.format("解析日志完成,共用时:%d分%d秒", min, sec)); 20 | } 21 | } 22 | --------------------------------------------------------------------------------