Full Code of SummerOak/CrashSDK for AI

master da56df636626
64 files
160.9 KB
53.1k tokens
Repository: SummerOak/CrashSDK
Branch: master
Commit: da56df636626
Files: 64
Total size: 160.9 KB

Directory structure:
gitextract_avj4b6vk/

├── .gitignore
├── LICENSE
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   ├── release/
│   │   ├── Demo.apk
│   │   └── output.json
│   └── src/
│       ├── .gitignore
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── summer/
│           │           └── demo/
│           │               └── crashsdkdemo/
│           │                   ├── CrashInfoWindow.java
│           │                   ├── CrashRecordWindow.java
│           │                   ├── ExceptionHandler.java
│           │                   ├── FileUtils.java
│           │                   ├── IOUtils.java
│           │                   └── MainActivity.java
│           └── res/
│               ├── drawable/
│               │   └── ic_launcher_background.xml
│               ├── drawable-v24/
│               │   └── ic_launcher_foreground.xml
│               ├── mipmap-anydpi-v26/
│               │   ├── ic_launcher.xml
│               │   └── ic_launcher_round.xml
│               └── values/
│                   ├── colors.xml
│                   ├── strings.xml
│                   └── styles.xml
├── build.gradle
├── crashsdk/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── .gitignore
│       ├── jni/
│       │   ├── Android.mk
│       │   ├── Application.mk
│       │   ├── Application_21.mk
│       │   ├── CrashSDK.cpp
│       │   ├── Test.cpp
│       │   ├── crash/
│       │   │   ├── CrashMonitor.cpp
│       │   │   ├── CrashMonitor.h
│       │   │   ├── Defines.h
│       │   │   └── unwind/
│       │   │       ├── Arch.h
│       │   │       ├── Context.cpp
│       │   │       ├── Context.h
│       │   │       ├── DataTypes.h
│       │   │       ├── _Unwind.cpp
│       │   │       ├── _Unwind.h
│       │   │       ├── arm/
│       │   │       │   ├── Config_arm.h
│       │   │       │   ├── Context_arm.cpp
│       │   │       │   ├── Context_arm.h
│       │   │       │   ├── Exidx.cpp
│       │   │       │   └── Exidx.h
│       │   │       ├── eh_frame/
│       │   │       │   ├── DWExpression.cpp
│       │   │       │   ├── DWExpression.h
│       │   │       │   ├── DWInstruction.cpp
│       │   │       │   ├── DWInstruction.h
│       │   │       │   ├── EHFrame.cpp
│       │   │       │   └── EHFrame.h
│       │   │       └── x86/
│       │   │           ├── Config_x86.h
│       │   │           ├── Context_x86.cpp
│       │   │           └── Context_x86.h
│       │   └── utils/
│       │       ├── Log.h
│       │       ├── Utils.cpp
│       │       └── Utils.h
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── summer/
│           │           └── crashsdk/
│           │               └── CrashSDK.java
│           └── res/
│               └── values/
│                   └── strings.xml
├── gradle.properties
└── settings.gradle

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.iml
.idea
.gradle
/gradlew.bat
/gradlew
/gradle/
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild


================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    {description}
    Copyright (C) {year}  {fullname}

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  {signature of Ty Coon}, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

================================================
FILE: README.md
================================================
# What it for

  Catch crashes in Java & Native(arm/x86), and dump crash information to a file located in sdcard, crash information includes call stack, system version, architecture, and registers state for native crash, etc.
  
# Structure of this project
  
  This is an Android project created via Android Studio, the module 'crashsdk' is an aar library used by the demo app;
  
  The c++ part in the crashsdk is not configured in gradle, so you need build it manually, go to jni directory and type 'ndk-build' to build the target native libs.

# Implementation

  1. For crash in Java, we use Thread#setDefaultUncaughtExceptionHandler to handle it; 
  
  2. For crash in Native, we need a signal handler to get a chance to handle signals such as SIGSEGV; In the handle, we unwind the call stack and dump infomation we interest. Since we are doing these in a signal handler, we should take signal safety into consideration. 

# Unwind Native Call Stack
	
  When gcc generates code that handles exceptions, it produces tables that describe how to unwind the stack. These tables are found in the .eh_frame section; Read https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-Embedded/LSB-Embedded/ehframechpt.html for details of .eh_frame format. The definition of Call Frame Instructions in FDE can be found in DWARF(Debugging With Attributed Record Formats) specification , you can find it here: http://dwarfstd.org

  The process of unwinding can be describe as below:
  1. Get IP(instruction pointer) where the program crashed(by parsing the third parameter of sa_handler callback);
  2. Find the target library in which the crash address located;
  3. Parse .eh_frame segment in the library and find the corresponding FDE, restore this call frame with instructions described in CIE&FDE;
  4. After restoring call frame we can find out the return address and CFA(call frame address) of previous call frame;
  5. With the return address and CFA we can unwind the previous call frame and repeat it until reach the root call frame ( (where the CFA&IP no more change);

  In step 1, we may get a wrong crash IP if the crash is caused by wrong instruction pointer. In this case we need try to unwind this frame by calling convention;

  Unfortunately, .eh_frame section not always exist, for ARM, a section .ARM.exidx exist and it is similar to .eh_frame. So, if unwind with eh_frame failed we can try .ARM.exidx; For more information of EXIDX , look here: http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf


================================================
FILE: app/.gitignore
================================================
/build


================================================
FILE: app/build.gradle
================================================
apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.summer.demo.crashsdkdemo"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation project(':crashsdk')
}


================================================
FILE: app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile


================================================
FILE: app/release/output.json
================================================
[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1},"path":"app-release.apk","properties":{"packageId":"com.summer.demo.crashsdkdemo","split":"","minSdkVersion":"15"}}]

================================================
FILE: app/src/.gitignore
================================================
/androidTest
/test


================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.summer.demo.crashsdkdemo">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

================================================
FILE: app/src/main/java/com/summer/demo/crashsdkdemo/CrashInfoWindow.java
================================================
package com.summer.demo.crashsdkdemo;

import android.content.Context;
import android.graphics.Color;
import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;

/**
 * Created by summer on 26/06/2018.
 */

public class CrashInfoWindow extends FrameLayout {

    private static final String TAG = "CrashInfoWindow";

    private ScrollView mScrollView;
    private TextView mCrashInfo;

    private String mCrashFile;

    public CrashInfoWindow(Context context) {
        super(context);

        mScrollView = new ScrollView(context);
        mScrollView.setBackgroundColor(Color.TRANSPARENT);
        mCrashInfo = new TextView(context);
        mScrollView.addView(mCrashInfo, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        addView(mScrollView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
    }

    public void showCrash(String crashFilePath){
        mCrashFile = crashFilePath;

        update();
    }

    private int mTaskSeq = 0;
    private void update(){

        ++mTaskSeq;

        mCrashInfo.setText("Loading...");


        new Thread() {
            final private int taskSeq = mTaskSeq;
            StringBuffer sb;

            @Override
            public void run() {

                sb = FileUtils.readSmallFileText(mCrashFile);
                if(sb != null){

                    if(mTaskSeq == taskSeq){
                        new Handler(Looper.getMainLooper()).post(new Runnable() {
                            @Override
                            public void run() {
                                if(mTaskSeq == taskSeq){
                                    mCrashInfo.setText(sb.toString());
                                }
                            }
                        });
                    }
                }
            }
        }.start();
    }

}


================================================
FILE: app/src/main/java/com/summer/demo/crashsdkdemo/CrashRecordWindow.java
================================================
package com.summer.demo.crashsdkdemo;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.TextView;

import com.summer.crashsdk.CrashSDK;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by summer on 25/06/2018.
 */

public class CrashRecordWindow extends FrameLayout implements View.OnClickListener{

    private static final String TAG = "CrashRecordWindow";

    private ListView mListView;
    private MyAdapter mAdapter;
    private List<String> mData = new ArrayList<>();
    private ICallback mCallback;

    public CrashRecordWindow(Context context, ICallback cb) {
        super(context);

        mListView = new ListView(context);
        addView(mListView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        mAdapter = new MyAdapter();
        mListView.setAdapter(mAdapter);

        mCallback = cb;

        initData();
    }

    protected TextView createItemView(int position) {
        TextView v = new TextView(getContext());
        v.setGravity(Gravity.CENTER_VERTICAL);
        v.setOnClickListener(CrashRecordWindow.this);
        v.setTextSize(23);
        v.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,  ViewGroup.LayoutParams.WRAP_CONTENT));

        return v;
    }

    private int mTaskSeq = 0;
    public void initData(){

        ++mTaskSeq;


        new Thread() {
            final private int taskSeq = mTaskSeq;
            final List<String> ls = new ArrayList<>();

            @Override
            public void run() {

                String logDir = CrashSDK.getTombstonesDirectory();
                File fLogDir = new File(logDir);
                if(fLogDir.isDirectory()){
                    String[] logs = fLogDir.list();

                    if(logs != null){
                        for(int i=logs.length-1; i>=0;--i){
                            ls.add(logs[i]);
                        }
                    }
                }


                if(mTaskSeq == taskSeq){
                    new Handler(Looper.getMainLooper()).post(new Runnable() {
                        @Override
                        public void run() {
                            if(mTaskSeq == taskSeq){
                                mData.clear();
                                mData.addAll(ls);
                                mAdapter.notifyDataSetChanged();
                            }

                        }
                    });
                }

            }
        }.start();
    }

    @Override
    public void onClick(View v) {
        if(v.getTag() instanceof String){
            String name = (String)v.getTag();
            String directory = CrashSDK.getTombstonesDirectory();
            if(directory == null || name == null){
                return;
            }

            String fullPath = directory;
            if(!directory.endsWith(File.separator)){
                fullPath += File.separator;
            }
            fullPath += name;

            mCallback.showCrash(fullPath);
        }
    }

    private class MyAdapter extends BaseAdapter{

        @Override
        public int getCount() {
            return mData.size();
        }

        @Override
        public String getItem(int position) {
            return mData.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if(convertView == null){
                convertView = createItemView(position);
            }

            TextView tv = (TextView)convertView;
            tv.setText(getItem(position));
            tv.setTag(getItem(position));
            return convertView;
        }
    }

    interface ICallback{
        void showCrash(String path);
    }


}


================================================
FILE: app/src/main/java/com/summer/demo/crashsdkdemo/ExceptionHandler.java
================================================
package com.summer.demo.crashsdkdemo;

/**
 * Created by summer on 14/06/2018.
 */

public class ExceptionHandler {

    public static final void handleException(Throwable t){
        t.printStackTrace();
    }

}


================================================
FILE: app/src/main/java/com/summer/demo/crashsdkdemo/FileUtils.java
================================================
package com.summer.demo.crashsdkdemo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

/**
 * Created by summer on 26/06/2018.
 */

public class FileUtils {

    public static final StringBuffer readSmallFileText(String filePath){
        StringBuffer sb = new StringBuffer();
        File file = new File(filePath);
        BufferedReader reader = null;
        FileReader fr = null;
        try {
            fr = new FileReader(file);
            reader = new BufferedReader(fr);
            String buffer = null;
            while ((buffer = reader.readLine()) != null) {
                sb.append(buffer).append("\n\r");
            }
        } catch (IOException e) {
            ExceptionHandler.handleException(e);
        } finally {
            IOUtils.safeClose(fr);
            IOUtils.safeClose(reader);
        }

        return sb;
    }

}


================================================
FILE: app/src/main/java/com/summer/demo/crashsdkdemo/IOUtils.java
================================================
package com.summer.demo.crashsdkdemo;

import java.io.Closeable;
import java.io.IOException;

/**
 * Created by summer on 26/06/2018.
 */

public class IOUtils {

    public static final void safeClose(Closeable io){
        if(io != null){
            try {
                io.close();
            } catch (IOException e) {
                ExceptionHandler.handleException(e);
            }
        }
    }

}


================================================
FILE: app/src/main/java/com/summer/demo/crashsdkdemo/MainActivity.java
================================================
package com.summer.demo.crashsdkdemo;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Process;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.summer.crashsdk.CrashSDK;


public class MainActivity extends Activity implements View.OnClickListener, CrashRecordWindow.ICallback{

    private static final String TAG = "MainActivity";



    private final static int ID_NPE = 1;
    private final static int ID_WPE = 2;
    private final static int ID_VPE = 3;
    private final static int ID_STACK_OVERFLOW = 4;
    private final static int ID_OOM = 5;
    private final static int ID_ABORT = 6;
    private final static int ID_ART_CRASH = 7;
    private final static int ID_LIBC_CRASH = 8;


    private final static int ID_JAVA_CRASH_NPE = 1008;

    private final static int ID_SHOW_CRASH_LOG = 99999;

    private CrashRecordWindow mRecordWindow;
    private CrashInfoWindow mCrashInfoWindow;


    private final byte[] bbb = new byte[1<<23];

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        CrashSDK.init(getApplicationContext());

        bbb[0] = 0;

        FrameLayout view = new FrameLayout(this);
        setContentView(view);

        LinearLayout main = new LinearLayout(this);
        main.setOrientation(LinearLayout.VERTICAL);
        view.addView(main);

        mRecordWindow = new CrashRecordWindow(this, this);
        view.addView(mRecordWindow);
        mRecordWindow.setBackgroundColor(Color.WHITE);
        mRecordWindow.setVisibility(View.GONE);
        mCrashInfoWindow = new CrashInfoWindow(this);
        view.addView(mCrashInfoWindow);
        mCrashInfoWindow.setVisibility(View.GONE);
        mCrashInfoWindow.setBackgroundColor(Color.WHITE);

        TextView v = new TextView(this);
        v.setId(ID_NPE);
        v.setTextSize(28);
        v.setText("null pointer");
        v.setOnClickListener(this);
        main.addView(v);

        v = new TextView(this);
        v.setId(ID_WPE);
        v.setTextSize(28);
        v.setText("dangling pointer");
        v.setOnClickListener(this);
        main.addView(v);

        v = new TextView(this);
        v.setId(ID_VPE);
        v.setTextSize(28);
        v.setText("dangling virtual pointer");
        v.setOnClickListener(this);
        main.addView(v);

        v = new TextView(this);
        v.setId(ID_STACK_OVERFLOW);
        v.setTextSize(28);
        v.setText("stack overflow");
        v.setOnClickListener(this);
        main.addView(v);

        v = new TextView(this);
        v.setId(ID_OOM);
        v.setTextSize(28);
        v.setText("oom");
        v.setOnClickListener(this);
        main.addView(v);

        v = new TextView(this);
        v.setId(ID_ABORT);
        v.setTextSize(28);
        v.setText("abort");
        v.setOnClickListener(this);
        main.addView(v);

        v = new TextView(this);
        v.setId(ID_ART_CRASH);
        v.setTextSize(28);
        v.setText("invalidate jni");
        v.setOnClickListener(this);
        main.addView(v);

        v = new TextView(this);
        v.setId(ID_LIBC_CRASH);
        v.setTextSize(28);
        v.setText("libc crash");
        v.setOnClickListener(this);
        main.addView(v);

        v = new TextView(this);
        v.setId(ID_JAVA_CRASH_NPE);
        v.setTextSize(28);
        v.setTextColor(Color.RED);
        v.setText("java null pointer");
        v.setOnClickListener(this);
        main.addView(v);

        v = new TextView(this);
        v.setId(ID_SHOW_CRASH_LOG);
        v.setTextSize(28);
        v.setTextColor(Color.GRAY);
        v.setText("tombstones");
        v.setOnClickListener(this);
        main.addView(v);

        Log.d(TAG,"demo pid " + Process.myPid() + " tid " + Process.myTid());

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case ID_NPE:
            case ID_WPE:
            case ID_VPE:
            case ID_STACK_OVERFLOW:
            case ID_OOM:
            case ID_ABORT:
            case ID_ART_CRASH:
            case ID_LIBC_CRASH:
                CrashSDK.doCrash(v.getId());
                break;

            case ID_JAVA_CRASH_NPE:{
                throw new NullPointerException("Crash test");
            }
            case ID_SHOW_CRASH_LOG:{
                mCrashInfoWindow.setVisibility(View.GONE);
                mRecordWindow.setVisibility(View.VISIBLE);
                mRecordWindow.initData();
                break;
            }
        }
    }

    @Override
    public void onBackPressed() {
        if(mCrashInfoWindow.getVisibility() == View.VISIBLE){
            mCrashInfoWindow.setVisibility(View.GONE);
            return;
        }

        if(mRecordWindow.getVisibility() == View.VISIBLE){
            mRecordWindow.setVisibility(View.GONE);
            return;
        }

        super.onBackPressed();
    }

    @Override
    public void showCrash(String path) {
        mCrashInfoWindow.showCrash(path);
        mCrashInfoWindow.setVisibility(View.VISIBLE);
    }
}


================================================
FILE: app/src/main/res/drawable/ic_launcher_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="108dp"
    android:height="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108">
    <path
        android:fillColor="#26A69A"
        android:pathData="M0,0h108v108h-108z" />
    <path
        android:fillColor="#00000000"
        android:pathData="M9,0L9,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,0L19,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M29,0L29,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M39,0L39,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M49,0L49,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M59,0L59,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M69,0L69,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M79,0L79,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M89,0L89,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M99,0L99,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,9L108,9"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,19L108,19"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,29L108,29"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,39L108,39"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,49L108,49"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,59L108,59"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,69L108,69"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,79L108,79"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,89L108,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M0,99L108,99"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,29L89,29"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,39L89,39"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,49L89,49"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,59L89,59"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,69L89,69"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M19,79L89,79"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M29,19L29,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M39,19L39,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M49,19L49,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M59,19L59,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M69,19L69,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
    <path
        android:fillColor="#00000000"
        android:pathData="M79,19L79,89"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8" />
</vector>


================================================
FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="108dp"
    android:height="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108">
    <path
        android:fillType="evenOdd"
        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1">
        <aapt:attr name="android:fillColor">
            <gradient
                android:endX="78.5885"
                android:endY="90.9159"
                android:startX="48.7653"
                android:startY="61.0927"
                android:type="linear">
                <item
                    android:color="#44000000"
                    android:offset="0.0" />
                <item
                    android:color="#00000000"
                    android:offset="1.0" />
            </gradient>
        </aapt:attr>
    </path>
    <path
        android:fillColor="#FFFFFF"
        android:fillType="nonZero"
        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1" />
</vector>


================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background" />
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background" />
    <foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

================================================
FILE: app/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>
</resources>


================================================
FILE: app/src/main/res/values/strings.xml
================================================
<resources>
    <string name="app_name">Crash</string>
</resources>


================================================
FILE: app/src/main/res/values/styles.xml
================================================
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
        <!-- Customize your theme here. -->
    </style>

</resources>


================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}


================================================
FILE: crashsdk/.gitignore
================================================
/build


================================================
FILE: crashsdk/build.gradle
================================================
apply plugin: 'com.android.library'

android {
    compileSdkVersion 26

    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }

    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 26
        versionCode 3
        versionName "2.1"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

}


================================================
FILE: crashsdk/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

-keep class com.summer.crashsdk.** { *; }

================================================
FILE: crashsdk/src/.gitignore
================================================
/test
/androidTest
/obj


================================================
FILE: crashsdk/src/jni/Android.mk
================================================
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

ifeq ($(TARGET_PLATFORM), android-14)
    LOCAL_MODULE    := crashsdk
else
	LOCAL_MODULE    := crashsdk21
endif

#compile with c++
LOCAL_CFLAGS := -std=c++11

NDK_APP_DST_DIR := ../../libs/$(TARGET_ARCH_ABI)

#specify where to find header files.
LOCAL_C_INCLUDES := $(LOCAL_PATH)/
LOCAL_C_INCLUDES += $(LOCAL_PATH)/utils

LOCAL_C_INCLUDES += $(LOCAL_PATH)/crash
LOCAL_C_INCLUDES += $(LOCAL_PATH)/crash/unwind
LOCAL_C_INCLUDES += $(LOCAL_PATH)/crash/unwind/eh_frame

#specify source file need be compiled.
MY_SRC_LIST := $(wildcard $(LOCAL_PATH)/*.cpp)
MY_SRC_LIST += $(wildcard $(LOCAL_PATH)/utils/*.cpp)

MY_SRC_LIST += $(wildcard $(LOCAL_PATH)/crash/*.cpp)

ifneq (,$(filter $(TARGET_ARCH_ABI),x86))
	LOCAL_C_INCLUDES += $(LOCAL_PATH)/crash/unwind/x86
	MY_SRC_LIST += $(wildcard $(LOCAL_PATH)/crash/unwind/*.cpp)
	MY_SRC_LIST += $(wildcard $(LOCAL_PATH)/crash/unwind/x86/*.cpp)
	MY_SRC_LIST += $(wildcard $(LOCAL_PATH)/crash/unwind/eh_frame/*.cpp)
endif

ifneq (,$(filter $(TARGET_ARCH_ABI),armeabi-v7a))
	LOCAL_C_INCLUDES += $(LOCAL_PATH)/crash/unwind/arm
	MY_SRC_LIST += $(wildcard $(LOCAL_PATH)/crash/unwind/*.cpp)
	MY_SRC_LIST += $(wildcard $(LOCAL_PATH)/crash/unwind/arm/*.cpp)
	
	ifneq ($(TARGET_PLATFORM), android-14)
    	MY_SRC_LIST += $(wildcard $(LOCAL_PATH)/crash/unwind/eh_frame/*.cpp)
	endif
	
endif


LOCAL_SRC_FILES := $(MY_SRC_LIST:$(LOCAL_PATH)/%=%)

#$(warning $(MY_SRC_LIST))

LOCAL_LDLIBS += -llog -landroid
include $(BUILD_SHARED_LIBRARY)	

================================================
FILE: crashsdk/src/jni/Application.mk
================================================
APP_PLATFORM = android-14
APP_ABI := armeabi-v7a x86
APP_STL := gnustl_static


================================================
FILE: crashsdk/src/jni/Application_21.mk
================================================
APP_PLATFORM = android-21
APP_ABI := armeabi-v7a
APP_STL := gnustl_static


================================================
FILE: crashsdk/src/jni/CrashSDK.cpp
================================================
//
// Created by Summer on 09/06/2018.
//

#include <jni.h>
#include "Defines.h"
#include "Log.h"
#include "CrashMonitor.h"

#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jint JNICALL
Java_com_summer_crashsdk_CrashSDK_initNative(JNIEnv *env,jobject obj)
{
	LOGR(PTAG(),"init...");
	return CrashMonitor::init(env);
}

JNIEXPORT void JNICALL
Java_com_summer_crashsdk_CrashSDK_setSystemInfo(JNIEnv *env,jobject obj, jstring fingerprint, jint version, jstring abis)
{
	const char* jni_fingerprint = env->GetStringUTFChars(fingerprint,0);
	const char* jni_abis = env->GetStringUTFChars(abis,0);
	CrashMonitor::setSystemInfo(jni_fingerprint, version, jni_abis);

	env->ReleaseStringUTFChars(abis,jni_abis);
	env->ReleaseStringUTFChars(fingerprint,jni_fingerprint);
}

JNIEXPORT void JNICALL
Java_com_summer_crashsdk_CrashSDK_setLogDir(JNIEnv *env,jobject obj, jstring dir)
{
	const char* szDir = env->GetStringUTFChars(dir,0);
	CrashMonitor::setLogDir(szDir);
	env->ReleaseStringUTFChars(dir,szDir);
}

jint JNI_OnLoad(JavaVM *vm, void *reserved) 
{
	LOGR(PTAG(),"jni onload...");
	CrashMonitor::setVM(vm);
	return JNI_VERSION_1_6;
}

#ifdef __cplusplus
}
#endif

================================================
FILE: crashsdk/src/jni/Test.cpp
================================================
//
// Created by Summer on 09/06/2018.
//

#ifndef CRASHSDKDEMO_TEST_H
#define CRASHSDKDEMO_TEST_H

#include <jni.h>
#include <string.h>
#include <stdlib.h>
#include "Defines.h"
#include "Log.h"

#ifdef __cplusplus
extern "C" {
#endif

enum TEST_ID{
	NPE = 1,
	WPE = 2,
	VPE = 3,
	STACK_OVERFLOW = 4,
	OOM = 5,
	ABORT = 6,
	ART_CRASH = 7,
	LIBC_CRASH =8,
};

void __attribute__((noinline)) a(int aa){
	LOGR(PTAG("test"),"a %d", aa);

	int arr[2000];
	memset(arr,0,sizeof(arr));
	for(int i=0;i<1000;i++){
		arr[i] = i;
	}

	a(arr[aa] + 1);
}

static jobject sobj;

void invalidatePointer(){
	int *p = (int*)0x123;
	a(*p);
}

void stackOverflow(){
	a(1);
}

void artCrash(JNIEnv *env, jobject obj){
	if(sobj == 0){
		sobj = obj;
	}else{
		jmethodID id = env->GetStaticMethodID((jclass)sobj, "getName", "()Ljava/lang/String;");
		jstring ret = (jstring)env->CallObjectMethod((jclass)sobj, id);
		const char* jsz = env->GetStringUTFChars(ret,0);
		LOGE(PTAG("test"),"test class: %s", jsz);
		env->ReleaseStringUTFChars(ret, jsz);
	}
}

class Test{
public:
	virtual void f(){
		LOGE(PTAG("test"), "test#f %d", a);
	}

	int a;
};

void falseVirtualPointer(){
	Test *t = new Test();
	SAFE_DELETE(t);
	t->f();

}

void oom(){

}


JNIEXPORT jint JNICALL
Java_com_summer_crashsdk_CrashSDK_test(JNIEnv *env,jobject obj, jint id)
{
	switch(id){
		case NPE:{
			int *p = NULL;
			a(*p);
			break;
		}
		case WPE:{
			int *dp = (int*)0xd123;
			LOGE(PTAG("test"), "dp %d", *dp);
			break;
		}
		case VPE:{
			falseVirtualPointer();
			break;
		}
		case STACK_OVERFLOW:{
			stackOverflow();
			break;
		}
		case OOM:{
			oom();
			break;
		}
		case ABORT:{
			abort();
			break;
		}
		case ART_CRASH:{
			artCrash(env, obj);
			break;
		}
		case LIBC_CRASH:{
			char* s = (char*)333;
			strncpy(s, "abc", 12);
			break;
		}
	}

	LOGD(PTAG("test"),"end of moduleTest");

	return 0;
}

#ifdef __cplusplus
}
#endif

#endif //CRASHSDKDEMO_TEST_H


================================================
FILE: crashsdk/src/jni/crash/CrashMonitor.cpp
================================================
#include "CrashMonitor.h"
#include "Defines.h"
#include "Log.h"
#include <cxxabi.h>
#include <ucontext.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>

JavaVM* CrashMonitor::sVM = NULL;
jclass CrashMonitor::sClzDebug = 0;
jmethodID CrashMonitor::sMidGetNativeHeapSize = 0;
jmethodID CrashMonitor::sMidGetNativeHeapAllocatedSize = 0;

struct sigaction CrashMonitor::sOldSigAction;
const int CrashMonitor::SIGNALS[] = { SIGABRT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV, SIGTERM, SIGPIPE, 

#ifdef SIGSTKFLT
	SIGSTKFLT,
#endif
	 0 };

const char* CrashMonitor::TAG = PTAG("CrashMonitor");

int CrashMonitor::sPID = 0;
char CrashMonitor::sLogDir[128];
char CrashMonitor::sProcessName[60];
char CrashMonitor::sThreadName[60];
char CrashMonitor::sFingerprint[100];
int CrashMonitor::sVersion = 0;
char CrashMonitor::sABIs[50];
char CrashMonitor::sTEMP[512];

int CrashMonitor::init(JNIEnv* env){

	LOGD(TAG,"arch: %s", ARCH_NAME);

	struct sigaction action;
	memset(&action, 0, sizeof(action));
    sigemptyset(&action.sa_mask);
    action.sa_sigaction = CrashMonitor::handle;
    action.sa_flags = SA_SIGINFO | SA_ONSTACK;

    sOldSigAction.sa_sigaction = NULL;
    sOldSigAction.sa_handler = NULL;

    for(int i=0;i<sizeof(SIGNALS);i++){
    	int sig = SIGNALS[i];
    	if(sig == 0){
    		break;
    	}

    	if (sigaction(sig, &action, &sOldSigAction) != 0) {
    		LOGE(TAG,"register signal(%d) failed.",sig);
			return 1;
		}
    }

    stack_t stack;  
	memset(&stack, 0, sizeof(stack));  
	/* Reserver the system default stack size. We don't need that much by the way. */  
	stack.ss_size = SIGSTKSZ;  
	stack.ss_sp = malloc(stack.ss_size);  
	if(stack.ss_sp == NULL){
		LOGE(TAG,"alloc preserved stack space failed.");
		return 1;
	}
	stack.ss_flags = 0;  
	/* Install alternate stack size. Be sure the memory region is valid until you revert it. */  
	if (sigaltstack(&stack, NULL) != 0) {  
		LOGE(TAG,"set preserved stack space failed.");
		return 1;
	}

	initProcessInfo();
	initJniBridge(env);

    LOGD(TAG,"init crash monitor successed.");
	
	return 0;
}

void CrashMonitor::setLogDir(const char* dir){
	strncpy(sLogDir, dir, sizeof(sLogDir));
	LOGR(TAG,"setLogDir: %s", sLogDir);
}

void CrashMonitor::initProcessInfo(){
	sPID = getpid();
	sprintf(sProcessName,"/proc/%d/cmdline",sPID);
	if(readline(sProcessName, sProcessName, sizeof(sProcessName)) == 0){
		LOGR(TAG,"initProcessInfo successed, pid=%d, name=%s", sPID, sProcessName);
	}  
}

int CrashMonitor::readline(const char* file, char* out, int len){

	int fd = open(file,O_RDONLY);
	if(fd < 0){
		LOGE(TAG,"open %s file failed.", file);
		return 1;
	}

	char c;
	int i = 0;
	while(i < len && read(fd,&c,1)==1){
		if(c == '\n' || c == ' ' || c == '\r'){
			break;
		}
		out[i++] = c;
	}

	out[i] = 0;
    
    if(close(fd)<0){
        LOGE(TAG,"close %s failed.", file);
        return 1;
    }

    return 0;
}

void CrashMonitor::setSystemInfo(const char* fingerprint, int version, const char* abis){
	strncpy(sFingerprint, fingerprint,sizeof(sFingerprint) - 1);
	strncpy(sABIs, abis,sizeof(sABIs) - 1);
	sFingerprint[sizeof(sFingerprint)-1] = '\0';
	sABIs[sizeof(sABIs)-1] = '\0';
	sVersion = version;

	LOGR(TAG,"setSystemInfo: fingerprint: %s\n version: %d\n abis: %s", fingerprint, version, abis);
}

void CrashMonitor::handle(int sig, siginfo_t* si, void* uc){
	LOGE(TAG,"recv signal(%d), siginfo(%p), ucontext(%p) si_code(%d)",sig,si,uc, si->si_code);

	int fd = -1;
	time_t rawtime;
	struct tm *timeinfo;
	time(&rawtime);
	timeinfo = localtime(&rawtime);

	LOGE(TAG,"sLogDir: %s", sLogDir);
	sprintf(sTEMP,"%s/%4d%02d%02d_%02d_%02d_%02d.crash", sLogDir, 
						1900+timeinfo->tm_year, 1+timeinfo->tm_mon, timeinfo->tm_mday,
						timeinfo->tm_hour,timeinfo->tm_min,timeinfo->tm_sec);
	LOGE(TAG,"create crash log file: %s", sTEMP);
	fd = open(sTEMP,O_CREAT|O_WRONLY);

	if(fd < 0){
		LOGE(TAG,"create log file failed.");
	}

	logCrashHeader(fd, si);

#ifdef UNWIND_SUPPORT
	LOGR(TAG,"dump call stack...");
	Unwind und = Unwind();
	und.setucontext((ucontext_t*)uc);
	UnwindNode* head = NULL;
	und.unwind(&head);

	printBacktrace(fd, *head);

#else
	LOGR(TAG,"current arch(%s) not support unwind yet.", ARCH_NAME);
#endif

	if(sOldSigAction.sa_sigaction != NULL){
		LOGD(TAG,"relay to old sa_sigaction.");
		sOldSigAction.sa_sigaction(sig,si,uc);
	}else if(sOldSigAction.sa_handler != NULL){
		LOGD(TAG,"relay to old sa_handler.");
		sOldSigAction.sa_handler(sig);
	}

	if(fd >= 0 && close(fd) < 0){
		LOGE(TAG,"close log file failed.");
	}

	LOGR(TAG,"handle finished.");

	return;
}

void CrashMonitor::logCrashHeader(int fd, siginfo* siginfo){
	int len = 0;
	int r = sizeof(sTEMP);
	int l = snprintf(sTEMP,r, 
		"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"
		"Build fingerprint: \'%s\'\n"
		"Revision: %d\n"
		"ABI: \'%s\'\n", sFingerprint, sVersion, sABIs);

	if(l > 0){
		len += l; r -= l;
	}

	int tid = gettid();
	sprintf(sThreadName,"/proc/%d/comm",tid);
	if(readline(sThreadName, sThreadName, sizeof(sThreadName)) == 0){
		l = snprintf(sTEMP+len, r, "pid: %d, tid: %d, name: %s  >>> %s <<<\n", sPID, tid, sThreadName, sProcessName);
	}else{
		l = snprintf(sTEMP+len, r, "pid: %d, >>> %s <<<\n", sPID, sProcessName);
	}

	if(l > 0){
		len += l; r -= l;
	}



	if((l = parseSignalInfo(siginfo, sTEMP + len, r)) > 0){
		len += l; r -= l;
		snprintf(sTEMP+len, r, "\n");
		++len; --r;
	}

	if(len > 0){
		LOGE(TAG,"%s",sTEMP);

		if(fd >= 0){
			write(fd, sTEMP, len);
		}
		
	}

}

void CrashMonitor::printBacktrace(int fd, UnwindNode& head){
	LOGR(TAG,"backtrace:");
	if(fd >= 0){
		write(fd, "backtrace:\n", 11);
	}

	UnwindNode* p = &head;
	int i = 0;
	int l = 0;
	while(p){
		l = 0;
		const char* symbol = NULL;
		Dl_info info;
        if(dladdr(p->ip, &info) == 0){
        	// LOGE(TAG,"dladdr (0x%p) failed.", p->ip);
        	switch(sizeof(void*)){
				case 4:
				l = snprintf(sTEMP,sizeof(sTEMP),"\t#%02d pc %08x  unknow\n", i, p->pc);
				break;
				case 8:
				l = snprintf(sTEMP,sizeof(sTEMP),"\t#%02d pc %016lx  unknown\n", i, (unsigned long)p->pc);
				break;
			}
        }else{
        	if(info.dli_sname == NULL){
				switch(sizeof(void*)){
					case 4:
					l = snprintf(sTEMP,sizeof(sTEMP),"\t#%02d pc %08x  %s\n", i, p->pc, p->libname);
					break;
					case 8:
					l = snprintf(sTEMP,sizeof(sTEMP),"\t#%02d pc %016lx  %s\n", i, (unsigned long)p->pc, p->libname);
					break;
				}
        	}else{
        		switch(sizeof(void*)){
					case 4:
					l = snprintf(sTEMP,sizeof(sTEMP),"\t#%02d pc %08x  %s (%s+%u)\n", i, (word_t)((word_t)p->ip - (word_t)info.dli_fbase), info.dli_fname, info.dli_sname, ((uint32_t)p->ip)-((uint32_t)info.dli_saddr));
					break;
					case 8:
					l = snprintf(sTEMP,sizeof(sTEMP),"\t#%02d pc %016lx  %s (%s+%llu)\n", i, (unsigned long)((unsigned long)p->ip-(unsigned long)info.dli_fbase), info.dli_fname, info.dli_sname, ((uint64_t)p->ip)-((uint64_t)info.dli_saddr));
					break;
				}
        	}
        	
        }

        LOGE(TAG,"%s", sTEMP);
        if(fd >= 0 && l > 0){
        	write(fd, sTEMP, l);
        }
		
		++i;
		p = p->next;
	}
}

const int CrashMonitor::parseSignalInfo(siginfo_t* siginfo, char* out, int len){

	switch(siginfo->si_signo){
		case SIGSEGV:{

			switch(siginfo->si_code){
				case SEGV_MAPERR: return snprintf(out,len,"signal %d (%s), code %d (%s), fault addr %p", siginfo->si_signo, "SIGSEGV", siginfo->si_code, "SEGV_MAPERR", siginfo->si_addr);
				case SEGV_ACCERR: return snprintf(out,len,"signal %d (%s), code %d (%s), fault addr %p", siginfo->si_signo, "SIGSEGV", siginfo->si_code, "SEGV_ACCERR", siginfo->si_addr);
				case SEGV_BNDERR: return snprintf(out,len,"signal %d (%s), code %d (%s)", siginfo->si_signo, "SIGSEGV", siginfo->si_code, "SEGV_BNDERR");
				case SEGV_PKUERR: return snprintf(out,len,"signal %d (%s), code %d (%s)", siginfo->si_signo, "SIGSEGV", siginfo->si_code, "SEGV_PKUERR");
			}

			return snprintf(out,len,"signal %d (%s), code %d", siginfo->si_signo, "SIGSEGV", siginfo->si_code);
		}

		case SIGABRT:{
			return snprintf(out,len,"signal %d (%s), code %d", siginfo->si_signo, "SIGABRT", siginfo->si_code);
		}
		case SIGILL:{
			return snprintf(out,len,"signal %d (%s), code %d", siginfo->si_signo, "SIGILL", siginfo->si_code);
		}
		case SIGTRAP:{
			return snprintf(out,len,"signal %d (%s), code %d", siginfo->si_signo, "SIGTRAP", siginfo->si_code);
		}
		case SIGBUS:{
			return snprintf(out,len,"signal %d (%s), code %d", siginfo->si_signo, "SIGBUS", siginfo->si_code);
		}
		case SIGFPE:{
			return snprintf(out,len,"signal %d (%s), code %d", siginfo->si_signo, "SIGFPE", siginfo->si_code);
		}
		case SIGTERM:{
			return snprintf(out,len,"signal %d (%s), code %d", siginfo->si_signo, "SIGTERM", siginfo->si_code);
		}
#ifdef SIGSTKFLT
		case SIGSTKFLT:{
			return snprintf(out,len,"signal %d (%s), code %d", siginfo->si_signo, "SIGSTKFLT", siginfo->si_code);
		}
#endif

	}

	return 0;
}

void CrashMonitor::initJniBridge(JNIEnv* env){
 	jclass clz = env->FindClass("android/os/Debug");
 	if(clz != NULL){
 		sClzDebug = (jclass)env->NewGlobalRef(clz);
 	}

 	sMidGetNativeHeapSize = env->GetStaticMethodID(sClzDebug, "getNativeHeapSize", "()J");
 	sMidGetNativeHeapAllocatedSize = env->GetStaticMethodID(sClzDebug, "getNativeHeapAllocatedSize", "()J");
}

void CrashMonitor::setVM(JavaVM* vm){
	sVM = vm;
	return;
}

JNIEnv* CrashMonitor::getEnv(){
	JNIEnv* env = NULL;
    int state = sVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
    if (state == JNI_EDETACHED || env == NULL) {
        LOGD(TAG,"getEnv, env not attached yet");
        if (sVM->AttachCurrentThread(&env, NULL) != JNI_OK) {
        	LOGE(TAG,"getEnv, attach env to current thread failed.");
            return NULL;
        }
    } else if (state == JNI_OK) {
        LOGD(TAG,"getEnv, env already attached.");
    } else if (state == JNI_EVERSION) {
    	LOGE(TAG,"getEnv, jni version not supported");
    	return NULL;
    }

    return env;
}

uint64_t CrashMonitor::getNativeHeapAllocatedSize(){
	JNIEnv* env = getEnv();
	if(env != NULL){
		if(sMidGetNativeHeapAllocatedSize != 0){
	    	uint64_t ret = env->CallStaticLongMethod(sClzDebug, sMidGetNativeHeapAllocatedSize);
	    	jthrowable ex = (env)->ExceptionOccurred();
		    if (ex) {
		        env->ExceptionDescribe();
		        env->ExceptionClear();
		        env->DeleteLocalRef(ex);
		    }

	    	return ret;
	    }
	}
    
    return 0L;
}

uint64_t CrashMonitor::getNativeHeapSize(){
	JNIEnv* env = getEnv();
	if(env != NULL){
		if(sMidGetNativeHeapSize != 0){
	    	uint64_t ret = env->CallStaticLongMethod(sClzDebug, sMidGetNativeHeapSize);
	    	jthrowable ex = (env)->ExceptionOccurred();
		    if (ex) {
		        env->ExceptionDescribe();
		        env->ExceptionClear();
		        env->DeleteLocalRef(ex);
		    }
	    	return ret;
	    }
	}
    
    return 0L;
}



================================================
FILE: crashsdk/src/jni/crash/CrashMonitor.h
================================================
#ifndef CRASH_MONITOR_H
#define CRASH_MONITOR_H

#include <jni.h>
#include <signal.h>
#include <unwind.h>
#include <dlfcn.h>
#include "_Unwind.h"

struct BacktraceState{
    void** current;
    void** end;
};

struct CrashInfo{
	uint32_t pid;
	uint32_t tid;

	int sig;

};

class CrashMonitor{

public:
	static int init(JNIEnv* env);
	static void setLogDir(const char* dir);
	static void setSystemInfo(const char* fingerprint, int version, const char* abis);
	static void setVM(JavaVM* vm);

private:
	static void initProcessInfo();
	static int readline(const char* file, char* out, int len);
	static void handle(int sig, siginfo_t* si, void* uctx);
	static void logCrashHeader(int fd, siginfo_t *si);
	static const int parseSignalInfo(siginfo_t* siginfo, char* out, int len);

	static void printBacktrace(int fd, UnwindNode& head);

	
	static JNIEnv* getEnv();
	static void initJniBridge(JNIEnv* env);
	static uint64_t getNativeHeapAllocatedSize();
	static uint64_t getNativeHeapSize();

	static struct sigaction sOldSigAction;

	static JavaVM* sVM;
	static jclass sClzDebug;
	static jmethodID sMidGetNativeHeapSize;
	static jmethodID sMidGetNativeHeapAllocatedSize;

	static const int SIGNALS[];
	static const char* TAG;

	static int sPID;
	static char sLogDir[];
	static char sProcessName[];
	static char sThreadName[];
	static char sFingerprint[];
	static int sVersion;
	static char sABIs[];

	static char sTEMP[];

};


#endif

================================================
FILE: crashsdk/src/jni/crash/Defines.h
================================================
#ifndef DEFINES_H
#define DEFINES_H

#define PTAG(tag) "CrashSDK." tag

#define SAFE_FREE(p) if(p != NULL) {free(p);p=NULL;}
#define SAFE_DELETE(p) if(p != NULL) {delete p; p = NULL;}

#define OFFSET(type,field)	((char *) &((type *) 0)->field - (char *) 0)

#define ARRAY_SIZE(a)	(sizeof (a) / sizeof ((a)[0]))


#endif


================================================
FILE: crashsdk/src/jni/crash/unwind/Arch.h
================================================
#ifndef Unwind_arch_h
#define Unwind_arch_h

#if defined __aarch64__
#define ARCH_NAME "__aarch64__"
#elif defined __arm__
#define ARCH_NAME "__arm__"
#define UNWIND_SUPPORT 1
#include "Config_arm.h"
#elif defined __hppa__
#define ARCH_NAME "__hppa__"
#elif defined __ia64__
#define ARCARCH_NAMEH "__ia64__"
#elif defined __mips__
#define ARCH_NAME "__mips__"
#elif defined __i386__
#define ARCH_NAME "__i386__"
#define UNWIND_SUPPORT 1
#include "Config_x86.h"
#elif defined __x86_64__
#define ARCH_NAME "__x86_64__"
#else
#define ARCH_NAME "unknown"
#endif

#endif

================================================
FILE: crashsdk/src/jni/crash/unwind/Context.cpp
================================================
#include "Arch.h"
#include "Context.h"
#include "Log.h"
#include "EHFrame.h"

const char* Context::TAG = PTAG("Context");

void Context::setucontext(ucontext_t *uc){
	mContext = uc;
}

int Context::reset(){
	LOGD(TAG,"sizeof CFI = %d", sizeof(mCfi));
	memset(&mCfi, 0, sizeof(mCfi));
	mCfi.last = 1;

	return setupFrame(mCfi);
}

bool Context::rootFrame(){
	return mCfi.root;
}

void* Context::getCFA(){
	return (void*)mCfi.cfa;
}

void* Context::getIP(){
	return (void*)mCfi.ip;
}

void* Context::lastLibBase(){
	return (void*)mCfi.last_libbase;
}

const char* Context::lastLibName(){
	return mCfi.last_libname;
}

int Context::readS8( word_t* addr, int8_t* valp, void* arg){
	uint8_t uval;
	int ret;

	if ((ret = readU8 (addr, &uval, arg)) < 0){
		return ret;
	}

	*valp = (int8_t) uval;
	return 0;
}


int Context::readS16( word_t* addr, int16_t* valp, void* arg){

	uint16_t uval;
	int ret;

	if ((ret = readU16 (addr, &uval, arg)) < 0){
		return ret;
	}

	*valp = (int16_t) uval;
	return 0;
}


int Context::readS32( word_t* addr, int32_t* valp, void* arg){
	uint32_t uval;
	int ret;

	if ((ret = readU32 (addr, &uval, arg)) < 0){
		return ret;
	}

	*valp = (int32_t) uval;
	return 0;
}


int Context::readS64( word_t* addr, int64_t* valp, void* arg){
	uint64_t uval;
	int ret;

	if ((ret = readU64 (addr, &uval, arg)) < 0){
		return ret;
	}

	*valp = (int64_t) uval;
	return 0;
}

int Context::readU8( word_t* addr, uint8_t* valp, void* arg){
	*valp = ((value_t *)(uintptr_t)*addr)->u8;
	*addr += sizeof (*valp);
	return 0;
}


int Context::readU16( word_t* addr, uint16_t* valp, void* arg){

	*valp = ((value_t *)(uintptr_t)*addr)->u16;
	*addr += sizeof (*valp);
	return 0;
}


int Context::readU32( word_t* addr, uint32_t* valp, void* arg){
	*valp = ((value_t *)(uintptr_t)*addr)->u32;
	*addr += sizeof (*valp);
	return 0;
}


int Context::readU64( word_t* addr, uint64_t* valp, void* arg){
	*valp = ((value_t *)(uintptr_t)*addr)->u64;
	*addr += sizeof (*valp);
	return 0;
}


int Context::readW( word_t* addr, word_t* valp, void* arg){
	uint32_t u32;
	uint64_t u64;
	int ret;

	switch (sizeof(word_t)){
		case 4:
			if((ret = readU32 (addr, &u32, arg)) < 0){
				return ret;
			}

			*valp = u32;
			return ret;

		case 8:
			if((ret = readU64 (addr, &u64, arg)) < 0){
				return ret;
			}

			*valp = u64;
			return ret;

		default:
			LOGE(TAG,"fault address size");
			return -1;
	}
}


int Context::readUleb128( word_t* addr, word_t* valp, void* arg){
	word_t val = 0, shift = 0;
	uint8_t byte;
	int ret;

	do{
		if ((ret = readU8 (addr, &byte, arg)) < 0){
			return ret;
		}

		val |= ((word_t) byte & 0x7f) << shift;
		shift += 7;
	}while (byte & 0x80);

	*valp = val;
	return 0;
}


int Context::readSleb128( word_t* addr, word_t* valp, void* arg){
	word_t val = 0, shift = 0;
	uint8_t byte;
	int ret;

	do{
		if ((ret = readU8 (addr, &byte, arg)) < 0){
			return ret;
		}

		val |= ((word_t) byte & 0x7f) << shift;
		shift += 7;
	}while (byte & 0x80);

	if (shift < 8 * sizeof (word_t) && (byte & 0x40) != 0){
		/* sign-extend negative value */
		val |= ((word_t) -1) << shift;
	}
		
	*valp = val;
	return 0;
}

int Context::sword (word_t val, sword_t &out){
	switch (getAddrWidth()){
		case 1: out = (int8_t) val; return 0;
		case 2: out = (int16_t) val; return 0;
		case 4: out = (int32_t) val; return 0;
		case 8: out = (int64_t) val; return 0;
		default: LOGE(TAG,"unknown address width %d", getAddrWidth());
	}

	return -1;
}



================================================
FILE: crashsdk/src/jni/crash/unwind/Context.h
================================================
#ifndef Context_h
#define Context_h

#include "Defines.h"
#include "Arch.h"
#include "DataTypes.h"
#include <ucontext.h>
#include <dlfcn.h>

class EHFrame;
class Context{

	friend class EHFrame;

public:
	virtual ~Context(){}

	virtual void setucontext(ucontext_t *uc);
	virtual int reset();
	virtual bool rootFrame();
	virtual void* getCFA();
	virtual void* getIP();
	virtual void* lastLibBase();
	virtual const char* lastLibName();
	virtual uint8_t getAddrWidth(){
		return sizeof(word_t);
	}
	virtual bool isBigEndian(){
		return false;
	}
	
	virtual int restoreFrame() = 0;
	virtual word_t getReg(uint32_t reg) = 0;
	virtual int setupFrame(CFI& cfi) = 0;
	virtual int stepBack(CFI& cfi) = 0;

	virtual int readS8(word_t* addr, int8_t* valp, void* arg = NULL);
	virtual int readS16(word_t* addr, int16_t* valp, void* arg = NULL);
	virtual int readS32(word_t* addr, int32_t* valp, void* arg = NULL);
	virtual int readS64(word_t* addr, int64_t* valp, void* arg = NULL);
	virtual int readU8(word_t* addr, uint8_t* valp, void* arg = NULL);
	virtual int readU16(word_t* addr, uint16_t* valp, void* arg = NULL);
	virtual int readU32(word_t* addr, uint32_t* valp, void* arg = NULL);
	virtual int readU64(word_t* addr, uint64_t* valp, void* arg = NULL);
	virtual int readW(word_t* addr, word_t* valp, void* arg = NULL);
	virtual int readUleb128(word_t* addr, word_t* valp, void* arg = NULL);
	virtual int readSleb128(word_t* addr, word_t* valp, void* arg = NULL);
	virtual int sword (word_t val, sword_t &out);

	CFI mCfi;

protected:
	static const char* TAG;

	ucontext_t* mContext;
	
};

#ifdef __i386__
	#include "Context_x86.h"
#elif defined __arm__
	#include "Context_arm.h"
#endif

#endif

================================================
FILE: crashsdk/src/jni/crash/unwind/DataTypes.h
================================================
#ifndef DataTypes_h
#define DataTypes_h

#include "Arch.h"
#include <stdint.h>

typedef struct table_entry{
	int32_t start_ip_offset;
	int32_t fde_offset;
}table_entry;

typedef struct eh_frame_hdr{
	uint8_t version;
	uint8_t eh_frame_ptr_enc;
	uint8_t fde_count_enc;
	uint8_t table_enc;
	/* The rest of the header is variable-length and consists of the
	   following members:

	encoded_t eh_frame_ptr;
	encoded_t fde_count;
	struct
	  {
	    encoded_t start_ip;	// first address covered by this FDE
	    encoded_t fde_addr;	// address of the FDE
	  }
	binary_search_table[fde_count];  */
}eh_frame_hdr_t;

typedef enum{
	DWARF_WHERE_UNDEF,		/* register isn't saved at all */
	DWARF_WHERE_SAME,		/* register has same value as in prev. frame */
	DWARF_WHERE_CFAREL,		/* register saved at CFA-relative address */
	DWARF_WHERE_REG,		/* register saved in another register */
	DWARF_WHERE_EXPR,		/* register saved */
}dwarf_where_t;

typedef struct{
	dwarf_where_t where;	/* how is the register saved? */
	word_t val;		/* where it's saved */
}dwarf_save_loc_t;

#define DWARF_CFA_REG_COLUMN	DWARF_NUM_PRESERVED_REGS
#define DWARF_CFA_OFF_COLUMN	(DWARF_NUM_PRESERVED_REGS + 1)

typedef struct dwarf_reg_state{
	struct dwarf_reg_state *next;	/* for rs_stack */
	dwarf_save_loc_t reg[DWARF_NUM_PRESERVED_REGS + 2];
	word_t ip;		          /* ip this rs is for */
	word_t ret_addr_column;       /* indicates which column in the rule table represents return address */
	unsigned short lru_chain;	  /* used for least-recently-used chain */
	unsigned short coll_chain;	/* used for hash collisions */
	unsigned short hint;	      /* hint for next rs to try (or -1) */
	unsigned short valid : 1;         /* optional machine-dependent signal info */
	unsigned short signal_frame : 1;  /* optional machine-dependent signal info */
}dwarf_reg_state_t;

typedef struct dwarf_state_record{
	unsigned char fde_encoding;
	word_t args_size;

	dwarf_reg_state_t rs_initial;	/* reg-state after CIE instructions */
	dwarf_reg_state_t rs_current;	/* current reg-state */
}dwarf_state_record_t;

typedef struct CallFrameInfo{

	word_t ip;	//current ip of this call frame
	word_t cfa; //current cfa of this call frame

	const char* last_libname; //the name of lib the last ip located in.
    word_t last_libbase; // this load base addr of lib the last ip locate in.

	word_t loc[DWARF_NUM_PRESERVED_REGS];
	
	uint8_t root : 1;
	uint8_t last : 1;
	uint8_t signal_frame : 1;

	//eh_frame info start>>>
	word_t gp = 0;
	uint8_t version;
	word_t code_align;
	word_t data_align;
	word_t handler;
    uint16_t abi;
    uint16_t tag;
    uint8_t fde_encoding;
    uint8_t lsda_encoding;
    
    word_t lsda;

    word_t start_ip;
	word_t end_ip;
    word_t cie_instr_start;	/* start addr. of CIE "initial_instructions" */
    word_t cie_instr_end;	/* end addr. of CIE "initial_instructions" */
    word_t fde_instr_start;	/* start addr. of FDE "instructions" */
    word_t fde_instr_end;	/* end addr. of FDE "instructions" */

	uint8_t sized_augmentation : 1;
    uint8_t have_abi_marker : 1;

	dwarf_state_record_t sr;
    
    word_t ret_addr_column;

	//<<<eh_frame info end

    //exidx info >>>
    word_t exidx_table_start;
    word_t exidx_table_end;
    word_t exidx_entry;
    //<<< exidx info
    

}CFI;

typedef union __attribute__ ((packed)){
	int8_t s8;
	int16_t s16;
	int32_t s32;
	int64_t s64;
	uint8_t u8;
	uint16_t u16;
	uint32_t u32;
	uint64_t u64;
	void *ptr;
}value_t;

#endif

================================================
FILE: crashsdk/src/jni/crash/unwind/_Unwind.cpp
================================================
#include "_Unwind.h"
#include "Log.h"
#include "EHFrame.h"


#define MAX_STACK_DEEP 200

const char* Unwind::TAG = PTAG("Unwind");

Unwind::Unwind(){
	#ifdef __i386__
		mContext = new Context_x86();
	#elif defined __arm__
		mContext = new Context_arm();
	#else
		mContext = NULL;
	#endif
}

Unwind::~Unwind(){
	if(mContext != NULL){
		SAFE_DELETE(mContext);
	}
}

void Unwind::setucontext(ucontext_t *uc){
	if(mContext != NULL){
		mContext->setucontext(uc);
	}
}

int Unwind::unwind(UnwindNode** head){
	if(mContext == NULL){
		LOGE(TAG,"context is NULL, %s not support yet.", ARCH_NAME);
		return 1;
	}

	if(head == NULL){
		return 1;
	}

	LOGD(TAG,"unwind start...");

	if(mContext->reset()){
		LOGE(TAG,"reset first frame failed.");
		return 1;
	}

	UnwindNode* tail = NULL;
	uint8_t deep = 0;
	bool skiped = false;
	while(MAX_STACK_DEEP > deep){
		if(mContext->rootFrame()){
			LOGE(TAG,"end of call stack.");
			break;
		}

		UnwindNode* node = new UnwindNode();
		if((tail) != NULL){
			tail->next = node;
		}else{
			*head = node;
		}
		tail = node;
		node->next = NULL;

		node->ip = (void*)(mContext->getIP());
		node->pc = (uint32_t)node->ip;
		node->cfa = (void*)mContext->getCFA();
		if(mContext->restoreFrame() == 0){
			node->pc = (word_t)node->ip - (word_t)mContext->lastLibBase();
			node->libname = mContext->lastLibName();
			node->libbase = (void*)mContext->lastLibBase();

			LOGD(TAG,"\t#%02d pc(0x%08x) ip(%p) cfa(0x%p) %s", 
				deep, node->pc, mContext->getIP(), mContext->getCFA(), mContext->rootFrame()?"root":"");

			++deep;
			continue;
		}

		return 1;
	}

	return 0;
}

void Unwind::printNode(UnwindNode& node){
	switch(sizeof(void*)){
		case 4:
		LOGR(TAG,"\t#%02d pc 0x%08x\t%s(%s)", 0, node.pc, node.libname, node.signame);
		break;
		case 8:
		LOGR(TAG,"\t#%02d pc 0x%016lx\t%s(%s)", 0, node.pc, node.libname, node.signame);
		break;
	}
}







================================================
FILE: crashsdk/src/jni/crash/unwind/_Unwind.h
================================================
#ifndef _UNWIND_H
#define _UNWIND_H

#include "Arch.h"

typedef struct RegState{
	const char* regName;
	void* val;
}RegState;

typedef struct UnwindNode{
	uint32_t pc;
	void* ip;
	void* libbase;
	const char *libname;
	const char* signame;
	void* cfa;
	uint8_t regs_len;
	UnwindNode* next;
}UnwindNode;

#ifndef UNWIND_SUPPORT
#pragma message(ARCH_NAME "arch not support yet.")
#else
#include <link.h>
#include "DataTypes.h"
#include "Context.h"

class Unwind{

public:

	Unwind();
	~Unwind();

	void setucontext(ucontext_t *uc);
	int unwind(UnwindNode** head);

	static void printNode(UnwindNode& node);
	
private:

	static const char* TAG;

	Context* mContext;
};

#endif// ARCH_SUPPORT



#endif

================================================
FILE: crashsdk/src/jni/crash/unwind/arm/Config_arm.h
================================================
#ifndef CONFIG_arm_H
#define CONFIG_arm_H

#ifdef __arm__

#include<stdint.h>

/* This matches the value used by GCC (see
   gcc/config/i386.h:DWARF_FRAME_REGISTERS), which leaves plenty of
   room for expansion.  */
#define DWARF_NUM_PRESERVED_REGS	128

typedef uint32_t word_t;
typedef int32_t sword_t;

#endif

#endif

================================================
FILE: crashsdk/src/jni/crash/unwind/arm/Context_arm.cpp
================================================
#include "Context_arm.h"
#include "Log.h"
#include "Utils.h"
#if __ANDROID_API__ >= 21
#include "EHFrame.h"
#endif
#include <asm/sigcontext.h>

const char* Context_arm::TAG = PTAG("Context_arm");

int Context_arm::setupFrame(CFI& cfi){

	cfi.loc[UNW_ARM_R0] = getReg(UNW_ARM_R0);
	cfi.loc[UNW_ARM_R1] = getReg(UNW_ARM_R1);
	cfi.loc[UNW_ARM_R2] = getReg(UNW_ARM_R2);
	cfi.loc[UNW_ARM_R3] = getReg(UNW_ARM_R3);
	cfi.loc[UNW_ARM_R4] = getReg(UNW_ARM_R4);
	cfi.loc[UNW_ARM_R5] = getReg(UNW_ARM_R5);
	cfi.loc[UNW_ARM_R6] = getReg(UNW_ARM_R6);
	cfi.loc[UNW_ARM_R7] = getReg(UNW_ARM_R7);
	cfi.loc[UNW_ARM_R8] = getReg(UNW_ARM_R8);
	cfi.loc[UNW_ARM_R9] = getReg(UNW_ARM_R9);
	cfi.loc[UNW_ARM_R10] = getReg(UNW_ARM_R10);
	cfi.loc[UNW_ARM_R11] = getReg(UNW_ARM_R11);
	cfi.loc[UNW_ARM_R12] = getReg(UNW_ARM_R12);
	cfi.loc[UNW_ARM_R13] = getReg(UNW_ARM_R13);
	cfi.loc[UNW_ARM_R14] = getReg(UNW_ARM_R14);
	cfi.loc[UNW_ARM_R15] = getReg(UNW_ARM_R15);

	cfi.ip = cfi.loc[UNW_ARM_R15];
	cfi.cfa = cfi.loc[UNW_ARM_R13];

	for(int i=0;i<=UNW_ARM_R15;i++){
		LOGD(TAG,"r%d = 0x%lx", i, (long)cfi.loc[i]);
	}
	
	return 0;
}

word_t Context_arm::getReg(uint32_t reg){
	void* addr;

	struct sigcontext* sig_ctx = &mContext->uc_mcontext;
	switch (reg){
		case UNW_ARM_R0:	addr = &sig_ctx->arm_r0; break;
		case UNW_ARM_R1:  	addr = &sig_ctx->arm_r1; break;
		case UNW_ARM_R2:  	addr = &sig_ctx->arm_r2; break;
		case UNW_ARM_R3:  	addr = &sig_ctx->arm_r3; break;
		case UNW_ARM_R4: 	addr = &sig_ctx->arm_r4; break;
		case UNW_ARM_R5: 	addr = &sig_ctx->arm_r5; break;
		case UNW_ARM_R6:	addr = &sig_ctx->arm_r6; break;
		case UNW_ARM_R7: 	addr = &sig_ctx->arm_r7; break;
		case UNW_ARM_R8: 	addr = &sig_ctx->arm_r8; break;
		case UNW_ARM_R9: 	addr = &sig_ctx->arm_r9; break;
		case UNW_ARM_R10: 	addr = &sig_ctx->arm_r10; break;
		case UNW_ARM_R11: 	addr = &sig_ctx->arm_fp; break;
		case UNW_ARM_R12: 	addr = &sig_ctx->arm_ip; break;
		case UNW_ARM_R13:  	addr = &sig_ctx->arm_sp; break;
		case UNW_ARM_R14:  	addr = &sig_ctx->arm_lr; break;
		case UNW_ARM_R15:  	addr = &sig_ctx->arm_pc; break;

		default:
		LOGE(TAG,"unknown reg %d", reg);
		addr = NULL;
	}

	if(addr != NULL){
		return *(word_t*)addr;
	}

	return 0;
}

void Context_arm::adjustIP(word_t& ip){
	if (ip){
		int adjust = 4;
		if (ip & 1){
			/* Thumb instructions, the currently executing instruction could be
			* 2 or 4 bytes, so adjust appropriately.
			*/

			word_t addr = ip - 5;
			word_t value;
			if (ip < 5 || readW(&addr, &value) < 0 || (value & 0xe000f000) != 0xe000f000){
				adjust = 2;
			}
		}

		ip -= adjust;
	}
}

int Context_arm::restoreFrame(){
	if(rootFrame()){
		LOGE(TAG,"restoreFrame failed , it's root frame.");
		return 1;
	}

	LOGD(TAG,"restoreFrame ip(0x%lx)", (long)mCfi.ip);
	int ret = 1;

	word_t ip = mCfi.ip;
	word_t cfa = mCfi.cfa;

#if __ANDROID_API__ >= 21
	if((ret = EHFrame::restoreFrame(*this)) != 0){
		LOGE(TAG,"eh_frame parse failed.");
	}
#endif

	if(ret != 0 && (ret = Exidx::restoreFrame(*this)) != 0){
		LOGE(TAG,"restoreFrame(0x%lx) from exidx failed, try call routine.", (long)mCfi.ip);
		if((ret = stepBack(mCfi)) != 0){
			LOGE(TAG,"try call routine failed.");
			return ret;
		}
	}

	adjustIP(mCfi.ip);

	if(ip == mCfi.ip && cfa == mCfi.cfa){
		LOGE(TAG,"reach root, stop here.");
		mCfi.root = 1;
		vpnlib::printMem(cfa, cfa + (50<<2));

	}else{
		vpnlib::printMem(cfa, mCfi.cfa);
	}

	mCfi.loc[UNW_ARM_R13] = mCfi.cfa;

	return ret;

}

int Context_arm::stepBack(CFI& cfi){

	cfi.ip = cfi.loc[UNW_ARM_R15] = cfi.loc[UNW_ARM_R14]; //set pc to last frame

	LOGD(TAG,"stepBack ip(0x%lx)", cfi.ip);
	return 0;
}



================================================
FILE: crashsdk/src/jni/crash/unwind/arm/Context_arm.h
================================================
#ifndef Context_arm_h
#define Context_arm_h

#include "Context.h"
#include "Exidx.h"

typedef enum{
	UNW_ARM_R0,
	UNW_ARM_R1,
	UNW_ARM_R2,
	UNW_ARM_R3,
	UNW_ARM_R4,
	UNW_ARM_R5,
	UNW_ARM_R6,
	UNW_ARM_R7,
	UNW_ARM_R8,
	UNW_ARM_R9,
	UNW_ARM_R10,
	UNW_ARM_R11,
	UNW_ARM_R12,
	UNW_ARM_R13,
	UNW_ARM_R14,
	UNW_ARM_R15,

	UNW_TDEP_IP = UNW_ARM_R14,  /* A little white lie.  */
	UNW_TDEP_SP = UNW_ARM_R13,

}arm_regnum_t;

class Context_arm: public Context{

	friend class Exidx;

public:
	Context_arm(){
	}

	~Context_arm(){
		mContext = NULL;
	}

	virtual int setupFrame(CFI& cfi);
	virtual word_t getReg(uint32_t reg);
	virtual int restoreFrame();
	virtual int stepBack(CFI& cfi);
	void adjustIP(word_t& ip);
	
	static int callback (struct dl_phdr_info *info, size_t size, void *ptr);
	
private:

	static const char* TAG;


};

#endif

================================================
FILE: crashsdk/src/jni/crash/unwind/arm/Exidx.cpp
================================================
#include "Exidx.h"
#include "Log.h"
#include "Defines.h"
#include <link.h>

const char* Exidx::TAG = PTAG("Exidx");

int Exidx::restoreFrame(Context& context){
	CFI& cfi = context.mCfi;

	LOGD(TAG,"restoreFrame ip=0x%x",cfi.ip);

#ifdef __arm__
	word_t ip = cfi.ip;
	int pcount = 0;
	 _Unwind_Ptr exidxBase =  dl_unwind_find_exidx(ip, &pcount);
	 LOGE(TAG, "find exidx ip 0x%x , exidx base = 0x%x, pcount %d", ip, exidxBase, pcount);
	 if(exidxBase != 0){
	 	cfi.exidx_table_start = exidxBase;
	 	cfi.exidx_table_end = cfi.exidx_table_start + (pcount<<3);
	 	if(retriveEntry(context, cfi.exidx_table_start, cfi.exidx_table_end) != 0){
			LOGE(TAG, "retrive exidx entry failed.");
			return 2;
		}

		LOGD(TAG,"find exidx section success, ip(0x%x - 0x%x), exidx(0x%x - 0x%x)", 
	    		cfi.start_ip, cfi.end_ip, cfi.exidx_table_start, cfi.exidx_table_end);

		uint8_t instr[32];
		int instrLen = readUnwindInstr(context, cfi.exidx_entry, instr);
		if(instrLen <= 0){
			LOGE(TAG,"read instr failed.");
			return 1;
		}

		if(runExidxInstr(instr, instrLen, cfi) != 0){
			LOGE(TAG,"run exidx instr failed.");
			return 1;
		}

		return 0;

	 }else{
	 	LOGE(TAG, "exidxBase not found");
	 }
#else

	int ret = dl_iterate_phdr (Exidx::checklib, &context);
	if(ret == 1){

		LOGD(TAG,"find target exidx success, restore reg states...");

		uint8_t instr[32];
		int instrLen = readUnwindInstr(context, cfi.exidx_entry, instr);
		if(instrLen <= 0){
			LOGE(TAG,"read instr failed.");
			return 1;
		}

		if(runExidxInstr(instr, instrLen, cfi) != 0){
			LOGE(TAG,"run exidx instr failed.");
			return 1;
		}

		LOGD(TAG,"restoreFrame(0x%lx) success.", (long)cfi.ip);
		return 0;
	}else if(ret == 0){
		LOGE(TAG,"restoreFrame(0x%lx) failed after search all libs.", (long)cfi.ip);
	}else{
		LOGE(TAG,"restoreFrame(0x%lx) failed.", (long)cfi.ip);
	}
	
#endif

	return 1;

}

int Exidx::checklib (struct dl_phdr_info *info, size_t size, void *ptr){

	if (size < OFFSET(struct dl_phdr_info, dlpi_phnum) + sizeof (info->dlpi_phnum)){
		LOGE(TAG,"size not match while iterating libs, continue");
		return 0;
	}

	// LOGD(TAG,"checking %s (0x%x)", info->dlpi_name, info->dlpi_addr);

	Context& context = (*(Context*)ptr);
	CFI& cfi = context.mCfi;

	const ElfW(Phdr) *phdr, *p_arm_exidx, *p_text;
	ElfW(Addr) libbase;
	phdr = info->dlpi_phdr;
	libbase = info->dlpi_addr;
	p_text = NULL;
	p_arm_exidx = NULL;
	word_t ip = cfi.ip;

	for (int n = info->dlpi_phnum; --n >= 0; phdr++){
		if (phdr->p_type == PT_LOAD){
			ElfW(Addr) vaddr = phdr->p_vaddr + libbase;

			// LOGD(TAG,"(0x%x  0x%x) 0x%x",vaddr,vaddr + phdr->p_memsz, ip);

			if (ip >= vaddr && ip < vaddr + phdr->p_memsz){
				p_text = phdr;
			}

		} else if (phdr->p_type == PT_ARM_EXIDX){
			p_arm_exidx = phdr;
		}
    }

    if(p_text == NULL || p_arm_exidx == NULL){
    	return 0;
    }

    LOGD(TAG,"found ip(0x%x) fall in to lib(%s) (0x%lx)", ip, info->dlpi_name, (long)libbase);

    cfi.last_libname = info->dlpi_name;
    cfi.last_libbase = libbase;
	cfi.start_ip = p_text->p_vaddr + libbase;
	cfi.end_ip = cfi.start_ip + p_text->p_memsz;
	cfi.exidx_table_start = p_arm_exidx->p_vaddr + libbase;
	cfi.exidx_table_end = cfi.exidx_table_start + p_arm_exidx->p_memsz;

	if(retriveEntry(context, cfi.exidx_table_start, cfi.exidx_table_end) != 0){
		LOGE(TAG, "retrive exidx entry failed.");
		return 2;
	}

	LOGD(TAG,"find exidx section success, ip(0x%x - 0x%x), exidx(0x%x - 0x%x)", 
    		cfi.start_ip, cfi.end_ip, cfi.exidx_table_start, cfi.exidx_table_end);

	return 1;
}

int Exidx::retriveEntry(Context& context, word_t s, word_t e){
	/* The .ARM.exidx section contains a sorted list of key-value pairs -
	the unwind entries.  The 'key' is a prel31 offset to the start of a
	function.  We binary search this section in order to find the
	appropriate unwind entry.  */
	word_t first = s;
	word_t last = e - 8;
	word_t entry, val;
	CFI& cfi = context.mCfi;
	word_t ip = cfi.ip;

	if (prel31_to_addr (first, &val) < 0 || ip < val){
		LOGE(TAG,"init first failed or ip < val");
		return -1;
	}

	if (prel31_to_addr (last, &val) < 0){
		LOGE(TAG,"init last failed");
		return -1;
	}

	if (ip >= val){
		LOGE(TAG,"ip exceed exidx range.");
		entry = last;
		if (prel31_to_addr (last, &cfi.start_ip) < 0){
			return -1;
		}
		--cfi.end_ip;
	} else {
		while (first < last - 8){
			entry = first + (((last - first) / 8 + 1) >> 1) * 8;

			if (prel31_to_addr (entry, &val) < 0){
				return -1;
			}

			if (ip < val){
				last = entry;
			} else{
				first = entry;
			}
		}

		entry = first;

		if (prel31_to_addr (entry, &cfi.start_ip) < 0
			|| prel31_to_addr (entry + 8, &cfi.end_ip) < 0){
			return -1;
		}

		--cfi.end_ip;
	}

	cfi.exidx_entry = entry;

	return 0;
}

int Exidx::readUnwindInstr(Context& context, word_t entry, uint8_t* instr){
	int nbuf = 0;
	word_t addr, temp;
	uint32_t data;

	/* An ARM unwind entry consists of a prel31 offset to the start of a
	function followed by 31bits of data: 
	* if set to 0x1: the function cannot be unwound (EXIDX_CANTUNWIND)
	* if bit 31 is one: this is a table entry itself (ARM_EXIDX_COMPACT)
	* if bit 31 is zero: this is a prel31 offset of the start of the
	table entry for this function  */
	if (prel31_to_addr(entry, &addr) < 0){
		return -1;
	}

	temp = entry+4;
	if (context.readU32(&temp, &data) < 0){
		return -1;
	}

	if (data == ARM_EXIDX_CANT_UNWIND) {
		LOGE (TAG, "ARM_EXIDX_CANT_UNWIND(%x)", data);
		nbuf = -1;
	} else if (data & ARM_EXIDX_COMPACT){
		LOGD (TAG, "%p compact model %d [0x%x]", (void *)addr, (data >> 24) & 0x7f, data);
		instr[nbuf++] = data >> 16;
		instr[nbuf++] = data >> 8;
		instr[nbuf++] = data;
	} else {
		word_t extbl_data;
		uint8_t n_table_words = 0;

		if (prel31_to_addr(entry + 4, &extbl_data) < 0){
			return -1;
		}

		temp = extbl_data;
		if (context.readU32(&temp, &data) < 0){
			return -1;
		}

		if (data & ARM_EXIDX_COMPACT){
			int pers = (data >> 24) & 0x0f;
			LOGD (TAG, "%p compact model %d [%8.8x]", (void *)addr, pers, data);
			if (pers == 1 || pers == 2) {
				n_table_words = (data >> 16) & 0xff;
				extbl_data += 4;
			}else{
				instr[nbuf++] = data >> 16;
			}

			instr[nbuf++] = data >> 8;
			instr[nbuf++] = data;
		} else {
			word_t pers;
			if (prel31_to_addr (extbl_data, &pers) < 0){
				return -1;
			}

			LOGD (TAG, "%p Personality routine: %8p", (void *)addr, (void *)pers);

			temp = extbl_data+4;
			if (context.readU32(&temp, &data) < 0){
				return -1;
			}

			n_table_words = data >> 24;
			instr[nbuf++] = data >> 16;
			instr[nbuf++] = data >> 8;
			instr[nbuf++] = data;
			extbl_data += 8;
		}

		if(n_table_words > 5){
			LOGE(TAG,"table too large %u", n_table_words);
			return -1;
		}

		for (uint8_t j = 0; j < n_table_words; j++){
			temp = extbl_data;
			if (context.readU32(&temp, &data) < 0){
				return -1;
			}

			instr[nbuf++] = data >> 24;
			instr[nbuf++] = data >> 16;
			instr[nbuf++] = data >> 8;
			instr[nbuf++] = data >> 0;
		}
	}

	if (nbuf > 0 && instr[nbuf - 1] != ARM_EXTBL_OP_FINISH){
		instr[nbuf++] = ARM_EXTBL_OP_FINISH;
	}

	return nbuf;
}

int Exidx::runExidxInstr(const uint8_t* instr, uint8_t len, CFI& cfi){
	LOGD(TAG,"runExidxInstr instr[%p +%u]", instr, len);
	if(instr == NULL || len <= 0){
		return 0;
	}

	#define READ_OP() *instr++
	const uint8_t *end = instr + len;
	int ret;
	arm_exbuf_data edata;

	while (instr < end){
		uint8_t op = READ_OP ();
		if ((op & 0xc0) == 0x00) {
			edata.cmd = ARM_EXIDX_CMD_DATA_POP;
			edata.data = (((int)op & 0x3f) << 2) + 4;
		} else if ((op & 0xc0) == 0x40){
			edata.cmd = ARM_EXIDX_CMD_DATA_PUSH;
			edata.data = (((int)op & 0x3f) << 2) + 4;
		} else if ((op & 0xf0) == 0x80) {
			uint8_t op2 = READ_OP ();
			if (op == 0x80 && op2 == 0x00){
				edata.cmd = ARM_EXIDX_CMD_REFUSED;
			} else {
				edata.cmd = ARM_EXIDX_CMD_REG_POP;
				edata.data = ((op & 0xf) << 8) | op2;
				edata.data = edata.data << 4;
			}
		} else if ((op & 0xf0) == 0x90) {
			if (op == 0x9d || op == 0x9f){
				edata.cmd = ARM_EXIDX_CMD_RESERVED;
			} else {
				edata.cmd = ARM_EXIDX_CMD_REG_TO_SP;
				edata.data = op & 0x0f;
			}
		} else if ((op & 0xf0) == 0xa0) {
			unsigned end = (op & 0x07);
			edata.data = (1 << (end + 1)) - 1;
			edata.data = edata.data << 4;
			if (op & 0x08)
			edata.data |= 1 << 14;
			edata.cmd = ARM_EXIDX_CMD_REG_POP;
		} else if (op == ARM_EXTBL_OP_FINISH) {
			edata.cmd = ARM_EXIDX_CMD_FINISH;
			instr = end;
		} else if (op == 0xb1) {
			uint8_t op2 = READ_OP ();
			if (op2 == 0 || (op2 & 0xf0)){
				edata.cmd = ARM_EXIDX_CMD_RESERVED;
			} else {
				edata.cmd = ARM_EXIDX_CMD_REG_POP;
				edata.data = op2 & 0x0f;
			}
		} else if (op == 0xb2) {
			uint32_t offset = 0;
			uint8_t byte, shift = 0;
			do{
				byte = READ_OP ();
				offset |= (byte & 0x7f) << shift;
				shift += 7;
			}while (byte & 0x80);
			edata.data = offset * 4 + 0x204;
			edata.cmd = ARM_EXIDX_CMD_DATA_POP;
		}else if (op == 0xb3 || op == 0xc8 || op == 0xc9) {
			edata.cmd = ARM_EXIDX_CMD_VFP_POP;
			edata.data = READ_OP ();
			if (op == 0xc8){
				edata.data |= ARM_EXIDX_VFP_SHIFT_16;
			}
			if (op != 0xb3){
				edata.data |= ARM_EXIDX_VFP_DOUBLE;
			}
		} else if ((op & 0xf8) == 0xb8 || (op & 0xf8) == 0xd0){
			edata.cmd = ARM_EXIDX_CMD_VFP_POP;
			edata.data = 0x80 | (op & 0x07);
			if ((op & 0xf8) == 0xd0){
				edata.data |= ARM_EXIDX_VFP_DOUBLE;
			}
		} else if (op >= 0xc0 && op <= 0xc5) {
			edata.cmd = ARM_EXIDX_CMD_WREG_POP;
			edata.data = 0xa0 | (op & 0x07);
		} else if (op == 0xc6) {
			edata.cmd = ARM_EXIDX_CMD_WREG_POP;
			edata.data = READ_OP ();
		} else if (op == 0xc7) {
			uint8_t op2 = READ_OP ();
			if (op2 == 0 || (op2 & 0xf0)){
				edata.cmd = ARM_EXIDX_CMD_RESERVED;
			} else {
				edata.cmd = ARM_EXIDX_CMD_WCGR_POP;
				edata.data = op2 & 0x0f;
			}
		} else{
			edata.cmd = ARM_EXIDX_CMD_RESERVED;
		}

		if ((ret = applyInstr(edata, cfi)) != 0){
			return ret;
		}
	}

	return 0;
}

int Exidx::applyInstr(arm_exbuf_data& edata, CFI& cfi){
	int ret = 0;
	unsigned i;

	LOGD(TAG,"applyInstr cmd(%d)", edata.cmd);
	switch (edata.cmd){
		case ARM_EXIDX_CMD_FINISH:
			/* Set LR to PC if not set already.  */
			// if (cfi.loc[UNW_ARM_R15] == 0){
				LOGD(TAG,"set r15(0x%lx) to r14(0x%lx)", cfi.loc[UNW_ARM_R15], cfi.loc[UNW_ARM_R14]);
				cfi.loc[UNW_ARM_R15] = cfi.loc[UNW_ARM_R14];
			// }

			LOGD(TAG,"set ip(0x%lx) to 0x%lx", cfi.ip, cfi.loc[UNW_ARM_R15]);
			/* Set IP.  */
			cfi.ip = cfi.loc[UNW_ARM_R15];
			break;
		case ARM_EXIDX_CMD_DATA_PUSH:
			LOGD (TAG, "cfa(0x%lx) - %d", cfi.cfa, edata.data);
			cfi.cfa -= edata.data;
			break;
		case ARM_EXIDX_CMD_DATA_POP:
			LOGD (TAG, "cfa(0x%lx) + %d", cfi.cfa, edata.data);
			cfi.cfa += edata.data;
			break;
		case ARM_EXIDX_CMD_REG_POP:
			for (i = 0; i < 16; i++) if (edata.data & (1 << i)) {
				LOGD(TAG, "pop {r%d}, 0x%lx", i, *(word_t*)cfi.cfa);
				cfi.loc[UNW_ARM_R0 + i] = *(word_t*)cfi.cfa;
				cfi.cfa += 4;
			}
			/* Set cfa in case the SP got popped. */
			if (edata.data & (1 << 13)){
				LOGD(TAG,"set cfa(0x%lx) = 0x%lx", cfi.cfa, cfi.loc[UNW_ARM_R13]);
				cfi.cfa = cfi.loc[UNW_ARM_R13];
			}
			break;
		case ARM_EXIDX_CMD_REG_TO_SP:
			if(edata.data >= 16){
				LOGE(TAG,"register indx must in [0,15], %d", edata.data);
				return -1;
			}

			LOGD(TAG, "cfa = r%d 0x%lx", edata.data, cfi.cfa);
			cfi.loc[UNW_ARM_R13] = cfi.loc[UNW_ARM_R0 + edata.data];
			cfi.cfa = cfi.loc[UNW_ARM_R13];
			break;
		case ARM_EXIDX_CMD_VFP_POP:
			/* Skip VFP registers, but be sure to adjust stack */
			for (i = ARM_EXBUF_START (edata.data); i <= ARM_EXBUF_END (edata.data); i++){
				LOGD(TAG,"pop, cfa += 8");
				cfi.cfa += 8;
			}
			if (!(edata.data & ARM_EXIDX_VFP_DOUBLE)){
				LOGD(TAG,"pop, cfa += 4");
				cfi.cfa += 4;
			}
			break;
		case ARM_EXIDX_CMD_WREG_POP:
			for (i = ARM_EXBUF_START (edata.data); i <= ARM_EXBUF_END (edata.data); i++){
				LOGD(TAG,"pop, cfa += 8");
				cfi.cfa += 8;
			}
			break;
		case ARM_EXIDX_CMD_WCGR_POP:
			for (i = 0; i < 4; i++) if (edata.data & (1 << i)){
				LOGD(TAG,"pop, cfa += 4");
				cfi.cfa += 4;
			}
			break;
		case ARM_EXIDX_CMD_REFUSED:
		case ARM_EXIDX_CMD_RESERVED:
			ret = -1;
			break;
	}

	return ret;
}

int Exidx::prel31_to_addr(word_t prel31, word_t *val){
	word_t offset = *(word_t*)prel31;
	offset = ((long)offset << 1) >> 1;
	*val = prel31 + offset;

	return 0;
}


================================================
FILE: crashsdk/src/jni/crash/unwind/arm/Exidx.h
================================================
#ifndef Exidx_h
#define Exidx_h

#include "Context.h"

#define ARM_EXBUF_START(x)	(((x) >> 4) & 0x0f)
#define ARM_EXBUF_COUNT(x)	((x) & 0x0f)
#define ARM_EXBUF_END(x)	(ARM_EXBUF_START(x) + ARM_EXBUF_COUNT(x))

#define ARM_EXIDX_CANT_UNWIND	0x00000001
#define ARM_EXIDX_COMPACT	0x80000000

#define ARM_EXTBL_OP_FINISH	0xb0

enum arm_exbuf_cmd_flags {
  ARM_EXIDX_VFP_SHIFT_16 = 1 << 16,
  ARM_EXIDX_VFP_DOUBLE = 1 << 17,
};

typedef enum arm_exbuf_cmd {
	ARM_EXIDX_CMD_FINISH,
	ARM_EXIDX_CMD_DATA_PUSH,
	ARM_EXIDX_CMD_DATA_POP,
	ARM_EXIDX_CMD_REG_POP,
	ARM_EXIDX_CMD_REG_TO_SP,
	ARM_EXIDX_CMD_VFP_POP,
	ARM_EXIDX_CMD_WREG_POP,
	ARM_EXIDX_CMD_WCGR_POP,
	ARM_EXIDX_CMD_RESERVED,
	ARM_EXIDX_CMD_REFUSED,
} arm_exbuf_cmd_t;

typedef struct arm_exbuf_data{
	arm_exbuf_cmd_t cmd;
	uint32_t data;
}arm_exbuf_data;

class Exidx{

public:

	static int restoreFrame(Context& context);
	static int checklib (struct dl_phdr_info *info, size_t size, void *ptr);
	static int retriveEntry(Context& context, word_t s, word_t e);
	static int readUnwindInstr(Context& context, word_t entry, uint8_t* instr);
	static int runExidxInstr(const uint8_t* instr, uint8_t len, CFI& cfi);
	static int prel31_to_addr(word_t prel31, word_t *val);

private:
	static const char* TAG;

	static int applyInstr(arm_exbuf_data& edata, CFI& cfi);

};

#endif

================================================
FILE: crashsdk/src/jni/crash/unwind/eh_frame/DWExpression.cpp
================================================
#include "DWExpression.h"
#include "Defines.h"
#include "Log.h"

/* The "pick" operator provides an index range of 0..255 indicating
   that the stack could at least have a depth of up to 256 elements,
   but the GCC unwinder restricts the depth to 64, which seems
   reasonable so we use the same value here.  */
#define MAX_EXPR_STACK_SIZE   64

#define NUM_OPERANDS(signature)  (((signature) >> 6) & 0x3)
#define OPND1_TYPE(signature) (((signature) >> 3) & 0x7)
#define OPND2_TYPE(signature) (((signature) >> 0) & 0x7)

#define OPND_SIGNATURE(n, t1, t2) (((n) << 6) | ((t1) << 3) | ((t2) << 0))
#define OPND1(t1)    OPND_SIGNATURE(1, t1, 0)
#define OPND2(t1, t2)      OPND_SIGNATURE(2, t1, t2)

#define VAL8   0x0
#define VAL16  0x1
#define VAL32  0x2
#define VAL64  0x3
#define ULEB128   0x4
#define SLEB128   0x5
#define OFFSET_VAL 0x6   /* 32-bit offset for 32-bit DWARF, 64-bit otherwise */
#define ADDR   0x7   /* Machine address.  */

typedef enum {
   DW_OP_addr       = 0x03,
   DW_OP_deref         = 0x06,
   DW_OP_const1u    = 0x08,
   DW_OP_const1s    = 0x09,
   DW_OP_const2u    = 0x0a,
   DW_OP_const2s    = 0x0b,
   DW_OP_const4u    = 0x0c,
   DW_OP_const4s    = 0x0d,
   DW_OP_const8u    = 0x0e,
   DW_OP_const8s    = 0x0f,
   DW_OP_constu     = 0x10,
   DW_OP_consts     = 0x11,
   DW_OP_dup        = 0x12,
   DW_OP_drop       = 0x13,
   DW_OP_over       = 0x14,
   DW_OP_pick       = 0x15,
   DW_OP_swap       = 0x16,
   DW_OP_rot        = 0x17,
   DW_OP_xderef     = 0x18,
   DW_OP_abs        = 0x19,
   DW_OP_and        = 0x1a,
   DW_OP_div        = 0x1b,
   DW_OP_minus         = 0x1c,
   DW_OP_mod        = 0x1d,
   DW_OP_mul        = 0x1e,
   DW_OP_neg        = 0x1f,
   DW_OP_not        = 0x20,
   DW_OP_or         = 0x21,
   DW_OP_plus       = 0x22,
   DW_OP_plus_uconst      = 0x23,
   DW_OP_shl        = 0x24,
   DW_OP_shr        = 0x25,
   DW_OP_shra       = 0x26,
   DW_OP_xor        = 0x27,
   DW_OP_skip       = 0x2f,
   DW_OP_bra        = 0x28,
   DW_OP_eq         = 0x29,
   DW_OP_ge         = 0x2a,
   DW_OP_gt         = 0x2b,
   DW_OP_le         = 0x2c,
   DW_OP_lt         = 0x2d,
   DW_OP_ne         = 0x2e,
   DW_OP_lit0       = 0x30,
   DW_OP_lit1,  DW_OP_lit2,  DW_OP_lit3,  DW_OP_lit4,  DW_OP_lit5,
   DW_OP_lit6,  DW_OP_lit7,  DW_OP_lit8,  DW_OP_lit9,  DW_OP_lit10,
   DW_OP_lit11, DW_OP_lit12, DW_OP_lit13, DW_OP_lit14, DW_OP_lit15,
   DW_OP_lit16, DW_OP_lit17, DW_OP_lit18, DW_OP_lit19, DW_OP_lit20,
   DW_OP_lit21, DW_OP_lit22, DW_OP_lit23, DW_OP_lit24, DW_OP_lit25,
   DW_OP_lit26, DW_OP_lit27, DW_OP_lit28, DW_OP_lit29, DW_OP_lit30,
   DW_OP_lit31,
   DW_OP_reg0       = 0x50,
   DW_OP_reg1,  DW_OP_reg2,  DW_OP_reg3,  DW_OP_reg4,  DW_OP_reg5,
   DW_OP_reg6,  DW_OP_reg7,  DW_OP_reg8,  DW_OP_reg9,  DW_OP_reg10,
   DW_OP_reg11, DW_OP_reg12, DW_OP_reg13, DW_OP_reg14, DW_OP_reg15,
   DW_OP_reg16, DW_OP_reg17, DW_OP_reg18, DW_OP_reg19, DW_OP_reg20,
   DW_OP_reg21, DW_OP_reg22, DW_OP_reg23, DW_OP_reg24, DW_OP_reg25,
   DW_OP_reg26, DW_OP_reg27, DW_OP_reg28, DW_OP_reg29, DW_OP_reg30,
   DW_OP_reg31,
   DW_OP_breg0         = 0x70,
   DW_OP_breg1,  DW_OP_breg2,  DW_OP_breg3,  DW_OP_breg4,  DW_OP_breg5,
   DW_OP_breg6,  DW_OP_breg7,  DW_OP_breg8,  DW_OP_breg9,  DW_OP_breg10,
   DW_OP_breg11, DW_OP_breg12, DW_OP_breg13, DW_OP_breg14, DW_OP_breg15,
   DW_OP_breg16, DW_OP_breg17, DW_OP_breg18, DW_OP_breg19, DW_OP_breg20,
   DW_OP_breg21, DW_OP_breg22, DW_OP_breg23, DW_OP_breg24, DW_OP_breg25,
   DW_OP_breg26, DW_OP_breg27, DW_OP_breg28, DW_OP_breg29, DW_OP_breg30,
   DW_OP_breg31,
   DW_OP_regx       = 0x90,
   DW_OP_fbreg         = 0x91,
   DW_OP_bregx         = 0x92,
   DW_OP_piece         = 0x93,
   DW_OP_deref_size    = 0x94,
   DW_OP_xderef_size      = 0x95,
   DW_OP_nop        = 0x96,
   DW_OP_push_object_address = 0x97,
   DW_OP_call2         = 0x98,
   DW_OP_call4         = 0x99,
   DW_OP_call_ref      = 0x9a,
   DW_OP_lo_user    = 0xe0,
   DW_OP_hi_user    = 0xff
}dwarf_expr_op_t;

const uint8_t DWExpression::OPERANDS[256] = {
   [DW_OP_addr] =    OPND1 (ADDR),
   [DW_OP_const1u] =    OPND1 (VAL8),
   [DW_OP_const1s] =    OPND1 (VAL8),
   [DW_OP_const2u] =    OPND1 (VAL16),
   [DW_OP_const2s] =    OPND1 (VAL16),
   [DW_OP_const4u] =    OPND1 (VAL32),
   [DW_OP_const4s] =    OPND1 (VAL32),
   [DW_OP_const8u] =    OPND1 (VAL64),
   [DW_OP_const8s] =    OPND1 (VAL64),
   [DW_OP_pick] =    OPND1 (VAL8),
   [DW_OP_plus_uconst] =   OPND1 (ULEB128),
   [DW_OP_skip] =    OPND1 (VAL16),
   [DW_OP_bra] =     OPND1 (VAL16),
   [DW_OP_breg0 +  0] = OPND1 (SLEB128),
   [DW_OP_breg0 +  1] = OPND1 (SLEB128),
   [DW_OP_breg0 +  2] = OPND1 (SLEB128),
   [DW_OP_breg0 +  3] = OPND1 (SLEB128),
   [DW_OP_breg0 +  4] = OPND1 (SLEB128),
   [DW_OP_breg0 +  5] = OPND1 (SLEB128),
   [DW_OP_breg0 +  6] = OPND1 (SLEB128),
   [DW_OP_breg0 +  7] = OPND1 (SLEB128),
   [DW_OP_breg0 +  8] = OPND1 (SLEB128),
   [DW_OP_breg0 +  9] = OPND1 (SLEB128),
   [DW_OP_breg0 + 10] = OPND1 (SLEB128),
   [DW_OP_breg0 + 11] = OPND1 (SLEB128),
   [DW_OP_breg0 + 12] = OPND1 (SLEB128),
   [DW_OP_breg0 + 13] = OPND1 (SLEB128),
   [DW_OP_breg0 + 14] = OPND1 (SLEB128),
   [DW_OP_breg0 + 15] = OPND1 (SLEB128),
   [DW_OP_breg0 + 16] = OPND1 (SLEB128),
   [DW_OP_breg0 + 17] = OPND1 (SLEB128),
   [DW_OP_breg0 + 18] = OPND1 (SLEB128),
   [DW_OP_breg0 + 19] = OPND1 (SLEB128),
   [DW_OP_breg0 + 20] = OPND1 (SLEB128),
   [DW_OP_breg0 + 21] = OPND1 (SLEB128),
   [DW_OP_breg0 + 22] = OPND1 (SLEB128),
   [DW_OP_breg0 + 23] = OPND1 (SLEB128),
   [DW_OP_breg0 + 24] = OPND1 (SLEB128),
   [DW_OP_breg0 + 25] = OPND1 (SLEB128),
   [DW_OP_breg0 + 26] = OPND1 (SLEB128),
   [DW_OP_breg0 + 27] = OPND1 (SLEB128),
   [DW_OP_breg0 + 28] = OPND1 (SLEB128),
   [DW_OP_breg0 + 29] = OPND1 (SLEB128),
   [DW_OP_breg0 + 30] = OPND1 (SLEB128),
   [DW_OP_breg0 + 31] = OPND1 (SLEB128),
   [DW_OP_regx] =    OPND1 (ULEB128),
   [DW_OP_fbreg] =      OPND1 (SLEB128),
   [DW_OP_bregx] =      OPND2 (ULEB128, SLEB128),
   [DW_OP_piece] =      OPND1 (ULEB128),
   [DW_OP_deref_size] = OPND1 (VAL8),
   [DW_OP_xderef_size] =   OPND1 (VAL8),
   [DW_OP_call2] =      OPND1 (VAL16),
   [DW_OP_call4] =      OPND1 (VAL32),
   [DW_OP_call_ref] =      OPND1 (OFFSET_VAL)
};

const char* DWExpression::TAG = PTAG("DWExpression");

int DWExpression::evaluate(Context& context, CFI& cfi, word_t* addr, word_t& val, int& is_register){

	int ret;
	word_t len;

	/* read the length of the expression: */
	if ((ret = context.readUleb128(addr, &len)) < 0){
		return ret;
	}

	if((ret = evaluate(context, cfi, addr, len, val, is_register)) != 0){
		LOGE(TAG,"evaluate expression val failed");
		return ret;
	}

	LOGD(TAG,"evaluate DWExpression result = 0x%lx",(long)val);

	if (is_register){
		if(val >= DWARF_NUM_PRESERVED_REGS){
			LOGE(TAG,"regNum out of range.");
			return -1;
		}

		val = cfi.loc[val];
	}else{
		val = *(word_t*)val;
	}

	return 0;
}


int DWExpression::evaluate(Context& context, CFI& cfi, word_t* addr, word_t len, word_t& val, int& is_register){

	LOGD(TAG,"evaluate DWExpression %u",len);
	
	word_t operand1 = 0, operand2 = 0, tmp1, tmp2, tmp3, end_addr;
	sword_t stmp1,stmp2;
	uint8_t opcode, operands_signature, u8;
	word_t stack[MAX_EXPR_STACK_SIZE];
	unsigned int tos = 0;
	uint16_t u16;
	uint32_t u32;
	uint64_t u64;
	int ret, regNum;


	# define pop() \
		({\
			if ((tos - 1) >= MAX_EXPR_STACK_SIZE){\
				LOGE (TAG, "Stack underflow");\
				return -1;\
			}\
			stack[--tos];\
		})

	# define push(x) \
		do{\
			if (tos >= MAX_EXPR_STACK_SIZE){\
				LOGE (TAG, "Stack overflow");\
				return -1;\
			}\
			stack[tos++] = (x);\
		} while (0)

	# define pick(n)\
	({\
		unsigned int _index = tos - 1 - (n);\
		if (_index >= MAX_EXPR_STACK_SIZE){\
			LOGE (TAG, "Out-of-stack pick");\
			return -1;\
		}\
		stack[_index];\
	})

	end_addr = *addr + len;

	LOGD (TAG, "len=%lu, pushing cfa=0x%lx",(unsigned long) len, (unsigned long) cfi.cfa);

	push (cfi.cfa);	/* push current CFA as required by DWARF spec */

	while (*addr < end_addr){
		if ((ret = context.readU8(addr,&opcode)) < 0){
			return ret;
		}

		operands_signature = OPERANDS[opcode];

		if (NUM_OPERANDS (operands_signature) > 0){
			if ((ret = read_operand (context, addr, OPND1_TYPE (operands_signature),&operand1)) < 0){
				return ret;
			}

			if (NUM_OPERANDS (operands_signature) > 1){
				if ((ret = read_operand (context, addr,OPND2_TYPE (operands_signature),&operand2)) < 0){
					return ret;
				}
			}
		}

		switch ((dwarf_expr_op_t) opcode){
			case DW_OP_lit0:  case DW_OP_lit1:  case DW_OP_lit2:
			case DW_OP_lit3:  case DW_OP_lit4:  case DW_OP_lit5:
			case DW_OP_lit6:  case DW_OP_lit7:  case DW_OP_lit8:
			case DW_OP_lit9:  case DW_OP_lit10: case DW_OP_lit11:
			case DW_OP_lit12: case DW_OP_lit13: case DW_OP_lit14:
			case DW_OP_lit15: case DW_OP_lit16: case DW_OP_lit17:
			case DW_OP_lit18: case DW_OP_lit19: case DW_OP_lit20:
			case DW_OP_lit21: case DW_OP_lit22: case DW_OP_lit23:
			case DW_OP_lit24: case DW_OP_lit25: case DW_OP_lit26:
			case DW_OP_lit27: case DW_OP_lit28: case DW_OP_lit29:
			case DW_OP_lit30: case DW_OP_lit31:
				LOGD (TAG, "OP_lit(%d)", (int) opcode - DW_OP_lit0);
				push (opcode - DW_OP_lit0);
				break;

			case DW_OP_breg0:  case DW_OP_breg1:  case DW_OP_breg2:
			case DW_OP_breg3:  case DW_OP_breg4:  case DW_OP_breg5:
			case DW_OP_breg6:  case DW_OP_breg7:  case DW_OP_breg8:
			case DW_OP_breg9:  case DW_OP_breg10: case DW_OP_breg11:
			case DW_OP_breg12: case DW_OP_breg13: case DW_OP_breg14:
			case DW_OP_breg15: case DW_OP_breg16: case DW_OP_breg17:
			case DW_OP_breg18: case DW_OP_breg19: case DW_OP_breg20:
			case DW_OP_breg21: case DW_OP_breg22: case DW_OP_breg23:
			case DW_OP_breg24: case DW_OP_breg25: case DW_OP_breg26:
			case DW_OP_breg27: case DW_OP_breg28: case DW_OP_breg29:
			case DW_OP_breg30: case DW_OP_breg31:
				regNum = opcode - DW_OP_breg0;
				LOGD (TAG, "OP_breg(r%d,0x%lx)",regNum, (unsigned long) operand1);
				if(0 > regNum || regNum >= DWARF_NUM_PRESERVED_REGS){
					LOGE(TAG,"regNum out of range");
					return -1;
				}

				tmp1 = cfi.loc[regNum];
				push (tmp1 + operand1);
				break;

			case DW_OP_bregx:
				regNum = (int)operand1;
				LOGD (TAG, "OP_bregx(r%d,0x%lx)", regNum, (unsigned long) operand2);
				
				if(0 > regNum || regNum >= DWARF_NUM_PRESERVED_REGS){
					LOGE(TAG,"regNum out of range");
					return -1;
				}

				tmp1 = cfi.loc[regNum];
				push (tmp1 + operand2);
				break;

			case DW_OP_reg0:  case DW_OP_reg1:  case DW_OP_reg2:
			case DW_OP_reg3:  case DW_OP_reg4:  case DW_OP_reg5:
			case DW_OP_reg6:  case DW_OP_reg7:  case DW_OP_reg8:
			case DW_OP_reg9:  case DW_OP_reg10: case DW_OP_reg11:
			case DW_OP_reg12: case DW_OP_reg13: case DW_OP_reg14:
			case DW_OP_reg15: case DW_OP_reg16: case DW_OP_reg17:
			case DW_OP_reg18: case DW_OP_reg19: case DW_OP_reg20:
			case DW_OP_reg21: case DW_OP_reg22: case DW_OP_reg23:
			case DW_OP_reg24: case DW_OP_reg25: case DW_OP_reg26:
			case DW_OP_reg27: case DW_OP_reg28: case DW_OP_reg29:
			case DW_OP_reg30: case DW_OP_reg31:
				LOGD (TAG, "OP_reg(r%d)", (int) opcode - DW_OP_reg0);
				val = (opcode - DW_OP_reg0);
				is_register = 1;
				return 0;

			case DW_OP_regx:
				LOGD (TAG, "OP_regx(r%d)", (int) operand1);
				val = (operand1);
				is_register = 1;
				return 0;

			case DW_OP_addr:
			case DW_OP_const1u:
			case DW_OP_const2u:
			case DW_OP_const4u:
			case DW_OP_const8u:
			case DW_OP_constu:
			case DW_OP_const8s:
			case DW_OP_consts:
				LOGD (TAG, "OP_const(0x%lx)", (unsigned long) operand1);
				push (operand1);
				break;

			case DW_OP_const1s:
				if (operand1 & 0x80){
					operand1 |= ((word_t) -1) << 8;
				}

				LOGD (TAG, "OP_const1s(%ld)", (long) operand1);
				push (operand1);
				break;

			case DW_OP_const2s:
				if (operand1 & 0x8000){
					operand1 |= ((word_t) -1) << 16;
				}
				LOGD (TAG, "OP_const2s(%ld)", (long) operand1);
				push (operand1);
				break;

			case DW_OP_const4s:
				if (operand1 & 0x80000000){
					operand1 |= (((word_t) -1) << 16) << 16;
				}
				LOGD(TAG, "OP_const4s(%ld)", (long) operand1);
				push (operand1);
				break;

			case DW_OP_deref:
				LOGD(TAG, "OP_deref");
				tmp1 = pop ();
				if ((ret = context.readW (&tmp1, &tmp2)) < 0){
					return ret;
				}
				push (tmp2);
				break;

			case DW_OP_deref_size:
				LOGD(TAG, "OP_deref_size(%d)", (int) operand1);
				tmp1 = pop ();
				switch (operand1){
					case 1:
						if ((ret = context.readU8 (&tmp1, &u8)) < 0){
							return ret;
						}
						tmp2 = u8;
						break;

					case 2:
						if ((ret = context.readU16 (&tmp1, &u16)) < 0){
							return ret;
						}
						tmp2 = u16;
						break;

					case 3:
					case 4:
						if ((ret = context.readU32 (&tmp1, &u32)) < 0){
							return ret;
						}
						tmp2 = u32;
						if (operand1 == 3){
							if (context.isBigEndian()){
								tmp2 >>= 8;
							}else{
								tmp2 &= 0xffffff;
							}
						}
						break;
					case 5:
					case 6:
					case 7:
					case 8:
						if ((ret = context.readU64 (&tmp1, &u64)) < 0){
							return ret;
						}
						tmp2 = u64;
						if (operand1 != 8){
							if (context.isBigEndian()){
								tmp2 >>= 64 - 8 * operand1;
							} else{
								tmp2 &= (~ (word_t) 0) << (8 * operand1);
							}
						}
						break;

					default: 
							LOGD (TAG, "Unexpected DW_OP_deref_size size %d", (int) operand1);
							return -1;
				}

				push (tmp2);
				break;

			case DW_OP_dup:
				LOGD(TAG, "OP_dup");
				push (pick (0));
				break;

			case DW_OP_drop:
				LOGD(TAG, "OP_drop");
				(void) pop ();
				break;

			case DW_OP_pick:
				LOGD(TAG, "OP_pick(%d)", (int) operand1);
				push (pick (operand1));
				break;

			case DW_OP_over:
				LOGD(TAG, "OP_over");
				push (pick (1));
				break;

			case DW_OP_swap:
				LOGD(TAG, "OP_swap");
				tmp1 = pop ();
				tmp2 = pop ();
				push (tmp1);
				push (tmp2);
				break;

			case DW_OP_rot:
				LOGD(TAG, "OP_rot");
				tmp1 = pop ();
				tmp2 = pop ();
				tmp3 = pop ();
				push (tmp1);
				push (tmp3);
				push (tmp2);
				break;

			case DW_OP_abs:
				LOGD(TAG, "OP_abs");
				tmp1 = pop ();
				if (tmp1 & ((word_t) 1 << (8 * context.getAddrWidth() - 1))){
					tmp1 = -tmp1;
				}
				push (tmp1);
				break;

			case DW_OP_and:
				LOGD(TAG, "OP_and");
				tmp1 = pop ();
				tmp2 = pop ();
				push (tmp1 & tmp2);
				break;

			case DW_OP_div:
				LOGD(TAG, "OP_div");
				tmp1 = pop ();
				tmp2 = pop ();
				if (tmp1){
					if(context.sword(tmp2,stmp2) == 0 && context.sword(tmp1,stmp1) == 0){
						tmp1 = stmp2/stmp1;
					}else{
						LOGE(TAG,"change word_t to sword_t failed.");
						return -1;
					}
				}
				push (tmp1);
				break;

			case DW_OP_minus:
				LOGD(TAG, "OP_minus");
				tmp1 = pop ();
				tmp2 = pop ();
				tmp1 = tmp2 - tmp1;
				push (tmp1);
				break;

			case DW_OP_mod:
				LOGD(TAG, "OP_mod");
				tmp1 = pop ();
				tmp2 = pop ();
				if (tmp1){
					tmp1 = tmp2 % tmp1;
				}
				push (tmp1);
				break;

			case DW_OP_mul:
				LOGD(TAG, "OP_mul");
				tmp1 = pop ();
				tmp2 = pop ();
				if (tmp1){
					tmp1 = tmp2 * tmp1;
				}
				push (tmp1);
				break;

			case DW_OP_neg:
				LOGD(TAG, "OP_neg");
				push (-pop ());
				break;

			case DW_OP_not:
				LOGD(TAG, "OP_not");
				push (~pop ());
				break;

			case DW_OP_or:
				LOGD(TAG, "OP_or");
				tmp1 = pop ();
				tmp2 = pop ();
				push (tmp1 | tmp2);
				break;

			case DW_OP_plus:
				LOGD(TAG, "OP_plus");
				tmp1 = pop ();
				tmp2 = pop ();
				push (tmp1 + tmp2);
				break;

			case DW_OP_plus_uconst:
				LOGD(TAG, "OP_plus_uconst(%lu)", (unsigned long) operand1);
				tmp1 = pop ();
				push (tmp1 + operand1);
				break;

			case DW_OP_shl:
				LOGD(TAG, "OP_shl");
				tmp1 = pop ();
				tmp2 = pop ();
				push (tmp2 << tmp1);
				break;

			case DW_OP_shr:
				LOGD(TAG, "OP_shr");
				tmp1 = pop ();
				tmp2 = pop ();
				push (tmp2 >> tmp1);
				break;

			case DW_OP_shra:
				LOGD(TAG, "OP_shra");
				tmp1 = pop ();
				tmp2 = pop ();
				if(context.sword(tmp2,stmp2)){
					LOGE(TAG,"signed change failed.");
					return -1;
				}
				push (stmp2 >> tmp1);
				break;

			case DW_OP_xor:
				LOGD(TAG, "OP_xor");
				tmp1 = pop ();
				tmp2 = pop ();
				push (tmp1 ^ tmp2);
				break;

			case DW_OP_le:
				LOGD(TAG, "OP_le");
				tmp1 = pop ();
				tmp2 = pop ();
				if(context.sword(tmp1,stmp1) == 0 && context.sword(tmp2,stmp2) == 0){
					push (stmp2 <= stmp1);
				}else{
					LOGE(TAG,"sword failed.");
					return -1;
				}
				
				break;

			case DW_OP_ge:
				LOGD(TAG, "OP_ge");
				tmp1 = pop ();
				tmp2 = pop ();
				if(context.sword(tmp1, stmp1) == 0 && context.sword(tmp2, stmp2) == 0){
					push (stmp2 >= stmp1);
				}else{
					LOGE(TAG,"sword failed.");
					return -1;
				}
				break;

			case DW_OP_eq:
			LOGD(TAG, "OP_eq");
			tmp1 = pop ();
			tmp2 = pop ();
			if(context.sword(tmp1, stmp1) == 0 && context.sword(tmp2, stmp2) == 0){
				push (stmp2 == stmp1);
			}else{
				LOGE(TAG,"sword failed.");
				return -1;
			}
			break;

			case DW_OP_lt:
			LOGD(TAG, "OP_lt");
			tmp1 = pop ();
			tmp2 = pop ();

			if(context.sword(tmp1, stmp1) == 0 && context.sword(tmp2, stmp2) == 0){
				push (stmp2 < stmp1);
			}else{
				LOGE(TAG,"sword failed.");
				return -1;
			}
			break;

			case DW_OP_gt:
			LOGD(TAG, "OP_gt");
			tmp1 = pop ();
			tmp2 = pop ();

			if(context.sword(tmp1, stmp1) == 0 && context.sword(tmp2, stmp2) == 0){
				push (stmp2 > stmp1);
			}else{
				LOGE(TAG,"sword failed.");
				return -1;
			}
			break;

			case DW_OP_ne:
			LOGD(TAG, "OP_ne");
			tmp1 = pop ();
			tmp2 = pop ();

			if(context.sword(tmp1, stmp1) == 0 && context.sword(tmp2, stmp2) == 0){
				push (stmp2 != stmp1);
			}else{
				LOGE(TAG,"sword failed.");
				return -1;
			}
			break;

			case DW_OP_skip:
				LOGD(TAG, "OP_skip(%d)", (int16_t) operand1);
				*addr += (int16_t) operand1;
				break;

			case DW_OP_bra:
				LOGD(TAG, "OP_skip(%d)", (int16_t) operand1);
				tmp1 = pop ();
				if (tmp1)
				*addr += (int16_t) operand1;
				break;

			case DW_OP_nop:
				LOGD(TAG, "OP_nop");
				break;

			case DW_OP_call2:
			case DW_OP_call4:
			case DW_OP_call_ref:
			case DW_OP_fbreg:
			case DW_OP_piece:
			case DW_OP_push_object_address:
			case DW_OP_xderef:
			case DW_OP_xderef_size:
			default:
				LOGE (TAG, "Unexpected opcode 0x%x", opcode);
				return -1;
		}
	}

	val = pop ();
	LOGD (TAG, "final value = 0x%lx", (unsigned long)val);

	return 0;
}

int DWExpression::read_operand (Context& context, word_t *addr, int operand_type, word_t *val, void *arg){
	uint8_t u8;
	uint16_t u16;
	uint32_t u32;
	uint64_t u64;
	int ret;

	if (operand_type == ADDR){
		switch (context.getAddrWidth()){
			case 1: operand_type = VAL8; break;
			case 2: operand_type = VAL16; break;
			case 4: operand_type = VAL32; break;
			case 8: operand_type = VAL64; break;
			default: 
				LOGE(TAG,"error addr width");
				return -1;
		}

		switch (operand_type){
			case VAL8:
				if((ret = context.readU8 (addr, &u8, arg)) < 0){
					return ret;
				}

				*val = u8;
				break;

			case VAL16:
				if((ret = context.readU16 (addr, &u16, arg)) < 0){
					return ret;
				}

				*val = u16;
				break;

			case VAL32:
				if((ret = context.readU32 (addr, &u32, arg)) < 0){
					return ret;
				}

				*val = u32;
				break;

			case VAL64:
				if((ret = context.readU64 (addr, &u64, arg)) < 0){
					return ret;
				}

				*val = u64;
				break;

			case ULEB128:
				if((ret = context.readUleb128 (addr, val, arg)) < 0){
					return ret;
				}

				break;

			case SLEB128:
				if((ret = context.readSleb128 (addr, val, arg)) < 0){
					return ret;
				}

				break;

			case OFFSET_VAL: /* only used by DW_OP_call_ref, which we don't implement */
			default:
				LOGE(TAG, "Unexpected operand type %d\n", operand_type);
				ret = -1;
		}
	}
		
	return ret;
}



================================================
FILE: crashsdk/src/jni/crash/unwind/eh_frame/DWExpression.h
================================================
#ifndef DWExpression_h
#define DWExpression_h

#include "Context.h"
#include "EHFrame.h"

class DWExpression{

public:

	static int evaluate(Context& context, CFI& cfi, word_t* addr, word_t& val, int& is_register);
	static int evaluate(Context& context, CFI& cfi, word_t* addr, word_t len, word_t& val, int& is_register);
	static int read_operand (Context& context, word_t *addr, int operand_type, word_t *val, void *arg = NULL);

private:
	static const char* TAG;
	static const uint8_t OPERANDS[256];

};

#endif

================================================
FILE: crashsdk/src/jni/crash/unwind/eh_frame/DWInstruction.cpp
================================================
#include "DWInstruction.h"
#include "Defines.h"
#include "Log.h"

#define DWARF_CFA_OPCODE_MASK 0xc0
#define DWARF_CFA_OPERAND_MASK   0x3f
#define SETREG(sr,regnum, w, v) \
	(sr)->rs_current.reg[(regnum)].where = (w); \
	(sr)->rs_current.reg[(regnum)].val = (v); \

typedef enum{
   DW_CFA_advance_loc     = 0x40,
   DW_CFA_offset    = 0x80,
   DW_CFA_restore      = 0xc0,
   DW_CFA_nop       = 0x00,
   DW_CFA_set_loc      = 0x01,
   DW_CFA_advance_loc1    = 0x02,
   DW_CFA_advance_loc2    = 0x03,
   DW_CFA_advance_loc4    = 0x04,
   DW_CFA_offset_extended = 0x05,
   DW_CFA_restore_extended   = 0x06,
   DW_CFA_undefined    = 0x07,
   DW_CFA_same_value      = 0x08,
   DW_CFA_register     = 0x09,
   DW_CFA_remember_state  = 0x0a,
   DW_CFA_restore_state   = 0x0b,
   DW_CFA_def_cfa      = 0x0c,
   DW_CFA_def_cfa_register   = 0x0d,
   DW_CFA_def_cfa_offset  = 0x0e,
   DW_CFA_def_cfa_expression = 0x0f,
   DW_CFA_expression      = 0x10,
   DW_CFA_offset_extended_sf = 0x11,
   DW_CFA_def_cfa_sf      = 0x12,
   DW_CFA_def_cfa_offset_sf  = 0x13,
   DW_CFA_lo_user      = 0x1c,
   DW_CFA_MIPS_advance_loc8  = 0x1d,
   DW_CFA_GNU_window_save = 0x2d,
   DW_CFA_GNU_args_size   = 0x2e,
   DW_CFA_GNU_negative_offset_extended   = 0x2f,
   DW_CFA_hi_user      = 0x3c
}dwarf_cfa_t;

const char* DWInstruction::TAG = PTAG("DWInstruction");

int DWInstruction::read_regnum (Context& context, word_t *addr, word_t *valp, void *arg) {
	int ret;

	if ((ret = context.readUleb128 (addr, valp, arg)) < 0){
		return ret;
	}

	if (*valp >= DWARF_NUM_PRESERVED_REGS){
		LOGE (TAG, "Invalid register number %u", (unsigned int) *valp);
		return -1;
	}

	return 0;
}

int DWInstruction::runCFIInstructions(Context& context, CFI& cfi, word_t ip, word_t* addr, word_t end_addr, dwarf_state_record_t *sr){
	
	word_t curr_ip, operand = 0, regnum, val, len, fde_encoding;
	dwarf_reg_state_t *rs_stack = NULL, *new_rs, *old_rs;
	uint8_t u8, op;
	uint16_t u16;
	uint32_t u32;
	void *arg;
	int ret;

	curr_ip = cfi.start_ip;

	LOGD(TAG,"run cfi instructions(0x%x - 0x%x), ip(0x%x - 0x%x)", *addr, end_addr, curr_ip, ip);

	/* Process everything up to and including the current 'ip',
	including all the DW_CFA_advance_loc instructions.  See
	'c->use_prev_instr' use in 'fetch_proc_info' for details. */
	while (curr_ip <= ip && *addr < end_addr){
		if ((ret = context.readU8 (addr, &op, arg)) < 0){
			LOGE(TAG,"read op failed(%d)",ret);
			return ret;
		}

		uint8_t oop = op;
		if (op & DWARF_CFA_OPCODE_MASK){
			operand = op & DWARF_CFA_OPERAND_MASK;
			op &= ~DWARF_CFA_OPERAND_MASK;
		}

		LOGD(TAG,"excute (%x) op(%x) operand(%x)", oop, op, operand);

		switch ((dwarf_cfa_t) op){
			case DW_CFA_advance_loc:
				curr_ip += operand * cfi.code_align;
				LOGD (TAG, "CFA_advance_loc to 0x%lx", (long) curr_ip);
				break;

			case DW_CFA_advance_loc1:
				if ((ret = context.readU8 (addr, &u8, arg)) < 0){
					goto fail;
				}
				curr_ip += u8 * cfi.code_align;
				LOGD (TAG, "CFA_advance_loc1 to 0x%lx", (long) curr_ip);
				break;

			case DW_CFA_advance_loc2:
				if ((ret = context.readU16 (addr, &u16, arg)) < 0){
					goto fail;
				}
				curr_ip += u16 * cfi.code_align;
				LOGD (TAG, "CFA_advance_loc2 to 0x%lx", (long) curr_ip);
				break;

			case DW_CFA_advance_loc4:
				if ((ret = context.readU32 (addr, &u32, arg)) < 0){
					goto fail;
				}
				curr_ip += u32 * cfi.code_align;
				LOGD (TAG, "CFA_advance_loc4 to 0x%lx", (long) curr_ip);
				break;

			case DW_CFA_MIPS_advance_loc8:
				#ifdef __mips__
					uint64_t u64;
					if ((ret = context.readU64 (addr, &u64, arg)) < 0){
						goto fail;
					}
					curr_ip += u64 * cfi.code_align;
					LOGD (TAG, "CFA_MIPS_advance_loc8");
					break;
				#else
					LOGD (TAG, "DW_CFA_MIPS_advance_loc8 on non-MIPS target");
					ret = -1;
					goto fail;
				#endif

			case DW_CFA_offset:
				regnum = operand;
				if (regnum >= DWARF_NUM_PRESERVED_REGS){
					LOGE (TAG, "Invalid register number %u in DW_cfa_OFFSET", (unsigned int) regnum);
					ret = -1;
					goto fail;
				}
				if ((ret = context.readUleb128 (addr, &val, arg)) < 0){
					goto fail;
				}

				SETREG (sr, regnum, DWARF_WHERE_CFAREL, val * cfi.data_align);
				LOGD(TAG, "CFA_offset r%lu at cfa+0x%lx",(long) regnum, (long) (val * cfi.data_align));
				break;

			case DW_CFA_offset_extended:
				if (((ret = read_regnum (context, addr, &regnum, arg)) < 0)
				|| ((ret = context.readUleb128 (addr, &val, arg)) < 0)){
					goto fail;
				}

				SETREG (sr, regnum, DWARF_WHERE_CFAREL, val * cfi.data_align);
				LOGD(TAG, "CFA_offset_extended r%lu at cf+0x%lx", (long) regnum, (long) (val * cfi.data_align));
				break;

			case DW_CFA_offset_extended_sf:
				if (((ret = read_regnum (context, addr, &regnum, arg)) < 0)
				|| ((ret = context.readSleb128 (addr, &val, arg)) < 0)){
					goto fail;
				}

				SETREG (sr, regnum, DWARF_WHERE_CFAREL, val * cfi.data_align);
				LOGD(TAG, "CFA_offset_extended_sf r%lu at cf+0x%lx", (long) regnum, (long) (val * cfi.data_align));
				break;

			case DW_CFA_restore:
				regnum = operand;
				if (regnum >= DWARF_NUM_PRESERVED_REGS){
					LOGE(TAG, "Invalid register number %u in DW_CFA_restore", (unsigned int) regnum);
					ret = -1;
					goto fail;
				}

				sr->rs_current.reg[regnum] = sr->rs_initial.reg[regnum];
				LOGD(TAG, "CFA_restore r%lu", (long) regnum);
				break;

			case DW_CFA_restore_extended:
				if ((ret = context.readUleb128 (addr, &regnum, arg)) < 0){
					goto fail;
				}

				if (regnum >= DWARF_NUM_PRESERVED_REGS){
					LOGE(TAG, "Invalid register number %u in DW_CFA_restore_extended", (unsigned int) regnum);
					ret = -1;
					goto fail;
				}

				sr->rs_current.reg[regnum] = sr->rs_initial.reg[regnum];
				LOGD(TAG, "CFA_restore_extended r%lu", (long) regnum);
				break;

			case DW_CFA_nop:
				break;

			case DW_CFA_set_loc:
				fde_encoding = cfi.fde_encoding;
				if ((ret = EHFrame::readEncodedPointer (context, addr, fde_encoding, &curr_ip)) < 0){
					goto fail;
				}

				LOGD(TAG, "CFA_set_loc to 0x%lx", (long) curr_ip);
				break;

			case DW_CFA_undefined:
				if ((ret = read_regnum (context, addr, &regnum, arg)) < 0){
					goto fail;
				}

				SETREG (sr, regnum, DWARF_WHERE_UNDEF, 0);
				LOGD(TAG, "CFA_undefined r%lu", (long) regnum);
				break;

			case DW_CFA_same_value:
				if ((ret = read_regnum (context, addr, &regnum, arg)) < 0){
					goto fail;
				}

				SETREG (sr, regnum, DWARF_WHERE_SAME, 0);
				LOGD(TAG, "CFA_same_value r%lu", (long) regnum);
				break;

			case DW_CFA_register:
				if (((ret = read_regnum (context, addr, &regnum, arg)) < 0)
				|| ((ret = context.readUleb128 (addr, &val, arg)) < 0)){
					goto fail;
				}

				SETREG (sr, regnum, DWARF_WHERE_REG, val);
				LOGD(TAG, "CFA_register r%lu to r%lu", (long) regnum, (long) val);
				break;

			case DW_CFA_remember_state:
				new_rs = new dwarf_reg_state();
				if (!new_rs) {
					LOGE(TAG, "Out of memory in DW_CFA_remember_state");
					ret = -1;
					goto fail;
				}

				memcpy (new_rs->reg, sr->rs_current.reg, sizeof (new_rs->reg));
				new_rs->next = rs_stack;
				rs_stack = new_rs;
				LOGD(TAG, "CFA_remember_state");
				break;

			case DW_CFA_restore_state:
				if (!rs_stack){
					LOGD(TAG, "register-state stack underflow");
					ret = -1;
					goto fail;
				}

				memcpy (&sr->rs_current.reg, &rs_stack->reg, sizeof (rs_stack->reg));
				old_rs = rs_stack;
				rs_stack = rs_stack->next;
				SAFE_DELETE (old_rs);
				LOGD(TAG, "CFA_restore_state");
				break;

			case DW_CFA_def_cfa:
				if (((ret = read_regnum (context, addr, &regnum, arg)) < 0)
				|| ((ret = context.readUleb128 (addr, &val, arg)) < 0)){
					goto fail;
				}

				SETREG (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
				SETREG (sr, DWARF_CFA_OFF_COLUMN, (dwarf_where_t)0, val);	/* NOT factored! */
				LOGD(TAG, "CFA_def_cfa r%lu+0x%lx", (long) regnum, (long) val);
				break;

			case DW_CFA_def_cfa_sf:
				if (((ret = read_regnum (context, addr, &regnum, arg)) < 0)
				|| ((ret = context.readSleb128 (addr, &val, arg)) < 0)){
					goto fail;
				}

				SETREG (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
				SETREG (sr, DWARF_CFA_OFF_COLUMN, (dwarf_where_t)0, val * cfi.data_align);		/* factored! */
				LOGD(TAG, "CFA_def_cfa_sf r%lu+0x%lx",(long) regnum, (long) (val * cfi.data_align));
				break;

			case DW_CFA_def_cfa_register:
				if ((ret = read_regnum (context, addr, &regnum, arg)) < 0){
					goto fail;
				}

				SETREG (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_REG, regnum);
				LOGD(TAG, "CFA_def_cfa_register r%lu", (long) regnum);
				break;

			case DW_CFA_def_cfa_offset:
				if ((ret = context.readUleb128 (addr, &val, arg)) < 0){
					goto fail;
				}
				SETREG (sr, DWARF_CFA_OFF_COLUMN, (dwarf_where_t)0, val);	/* NOT factored! */
				LOGD(TAG, "CFA_def_cfa_offset 0x%lx", (long) val);
				break;

			case DW_CFA_def_cfa_offset_sf:
				if ((ret = context.readSleb128 (addr, &val, arg)) < 0){
					goto fail;
				}

				SETREG (sr, DWARF_CFA_OFF_COLUMN, (dwarf_where_t)0, val * cfi.data_align);	/* factored! */
				LOGD(TAG, "CFA_def_cfa_offset_sf 0x%lx", (long) (val * cfi.data_align));
				break;

			case DW_CFA_def_cfa_expression:
				/* Save the address of the DW_FORM_block for later evaluation. */
				SETREG (sr, DWARF_CFA_REG_COLUMN, DWARF_WHERE_EXPR, *addr);

				if ((ret = context.readUleb128 (addr, &len, arg)) < 0){
					goto fail;
				}

				LOGD(TAG, "CFA_def_cfa_expr @ 0x%lx [%lu bytes]", (long) *addr, (long) len);
				*addr += len;
				break;

			case DW_CFA_expression:
				if ((ret = read_regnum (context, addr, &regnum, arg)) < 0){
					goto fail;
				}

				/* Save the address of the DW_FORM_block for later evaluation. */
				SETREG (sr, regnum, DWARF_WHERE_EXPR, *addr);

				if ((ret = context.readUleb128 (addr, &len, arg)) < 0){
					goto fail;
				}

				LOGD(TAG, "CFA_expression r%lu @ 0x%lx [%lu bytes]", (long) regnum, (long) addr, (long) len);
				*addr += len;
				break;

			case DW_CFA_GNU_args_size:
				if ((ret = context.readUleb128 (addr, &val, arg)) < 0){
					goto fail;
				}

				sr->args_size = val;
				LOGD(TAG, "CFA_GNU_args_size %lu", (long) val);
				break;

			case DW_CFA_GNU_negative_offset_extended:
				/* A comment in GCC says that this is obsoleted by
				DW_CFA_offset_extended_sf, but that it's used by older
				PowerPC code.  */
				if (((ret = read_regnum (context, addr, &regnum, arg)) < 0)
				|| ((ret = context.readUleb128 (addr, &val, arg)) < 0)){
					goto fail;
				}

				SETREG (sr, regnum, DWARF_WHERE_CFAREL, -(val * cfi.data_align));
				LOGD(TAG, "CFA_GNU_negative_offset_extended cfa+0x%lx", (long) -(val * cfi.data_align));
				break;

			case DW_CFA_GNU_window_save:
			case DW_CFA_lo_user:
			case DW_CFA_hi_user:
			default:
				LOGD(TAG, "Unexpected CFA opcode 0x%x", op);
				ret = -1;
				goto fail;
		}
	}

	ret = 0;

	fail:
	if(ret != 0){
		 LOGE(TAG,"runCFIInstructions failed.");
	}
	
	/* Free the register-state stack, if not empty already.  */
	while (rs_stack) {
		old_rs = rs_stack;
		rs_stack = rs_stack->next;
		SAFE_DELETE(old_rs);
	}
	return ret;
}

================================================
FILE: crashsdk/src/jni/crash/unwind/eh_frame/DWInstruction.h
================================================
#ifndef DWInstruction_H
#define DWInstruction_H

#include "Context.h"
#include "EHFrame.h"

class DWInstruction{

public:

	static int runCFIInstructions(Context& context, CFI& cfi, word_t ip, word_t* addr, word_t end_addr, dwarf_state_record_t *sr);

private:
	static int read_regnum (Context& context, word_t *addr, word_t *valp, void *arg);

	static const char* TAG;

};

#endif

================================================
FILE: crashsdk/src/jni/crash/unwind/eh_frame/EHFrame.cpp
================================================
#include "EHFrame.h"
#include "Defines.h"
#include "Log.h"
#include "DWInstruction.h"
#include "DWExpression.h"
#include <link.h>

const char* EHFrame::TAG = PTAG("EHFrame");

int EHFrame::restoreFrame(Context& context){

	CFI& cfi = context.mCfi;

	LOGD(TAG,"restoreFrame ip=0x%x",cfi.ip);

	if(dl_iterate_phdr (EHFrame::checklib, &context) == 1){

		LOGD(TAG,"find target fde&cie success, restore reg states...");

		dwarf_state_record_t& sr = cfi.sr;
		memset (&sr, 0, sizeof (sr));
		for (int i = 0; i < DWARF_NUM_PRESERVED_REGS + 2; ++i){
			(sr).rs_current.reg[i].where = DWARF_WHERE_SAME;
			(sr).rs_current.reg[i].val = 0;
		}

		word_t start = cfi.cie_instr_start;
		word_t end = cfi.cie_instr_end;
		if(DWInstruction::runCFIInstructions(context, cfi, ~(word_t)0, &start, end, &sr) < 0){
			LOGE(TAG,"run cie instructions failed.");
			return 1;
		}

		memcpy (&sr.rs_initial, &sr.rs_current, sizeof (sr.rs_initial));

		start = cfi.fde_instr_start;
		end = cfi.fde_instr_end;

		if(DWInstruction::runCFIInstructions(context, cfi, cfi.ip, &start, end, &sr) < 0){
			LOGE(TAG,"run fde instructions failed.");
			return 1;
		}

		if(applyInstr(context, cfi)){
			LOGE(TAG,"applyInstr failed");
			return 1;
		}

		return 0;
	}

	LOGE(TAG,"restoreFrame(0x%lx) failed, cant find host lib", (long)cfi.ip);

	return 1;
}

int EHFrame::checklib (struct dl_phdr_info *info, size_t size, void *ptr){

	Context& context = *((Context*)ptr);
	CFI& cfi = context.mCfi;

	if (size < OFFSET(struct dl_phdr_info, dlpi_phnum) + sizeof (info->dlpi_phnum)){
		LOGE(TAG,"size not match while iterating libs, continue");
		return 0;
	}

	// LOGD(TAG,"checking %s (0x%x)", info->dlpi_name, info->dlpi_addr);
	// return 0;

	word_t ip = cfi.ip;
	int ret;
	const ElfW(Phdr) *phdr = info->dlpi_phdr;
	const ElfW(Phdr) *p_eh_hdr = NULL;
	const ElfW(Phdr) *p_dynamic =NULL;
	const ElfW(Phdr) *p_text = NULL;
	ElfW(Addr) libbase = info->dlpi_addr;
	ElfW(Addr) max_load_addr = 0;

	for (int n = info->dlpi_phnum; --n >= 0; phdr++){
		if (phdr->p_type == PT_LOAD){
			ElfW(Addr) vaddr = phdr->p_vaddr + libbase;

			if (ip >= vaddr && ip < vaddr + phdr->p_memsz){
				// LOGD(TAG,"find p_text %p in %s", phdr, info->dlpi_name);
				p_text = phdr;
			}

			if (vaddr + phdr->p_filesz > max_load_addr){
				max_load_addr = vaddr + phdr->p_filesz;
			}
		} else if (phdr->p_type == PT_GNU_EH_FRAME){
			// LOGD(TAG,"find p_eh_hdr %p in %s", phdr, info->dlpi_name);
			p_eh_hdr = phdr;
		} else if (phdr->p_type == PT_DYNAMIC){
			p_dynamic = phdr;
		}
    }

    if (!p_text){
    	// LOGE(TAG,"p_text = %p, p_eh_hdr = (%p)", p_text, p_eh_hdr);
    	return 0;
    }

    if(!p_eh_hdr){
    	LOGE(TAG,"not found eh_frame, in %s", info->dlpi_name);
    	return 0;
    }

    LOGD(TAG,"found ip(0x%x) fall in to lib(%s) (0x%lx)", ip, info->dlpi_name, (long)libbase);

    cfi.last_libname = info->dlpi_name;
    cfi.last_libbase = libbase;
    cfi.gp = 0;

    if (p_dynamic){
		/* For dynamicly linked executables and shared libraries,
		 DT_PLTGOT is the value that data-relative addresses are
		 relative to for that object.  We call this the "gp".  */
		ElfW(Dyn) *dyn = (ElfW(Dyn) *)(p_dynamic->p_vaddr + libbase);
		for (; dyn->d_tag != DT_NULL; ++dyn){
			if (dyn->d_tag == DT_PLTGOT){
				/* Assume that _DYNAMIC is writable and GLIBC has
				   relocated it (true for x86 at least).  */
				cfi.gp = dyn->d_un.d_ptr;
				break;
			}
		}
	}

	eh_frame_hdr_t *eh_frame_hdr = (eh_frame_hdr_t*)(p_eh_hdr->p_vaddr + libbase);
	if(eh_frame_hdr->version != EH_VERSION){
		LOGE(TAG,"not support eh version %d",eh_frame_hdr->version);
		return -1;
	}

	word_t addr, eh_frame_start, eh_frame_end, fde_count;
	addr = (word_t) (uintptr_t) (eh_frame_hdr + 1);

	/* (Optionally) read eh_frame_ptr: */
	if ((ret = readEncodedPointer(context, &addr, eh_frame_hdr->eh_frame_ptr_enc, &eh_frame_start)) < 0){
		LOGE(TAG,"read eh_frame_start failed.");
		return ret;
	}

	/* (Optionally) read fde_count: */
	if ((ret = readEncodedPointer(context, &addr, eh_frame_hdr->fde_count_enc, &fde_count)) < 0){
		LOGE(TAG,"read fde_count failed");
		return ret;
	}

	LOGD(TAG,"eh_frame_start(%p) table_enc(0x%x), eh_frame_ptr_enc(0x%x) fde_count(%d) fde_count_enc(0x%x)", 
		eh_frame_start, eh_frame_hdr->table_enc, eh_frame_hdr->eh_frame_ptr_enc, fde_count , eh_frame_hdr->fde_count_enc);

	if (eh_frame_hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4)){
		LOGD(TAG,"table_enc not support now.");
	}else{
		word_t segbase = (word_t)(uintptr_t)eh_frame_hdr;
		int32_t relIP = cfi.ip - segbase;
		table_entry* entry = findEntry((table_entry*)addr,fde_count,relIP);
		if(entry == NULL){
			LOGE(TAG,"find fde entry failed.");
			return -1;
		}

		LOGD(TAG,"findEntry: e->start_ip_offset(%d), relIP(%d)", entry->start_ip_offset, relIP);

		word_t fde_addr = entry->fde_offset + segbase;
		if(extractCFI(context, &fde_addr) == 0){
			LOGD(TAG,"find cfi success, ip(0x%x - 0x%x), cie_instr(0x%x - 0x%x), fde_instr(0x%x - 0x%x)", 
	    		cfi.start_ip, cfi.end_ip, cfi.cie_instr_start, cfi.cie_instr_end, cfi.fde_instr_start, cfi.fde_instr_end);

	    	return 1;
		}else{
			LOGE(TAG,"extractCFI failed.");
		}
	}

    LOGE(TAG,"parse eh_frame failed");
    
    return 2;
}

table_entry* EHFrame::findEntry(table_entry* start, uint64_t size, int32_t rel_ip){
	struct table_entry *e = NULL;
	uint64_t lo, hi, mid;

	// int last = 0;
	// for(int i=0;i<size;i++){
	// 	int off = (start+i)->start_ip_offset;
	// 	int r = last == 0? 0:(off-last);
	// 	last = off;

	// 	LOGD(TAG,"findEntry %d: %d, %d", i, off, r);
	// }

	/* do a binary search for right entry: */
	for (lo = 0, hi = size; lo < hi;){
		mid = (lo + hi) / 2;
		e = start + mid;
		// LOGD(TAG, "findEntry: lo(%d) mid(%d) hi(%d) relIP(%d), lo(%llu) hi(%llu) mid(%llu)", 
		// 	(start+lo)->start_ip_offset, e->start_ip_offset, (start+hi)->start_ip_offset,
		//  	rel_ip, lo, hi, mid);
		if (rel_ip < e->start_ip_offset){
			hi = mid;
		}else{
			lo = mid + 1;
		}
	}

	if (hi <= 0){
		return NULL;
	}

	e = start + hi - 1;
	return e;
}

int EHFrame::extractCIE(Context& context, word_t* pCIEAddr){
	CFI& cfi = context.mCfi;

	word_t addr = *pCIEAddr;
	LOGD(TAG, "extractCIE at address 0x%lx", addr);

	uint8_t version, ch, augstr[5], fde_encoding, handler_encoding;
	word_t len, cie_end_addr, aug_size;
	uint32_t u32val;
	uint64_t u64val;
	size_t i;
	int ret;

	/* Pick appropriate default for FDE-encoding.  DWARF spec says
	start-IP (initial_location) and the code-size (address_range) are
	"address-unit sized constants".  The `R' augmentation can be used
	to override but by default, we pick an address-sized unit
	for fde_encoding.  */
	switch (context.getAddrWidth()){
		case 4:	fde_encoding = DW_EH_PE_udata4; break;
		case 8:	fde_encoding = DW_EH_PE_udata8; break;
		default:	fde_encoding = DW_EH_PE_omit; break;
	}

	cfi.lsda_encoding = DW_EH_PE_omit;
	cfi.handler = 0;

	if ((ret = context.readU32 (&addr, &u32val)) < 0){
		return ret;
	}

	if (u32val != 0xffffffff){
		/* the CIE is in the 32-bit DWARF format */
		uint32_t cie_id;
		/* DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0 */
		const uint32_t expected_id = 0;

		len = u32val;
		cie_end_addr = addr + len;
		if ((ret = context.readU32 (&addr, &cie_id)) < 0){
			return ret;
		}
		if (cie_id != expected_id) {
			LOGE (TAG, "Unexpected CIE id 0x%x", cie_id);
			return -1;
		}
	} else {
		/* the CIE is in the 64-bit DWARF format */
		uint64_t cie_id;
		/* DWARF says CIE id should be 0xffffffffffffffff, but in
		.eh_frame, it's 0 */
		const uint64_t expected_id = 0;

		if ((ret = context.readU64 (&addr, &u64val)) < 0){
			return ret;
		}

		len = u64val;
		cie_end_addr = addr + len;
		if ((ret = context.readU64 (&addr, &cie_id)) < 0){
			return ret;
		}

		if (cie_id != expected_id){
			LOGE (TAG, "Unexpected CIE id 0x%llx", cie_id);
			return -1;
		}
	}

	cfi.cie_instr_end = cie_end_addr;

	if ((ret = context.readU8 (&addr, &version)) < 0){
		return ret;
	}

	if (version != 1 && version != 3 && version != 4) {
		LOGE (TAG, "Got CIE version %u, expected version 1, 3 or 4; ", version);
		return -1;
	}

	/* read and parse the augmentation string: */
	memset (augstr, 0, sizeof (augstr));
	for (i = 0;;) {
		if ((ret = context.readU8 (&addr, &ch	)) < 0){
			return ret;
		}

		if (!ch){
			break;	/* end of augmentation string */
		}

		if (i < sizeof (augstr) - 1){
			augstr[i++] = ch;
		}
	}

	if (version == 4) {
		uint8_t address_size;
		if ((ret = context.readU8(&addr, &address_size)) < 0) {
			return ret;
		}
		if (address_size != sizeof(word_t)) {
			return -1;
		}
		uint8_t segment_size;
		if ((ret = context.readU8(&addr, &segment_size)) < 0) {
			return ret;
		}
		// We don't support non-zero segment size.
		if (segment_size != 0) {
			LOGE(TAG,"segment_size is not 0 (0x%x)",segment_size);
			return -1;
		}
	}


	if ((ret = context.readUleb128 (&addr, &cfi.code_align)) < 0
	|| (ret = context.readSleb128 (&addr, &cfi.data_align)) < 0){
		return ret;
	}

	/* Read the return-address column either as a u8 or as a uleb128.  */
	if (version == 1){
		if ((ret = context.readU8 (&addr, &ch)) < 0){
			return ret;
		}
		cfi.ret_addr_column = ch;
	} else if ((ret = context.readUleb128 (&addr, &cfi.ret_addr_column)) < 0){
		return ret;
	}

	i = 0;
	if (augstr[0] == 'z'){
		cfi.sized_augmentation = 1;
		if ((ret = context.readUleb128 (&addr, &aug_size)) < 0){
			return ret;
		}

		i++;
	}

	for (; i < sizeof (augstr) && augstr[i]; ++i){
		switch (augstr[i]){
			case 'L':
				/* read the LSDA pointer-encoding format.  */
				if ((ret = context.readU8 (&addr, &ch)) < 0){
					return ret;
				}
				cfi.lsda_encoding = ch;
				break;

			case 'R':
				/* read the FDE pointer-encoding format.  */
				if ((ret = context.readU8(&addr, &fde_encoding)) < 0){
					return ret;
				}
				break;

			case 'P':
				/* read the personality-routine pointer-encoding format.  */
				if ((ret = context.readU8 (&addr, &handler_encoding)) < 0){
					return ret;
				}

				if ((ret = readEncodedPointer (context, &addr, handler_encoding, &cfi.handler)) < 0){
					return ret;
				}
				break;

			case 'S':
				/* This is a signal frame. */
				cfi.signal_frame = 1;
				cfi.have_abi_marker = 1;
				break;

			default:
				LOGE (TAG, "Unexpected augmentation string `%s'\n", augstr);
				if (!cfi.sized_augmentation){
					return -1;
				}
		}
	}

	cfi.fde_encoding = fde_encoding;
	cfi.cie_instr_start = addr;
	LOGD (TAG, "CIE parsed OK, augmentation = \"%s\", handler=0x%lx, cie_instr_start(0x%lx) cie_instr_end(0x%lx) data_align(0x%lx) code_align(0x%lx)", 
		augstr, (long) cfi.handler, (long)cfi.cie_instr_start, (long)cfi.cie_instr_end, (long)(cfi.data_align), (long)(cfi.code_align));
	return 0;
}

int EHFrame::extractCFI(Context& context, word_t* addrp){
	LOGD(TAG,"extractCFI from 0x%x", *addrp);

	CFI& cfi = context.mCfi;

	word_t fde_end_addr, cie_addr, cie_offset_addr, aug_end_addr = 0;
	word_t start_ip, ip_range, aug_size, addr = *addrp;
	int ret, ip_range_encoding;
	uint64_t u64val;
	uint32_t u32val;

	if ((ret = context.readU32 (&addr, &u32val)) < 0){
		return ret;
	}

	if (u32val != 0xffffffff){
		int32_t cie_offset;

		/* In some configurations, an FDE with a 0 length indicates the
		end of the FDE-table.  */
		if (u32val == 0){
			LOGE(TAG,"end of FDE-table");
			return -1;
		}

		/* the FDE is in the 32-bit DWARF format */

		*addrp = fde_end_addr = addr + u32val;
		cie_offset_addr = addr;

		if ((ret = context.readS32(&addr, &cie_offset)) < 0){
			return ret;
		}

		if (cie_offset == 0){
			LOGE(TAG,"cie pointer is 0");
			return -1;
		}

		cie_addr = cie_offset_addr - cie_offset;
	} else {
		int64_t cie_offset;

		/* the FDE is in the 64-bit DWARF format */
		if ((ret = context.readU64 (&addr, &u64val)) < 0){
			return -1;
		}

		*addrp = fde_end_addr = addr + u64val;
		cie_offset_addr = addr;

		if ((ret = context.readS64 (&addr, &cie_offset)) < 0){
			return -1;
		}

		if (cie_offset == 0){
			LOGE(TAG,"cie pointer is 0");
			return -1;
		}

		cie_addr = cie_offset_addr - cie_offset;
	}

	if ((ret = extractCIE (context, &cie_addr)) < 0){
		return -1;
	}

	/* IP-range has same encoding as FDE pointers, except that it's
	always an absolute value: */
	ip_range_encoding = cfi.fde_encoding & DW_EH_PE_FORMAT_MASK;

	if ((ret = readEncodedPointer (context, &addr, cfi.fde_encoding, &start_ip)) < 0
		|| (ret = readEncodedPointer (context, &addr, ip_range_encoding, &ip_range)) < 0){
		return -1;
	}

	cfi.start_ip = start_ip;
	cfi.end_ip = start_ip + ip_range;

	if (cfi.sized_augmentation){
		if ((ret = context.readUleb128 (&addr, &aug_size)) < 0){
			LOGE(TAG,"read aug size failed.");
			return -1;
		}

		aug_end_addr = addr + aug_size;
	}

	if(cfi.lsda_encoding != DW_EH_PE_omit){
		if ((ret = readEncodedPointer (context, &addr, cfi.lsda_encoding, &cfi.lsda)) < 0){
			LOGE(TAG,"read lsda failed");
			return -1;
		}
	}

	LOGD(TAG,"FDE covers IP 0x%lx-0x%lx, LSDA=0x%lx\n", (long) cfi.start_ip, (long) cfi.end_ip, (long) cfi.lsda);

	if(cfi.ip < cfi.start_ip || cfi.ip >= cfi.end_ip){
		LOGE(TAG,"ip not fall in FDE");
		return -1;
	}

	if (cfi.have_abi_marker){
		if ((ret = context.readU16 (&addr, &cfi.abi)) < 0
		|| (ret = context.readU16 (&addr, &cfi.tag)) < 0){
			return -1;
		}
		
		LOGE(TAG, "Found ABI marker = (abi=%u, tag=%u)\n", cfi.abi, cfi.tag);
	}

	if (cfi.sized_augmentation){
		cfi.fde_instr_start = aug_end_addr;
	}else{
		cfi.fde_instr_start = addr;
	}

	cfi.fde_instr_end = fde_end_addr;
	
	return 0;
}

int EHFrame::applyInstr(Context& context, CFI& cfi){
	word_t regnum, addr;
	int ret = 0, isRegister = 0;
	dwarf_reg_state* rs = &cfi.sr.rs_current;
	word_t old_ip = cfi.ip;
	word_t old_cfa = cfi.cfa;

	/* Evaluate the CFA first, because it may be referred to by other
	expressions.  */
	if (rs->reg[DWARF_CFA_REG_COLUMN].where == DWARF_WHERE_REG){
		/* CFA is equal to [reg] + offset: */

		/* As a special-case, if the stack-pointer is the CFA and the
		stack-pointer wasn't saved, popping the CFA implicitly pops
		the stack-pointer as well.  */
		if ((rs->reg[DWARF_CFA_REG_COLUMN].val == UNW_TDEP_SP)
			&& (UNW_TDEP_SP < ARRAY_SIZE(rs->reg))
			&& (rs->reg[UNW_TDEP_SP].where == DWARF_WHERE_SAME)){

			cfi.cfa += (rs->reg[DWARF_CFA_OFF_COLUMN].val);
			
			// return -1;
		} else {
			word_t dwarfReg = rs->reg[DWARF_CFA_REG_COLUMN].val;
			if(dwarfReg >= DWARF_NUM_PRESERVED_REGS){
				LOGE(TAG,"dwarfReg out of range: %lu", dwarfReg);
				return -1;
			}

			cfi.cfa = cfi.loc[dwarfReg] + (rs->reg[DWARF_CFA_OFF_COLUMN].val);
			LOGD(TAG,"new cfa = 0x%lx, loc[%d] + offset(%d)", (long)cfi.cfa, dwarfReg, (rs->reg[DWARF_CFA_OFF_COLUMN].val));
		}

		if(cfi.cfa == old_cfa){
			LOGE(TAG,"same cfa!");
		}

	} else {
		/* CFA is equal to EXPR: */
		if(rs->reg[DWARF_CFA_REG_COLUMN].where != DWARF_WHERE_EXPR){
			LOGE(TAG,"it should be expression type");
			return -1;
		}

		addr = rs->reg[DWARF_CFA_REG_COLUMN].val;

		if ((ret = DWExpression::evaluate(context, cfi, &addr, cfi.cfa, isRegister)) < 0){
			LOGE(TAG,"caculate expression failed.");
			return ret;
		}

		if(isRegister){
			LOGE(TAG,"cfa cannot be in register");
			return -1;
		}
	}

	bool ret_addr_column_undefined = false;
	for (int i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i){
		switch ((dwarf_where_t) rs->reg[i].where){
		case DWARF_WHERE_UNDEF:
			LOGE(TAG,"undef reg(%d)", i);
			if(i == cfi.ret_addr_column){
				LOGE(TAG,"return addr cannot undefined");
				cfi.root = 1;
			}
			break;

		case DWARF_WHERE_SAME:
			break;

		case DWARF_WHERE_CFAREL:
			cfi.loc[i] = *(word_t*)(cfi.cfa + rs->reg[i].val);
			LOGD(TAG,"loc[%d] = cfa[0x%lx] offset %d , 0x%lx", i, (long)cfi.cfa, rs->reg[i].val, (long)cfi.loc[i]);
			break;

		case DWARF_WHERE_REG:
			regnum = rs->reg[i].val;
			if(regnum >= DWARF_NUM_PRESERVED_REGS){
				LOGE(TAG,"regnum out of range.");
				return -1;
			}

			cfi.loc[i] = cfi.loc[regnum];
			LOGD(TAG,"loc[%d] = loc[%d] = 0x%lx", i, regnum, cfi.loc[i]);
			break;

		case DWARF_WHERE_EXPR:
			addr = rs->reg[i].val;
			if ((ret = DWExpression::evaluate (context, cfi, &addr, cfi.loc[i], isRegister)) < 0){
				LOGE(TAG, "evaluate DWExpression failed %d", ret);
				return ret;
			}
			LOGD(TAG,"loc[%d] = 0x%lx", i, cfi.loc[i]);
			break;
		}
	}

	LOGD(TAG,"ret_addr_column = %d", cfi.ret_addr_column);
	/* DWARF spec says undefined return address location means end of stack. */
	if (cfi.ret_addr_column < DWARF_NUM_PRESERVED_REGS) {
		cfi.ip = cfi.loc[cfi.ret_addr_column];
	}

	LOGD(TAG,"after apply ip(0x%lx) old ip(0x%lx) cfa(0x%lx) old cfa(0x%lx) ",
		(long)cfi.ip, (long)old_ip, (long)cfi.cfa, (long) old_cfa);

	if(cfi.cfa == old_cfa){
		/* XXX: check for ip to be code_aligned */
		if (cfi.ip == old_ip){
			cfi.root = 1;
			LOGE(TAG, "ip and cfa unchanged; stopping here (ip=0x%lx)", (long) cfi.ip);
		}
	}

	return 0;
}

int EHFrame::readEncodedPointer(Context& context, word_t *addr, uint8_t encoding, word_t *valp){
	LOGD(TAG,"readEncodedPointer encoding(0x%x) from addr(%p)",encoding, addr);

	CFI& cfi = context.mCfi;
	word_t val, initial_addr = *addr;
	uint16_t uval16;
	uint32_t uval32;
	uint64_t uval64;
	int16_t sval16;
	int32_t sval32;
	int64_t sval64;
	int ret;

	int ws = sizeof(word_t);

	/* DW_EH_PE_omit and DW_EH_PE_aligned don't follow the normal
	format/application encoding.  Handle them first.  */
	if (encoding == DW_EH_PE_omit){
		*valp = 0;
		return -1;
	}else if (encoding == DW_EH_PE_aligned){
		*addr = (initial_addr + ws - 1) & -ws;
		return context.readW(addr, valp);
	}

	LOGD(TAG,"encoding & DW_EH_PE_FORMAT_MASK 0x%x, addr(%p)", encoding & DW_EH_PE_FORMAT_MASK, *addr);

	switch (encoding & DW_EH_PE_FORMAT_MASK){
		case DW_EH_PE_ptr:
			if ((ret = context.readW(addr, &val)) < 0) return ret;
			break;

		case DW_EH_PE_uleb128:
			if ((ret = context.readUleb128(addr, &val)) < 0) return ret;
			break;

		case DW_EH_PE_udata2:
			if ((ret = context.readU16(addr, &uval16)) < 0) return ret;
			val = uval16;
			break;

		case DW_EH_PE_udata4:
			if ((ret = context.readU32(addr, &uval32)) < 0) return ret;
			val = uval32;
			break;

		case DW_EH_PE_udata8:
			if ((ret = context.readU64(addr, &uval64)) < 0) return ret;
			val = uval64;
			break;

		case DW_EH_PE_sleb128:
			if ((ret = context.readUleb128(addr, &val)) < 0) return ret;
			break;

		case DW_EH_PE_sdata2:
			if ((ret = context.readS16(addr, &sval16)) < 0) return ret;
			val = sval16;
			break;

		case DW_EH_PE_sdata4:
			if ((ret = context.readS32(addr, &sval32)) < 0) return ret;
			val = sval32;
			break;

		case DW_EH_PE_sdata8:
			if ((ret = context.readS64(addr, &sval64)) < 0) return ret;
			val = sval64;
			break;

		default:
			LOGE (TAG, "unexpected encoding format 0x%x\n", encoding & DW_EH_PE_FORMAT_MASK);
			return -1;
	}

	if (val == 0){
		/* 0 is a special value and always absolute.  */
		*valp = 0;
		return 0;
	}

	LOGD(TAG,"val(0x%x) encoding & DW_EH_PE_APPL_MASK 0x%x ,addr(%p)", val, encoding & DW_EH_PE_APPL_MASK, *addr);

	switch (encoding & DW_EH_PE_APPL_MASK){
		case DW_EH_PE_absptr:
			break;

		case DW_EH_PE_pcrel:
			val += initial_addr;
			break;

		case DW_EH_PE_datarel:
			/* XXX For now, assume that data-relative addresses are relative
			to the global pointer.  */
			val += cfi.gp;
			break;

		case DW_EH_PE_funcrel:
			val += cfi.start_ip;
			break;

		case DW_EH_PE_textrel:
			/* XXX For now we don't support text-rel values.  If there is a
			platform which needs this, we probably would have to add a
			"segbase" member to unw_proc_info_t.  */
		default:
			LOGE(TAG, "unexpected application type 0x%x\n", encoding & DW_EH_PE_APPL_MASK);
			return -1;
	}

	/* Trim off any extra bits.  Assume that sign extension isn't
	required; the only place it is needed is MIPS kernel space
	addresses.  */
	if (sizeof (val) > ws){
		if(ws != 4){
			LOGE(TAG,"word size not 4!");
			return -1;
		}
		val = (uint32_t) val;
	}

	if (encoding & DW_EH_PE_indirect){
		word_t indirect_addr = val;
		if ((ret = context.readW(&indirect_addr, &val)) < 0){
			return ret;
		}
	}

	*valp = val;
	return 0;
}



================================================
FILE: crashsdk/src/jni/crash/unwind/eh_frame/EHFrame.h
================================================
#ifndef EHFrame_H
#define EHFrame_H

#include "Context.h"

#define EH_VERSION 1

/* DWARF Pointer-Encoding (PEs).

   Pointer-Encodings were invented for the GCC exception-handling
   support for C++, but they represent a rather generic way of
   describing the format in which an address/pointer is stored and
   hence we include the definitions here, in the main dwarf.h file.
   The Pointer-Encoding format is partially documented in Linux Base
   Spec v1.3 (http://www.linuxbase.org/spec/).  The rest is reverse
   engineered from GCC.

*/
#define DW_EH_PE_FORMAT_MASK	0x0f	/* format of the encoded value */
#define DW_EH_PE_APPL_MASK	0x70	/* how the value is to be applied */
/* Flag bit.  If set, the resulting pointer is the address of the word
   that contains the final address.  */
#define DW_EH_PE_indirect	0x80

/* Pointer-encoding formats: */
#define DW_EH_PE_omit		0xff
#define DW_EH_PE_ptr		0x00	/* pointer-sized unsigned value */
#define DW_EH_PE_uleb128	0x01	/* unsigned LE base-128 value */
#define DW_EH_PE_udata2		0x02	/* unsigned 16-bit value */
#define DW_EH_PE_udata4		0x03	/* unsigned 32-bit value */
#define DW_EH_PE_udata8		0x04	/* unsigned 64-bit value */
#define DW_EH_PE_sleb128	0x09	/* signed LE base-128 value */
#define DW_EH_PE_sdata2		0x0a	/* signed 16-bit value */
#define DW_EH_PE_sdata4		0x0b	/* signed 32-bit value */
#define DW_EH_PE_sdata8		0x0c	/* signed 64-bit value */

/* Pointer-encoding application: */
#define DW_EH_PE_absptr		0x00	/* absolute value */
#define DW_EH_PE_pcrel		0x10	/* rel. to addr. of encoded value */
#define DW_EH_PE_textrel	0x20	/* text-relative (GCC-specific???) */
#define DW_EH_PE_datarel	0x30	/* data-relative */
/* The following are not documented by LSB v1.3, yet they are used by
   GCC, presumably they aren't documented by LSB since they aren't
   used on Linux:  */
#define DW_EH_PE_funcrel	0x40	/* start-of-procedure-relative */
#define DW_EH_PE_aligned	0x50	/* aligned pointer */

class DWExpression;
class DWInstruction;
class EHFrame{

public:

   static int restoreFrame(Context& context);
   static int readEncodedPointer(Context& context, word_t *addr, uint8_t encoding, word_t *valp);

private:
   static const char* TAG;

   static int checklib(struct dl_phdr_info *info, size_t size, void *ptr);
   static table_entry* findEntry(table_entry* start, uint64_t size, int32_t rel_ip);
   static int extractCFI(Context& context, word_t* addrp);
   static int extractCIE(Context& context, word_t* pCIEAddr);
   static int applyInstr(Context& context, CFI& cfi);
   

};

#endif

================================================
FILE: crashsdk/src/jni/crash/unwind/x86/Config_x86.h
================================================
#ifndef CONFIG_x86_H
#define CONFIG_x86_H

#ifdef __i386__

#include<stdint.h>

/* This matches the value used by GCC (see
   gcc/config/i386.h:DWARF_FRAME_REGISTERS), which leaves plenty of
   room for expansion.  */
#define DWARF_NUM_PRESERVED_REGS	17

typedef uint32_t word_t;
typedef int32_t sword_t;

#endif

#endif

================================================
FILE: crashsdk/src/jni/crash/unwind/x86/Context_x86.cpp
================================================
#include "Context_x86.h"
#include "Log.h"
#include "Defines.h"
#include "EHFrame.h"

#ifdef __i386__

const char* Context_x86::TAG = PTAG("Context_x86");

int Context_x86::setupFrame(CFI& cfi){

	cfi.loc[EAX] = getReg(UNW_X86_EAX);
	cfi.loc[ECX] = getReg(UNW_X86_ECX);
	cfi.loc[EDX] = getReg(UNW_X86_EDX);
	cfi.loc[EBX] = getReg(UNW_X86_EBX);
	cfi.loc[ESP] = getReg(UNW_X86_ESP);
	cfi.loc[EBP] = getReg(UNW_X86_EBP);
	cfi.loc[ESI] = getReg(UNW_X86_ESI);
	cfi.loc[EDI] = getReg(UNW_X86_EDI);
	cfi.loc[EIP] = getReg(UNW_X86_EIP);
	cfi.loc[EFLAGS] = getReg(UNW_X86_EFLAGS);
	cfi.loc[TRAPNO] = getReg(UNW_X86_TRAPNO);
	cfi.loc[ST0] = getReg(UNW_X86_ST0);

	for (int i = ST0 + 1; i < DWARF_NUM_PRESERVED_REGS; ++i){
		cfi.loc[i] = 0;
	}

	cfi.ip = cfi.loc[EIP];
	cfi.cfa = cfi.loc[ESP];

	return 0;
}

word_t Context_x86::getReg(uint32_t reg){
	void* addr;
	switch (reg){
		case UNW_X86_GS:  addr = &mContext->uc_mcontext.gregs[REG_GS]; break;
		case UNW_X86_FS:  addr = &mContext->uc_mcontext.gregs[REG_FS]; break;
		case UNW_X86_ES:  addr = &mContext->uc_mcontext.gregs[REG_ES]; break;
		case UNW_X86_DS:  addr = &mContext->uc_mcontext.gregs[REG_DS]; break;
		case UNW_X86_EAX: addr = &mContext->uc_mcontext.gregs[REG_EAX]; break;
		case UNW_X86_EBX: addr = &mContext->uc_mcontext.gregs[REG_EBX]; break;
		case UNW_X86_ECX: addr = &mContext->uc_mcontext.gregs[REG_ECX]; break;
		case UNW_X86_EDX: addr = &mContext->uc_mcontext.gregs[REG_EDX]; break;
		case UNW_X86_ESI: addr = &mContext->uc_mcontext.gregs[REG_ESI]; break;
		case UNW_X86_EDI: addr = &mContext->uc_mcontext.gregs[REG_EDI]; break;
		case UNW_X86_EBP: addr = &mContext->uc_mcontext.gregs[REG_EBP]; break;
		case UNW_X86_EIP: addr = &mContext->uc_mcontext.gregs[REG_EIP]; break;
		case UNW_X86_ESP: addr = &mContext->uc_mcontext.gregs[REG_ESP]; break;
		case UNW_X86_TRAPNO:  addr = &mContext->uc_mcontext.gregs[REG_TRAPNO]; break;
		case UNW_X86_CS:  addr = &mContext->uc_mcontext.gregs[REG_CS]; break;
		case UNW_X86_EFLAGS:  addr = &mContext->uc_mcontext.gregs[REG_EFL]; break;
		case UNW_X86_SS:  addr = &mContext->uc_mcontext.gregs[REG_SS]; break;

		default:
		LOGE(TAG,"unknown reg %d", reg);
		addr = NULL;
	}

	if(addr != NULL){
		return *(word_t*)addr;
	}

	return 0;
}

int Context_x86::restoreFrame(){
	if(rootFrame()){
		LOGE(TAG,"restoreFrame failed , it's root frame.");
		return 1;
	}
	
	int ret = 0;
	if((ret = EHFrame::restoreFrame(*this)) != 0){
		LOGE(TAG,"restoreFrame from eh_frame failed, try call routine.")
		if((ret = stepBack(mCfi)) != 0){
			LOGE(TAG,"try call routine failed.");
			return ret;
		}
	}

	--mCfi.ip;
	mCfi.loc[ESP] = mCfi.cfa;

	return ret;
}

int Context_x86::stepBack(CFI& cfi){
	int ret;
	word_t ebp = cfi.loc[EBP];
	if(ebp <= cfi.cfa || ebp - cfi.cfa > 1<<20){
		LOGE(TAG,"wrong ebp(0x%lx)", ebp);
		return 1;
	}

	cfi.loc[EBP] = *(word_t*)(ebp);
	cfi.ip = cfi.loc[EIP] = *(word_t*)(ebp+sizeof(word_t));
	cfi.cfa = cfi.loc[ESP] = ebp + 8;
	cfi.ret_addr_column = EIP;

	LOGD(TAG,"stepBack ebp(0x%lx)", ebp);

	return 0;
}

#endif


================================================
FILE: crashsdk/src/jni/crash/unwind/x86/Context_x86.h
================================================
#ifndef Context_x86_h
#define Context_x86_h

#ifdef __i386__

#include "Context.h"

#define EAX	0
#define ECX	1
#define EDX	2
#define EBX	3
#define ESP	4
#define EBP	5
#define ESI	6
#define EDI	7
#define EIP	8
#define EFLAGS	9
#define TRAPNO	10
#define ST0	11

typedef enum {
	/* Note: general registers are expected to start with index 0.
	This convention facilitates architecture-independent
	implementation of the C++ exception handling ABI.  See
	_Unwind_SetGR() and _Unwind_GetGR() for details.

	The described register usage convention is based on "System V
	Application Binary Interface, Intel386 Architecture Processor
	Supplement, Fourth Edition" at

	http://www.linuxbase.org/spec/refspecs/elf/abi386-4.pdf

	It would have been nice to use the same register numbering as
	DWARF, but that doesn't work because the libunwind requires
	that the exception argument registers be consecutive, which the
	wouldn't be with the DWARF numbering.  */
	UNW_X86_EAX,	/* scratch (exception argument 1) */
	UNW_X86_EDX,	/* scratch (exception argument 2) */
	UNW_X86_ECX,	/* scratch */
	UNW_X86_EBX,	/* preserved */
	UNW_X86_ESI,	/* preserved */
	UNW_X86_EDI,	/* preserved */
	UNW_X86_EBP,	/* (optional) frame-register */
	UNW_X86_ESP,	/* (optional) frame-register */
	UNW_X86_EIP,	/* frame-register */
	UNW_X86_EFLAGS,	/* scratch (except for "direction", which is fixed */
	UNW_X86_TRAPNO,	/* scratch */

	/* MMX/stacked-fp registers */
	UNW_X86_ST0,	/* fp return value */
	UNW_X86_ST1,	/* scratch */
	UNW_X86_ST2,	/* scratch */
	UNW_X86_ST3,	/* scratch */
	UNW_X86_ST4,	/* scratch */
	UNW_X86_ST5,	/* scratch */
	UNW_X86_ST6,	/* scratch */
	UNW_X86_ST7,	/* scratch */

	UNW_X86_FCW,	/* scratch */
	UNW_X86_FSW,	/* scratch */
	UNW_X86_FTW,	/* scratch */
	UNW_X86_FOP,	/* scratch */
	UNW_X86_FCS,	/* scratch */
	UNW_X86_FIP,	/* scratch */
	UNW_X86_FEA,	/* scratch */
	UNW_X86_FDS,	/* scratch */

	/* SSE registers */
	UNW_X86_XMM0_lo,	/* scratch */
	UNW_X86_XMM0_hi,	/* scratch */
	UNW_X86_XMM1_lo,	/* scratch */
	UNW_X86_XMM1_hi,	/* scratch */
	UNW_X86_XMM2_lo,	/* scratch */
	UNW_X86_XMM2_hi,	/* scratch */
	UNW_X86_XMM3_lo,	/* scratch */
	UNW_X86_XMM3_hi,	/* scratch */
	UNW_X86_XMM4_lo,	/* scratch */
	UNW_X86_XMM4_hi,	/* scratch */
	UNW_X86_XMM5_lo,	/* scratch */
	UNW_X86_XMM5_hi,	/* scratch */
	UNW_X86_XMM6_lo,	/* scratch */
	UNW_X86_XMM6_hi,	/* scratch */
	UNW_X86_XMM7_lo,	/* scratch */
	UNW_X86_XMM7_hi,	/* scratch */

	UNW_X86_MXCSR,	/* scratch */

	/* segment registers */
	UNW_X86_GS,		/* special */
	UNW_X86_FS,		/* special */
	UNW_X86_ES,		/* special */
	UNW_X86_DS,		/* special */
	UNW_X86_SS,		/* special */
	UNW_X86_CS,		/* special */
	UNW_X86_TSS,	/* special */
	UNW_X86_LDT,	/* special */

	/* frame info (read-only) */
	UNW_X86_CFA,

	UNW_X86_XMM0,	/* scratch */
	UNW_X86_XMM1,	/* scratch */
	UNW_X86_XMM2,	/* scratch */
	UNW_X86_XMM3,	/* scratch */
	UNW_X86_XMM4,	/* scratch */
	UNW_X86_XMM5,	/* scratch */
	UNW_X86_XMM6,	/* scratch */
	UNW_X86_XMM7,	/* scratch */

	UNW_TDEP_LAST_REG = UNW_X86_XMM7,

	UNW_TDEP_IP = UNW_X86_EIP,
	UNW_TDEP_SP = UNW_X86_ESP,
	UNW_TDEP_EH = UNW_X86_EAX
}x86_regnum_t;

class Context_x86: public Context{

public:
	Context_x86(){
	}

	~Context_x86(){
		mContext = NULL;
	}

	virtual int setupFrame(CFI& cfi);
	virtual word_t getReg(uint32_t reg);
	virtual int restoreFrame();
	virtual int stepBack(CFI& cfi);
	

private:

	static const char* TAG;

};

#endif // __i386__

#endif //Context_x86_h

================================================
FILE: crashsdk/src/jni/utils/Log.h
================================================
#ifndef LOG_C
#define LOG_C

#include <android/log.h>

#ifdef DEBUG
#define LOGE(TAG,fmt...)	Log(0,ANDROID_LOG_ERROR,TAG,fmt);
#define LOGR(TAG,fmt...)	Log(1,ANDROID_LOG_INFO,TAG,fmt);
#define LOGW(TAG,fmt...) 	Log(2,ANDROID_LOG_WARN,TAG,fmt);
#define LOGI(TAG,fmt...)	Log(3,ANDROID_LOG_INFO,TAG,fmt);
#define LOGD(TAG,fmt...)	Log(4,ANDROID_LOG_DEBUG,TAG,fmt);
#define LOGT(TAG,fmt...)	Log(4,ANDROID_LOG_INFO,TAG,fmt);
#else
#define LOGE(TAG,fmt...)	Log(0,ANDROID_LOG_ERROR,TAG,fmt);
#define LOGR(TAG,fmt...)	Log(1,ANDROID_LOG_INFO,TAG,fmt);
#define LOGW(TAG,fmt...) 	//Log(2,ANDROID_LOG_WARN,TAG,fmt);
#define LOGI(TAG,fmt...)	//Log(3,ANDROID_LOG_INFO,TAG,fmt);
#define LOGD(TAG,fmt...)	//Log(4,ANDROID_LOG_DEBUG,TAG,fmt);
#define LOGT(TAG,fmt...)	//Log(4,ANDROID_LOG_INFO,TAG,fmt);
#endif

static void Log(int levl,int androidLevl,const char* TAG,const char* fmt, ...){

	va_list argptr;
    va_start(argptr, fmt);
    __android_log_vprint(androidLevl, TAG, fmt, argptr);
    va_end(argptr);
	
}


#endif


================================================
FILE: crashsdk/src/jni/utils/Utils.cpp
================================================
#include "Utils.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "Log.h"
#include "Defines.h"

template <typename T>
inline int vpnlib::compareArr(T a1[],int l1,T a2[],int l2){
	for(int i=0;i<l1&&i<l2;i++){
		if(a1[i] > a2[i]){
			return 1;
		}else if(a1[i] < a2[i]){
			return -1;
		}
	}

	return l1>l2?1:(l1<l2?-1:0);
}

char* vpnlib::bytes2hex(uint8_t* data,char* out,size_t len){

	for(int i=0;i<len;i++){
		sprintf(&out[i<<1], "%02X", data[i]);
	}

	out[len<<1] = '\0';
	return out;
}

uint8_t vpnlib::char2hex(const char c) {
    if (c >= '0' && c <= '9') return (uint8_t) (c - '0');
    if (c >= 'a' && c <= 'f') return (uint8_t) ((c - 'a') + 10);
    if (c >= 'A' && c <= 'F') return (uint8_t) ((c - 'A') + 10);
    return 255;
}

void vpnlib::hex2bytes(const char *hex, uint8_t *out) {
    size_t len = strlen(hex);
    for (int i = 0; i<len; i+=2){
        out[i>>1] = (char2hex(hex[i]) << 4) | char2hex(hex[i+1]);
    }
    return;
}

void vpnlib::printMem(uint32_t s, uint32_t e){
	while(s <= e){
		LOGD(PTAG(""), "%x: %08x", s, *(uint32_t*)s);
		s += 4;
	}
}


================================================
FILE: crashsdk/src/jni/utils/Utils.h
================================================
#ifndef UTILS_H
#define UTILS_H

#include <stdint.h>
#include <stdio.h>

namespace vpnlib{

	template <typename T>
	inline int compareArr(T a1[],int l1,T a2[],int l2);
	char* bytes2hex(uint8_t* data,char* out,size_t len);

	uint8_t char2hex(const char c);
	void hex2bytes(const char *hex, uint8_t *out);
	int compare_u32(uint32_t s1,uint32_t s2);

	void printMem(uint32_t s, uint32_t e);

	int getUID(uint8_t version, uint8_t protocol, void *saddr,  uint16_t sport,  void *daddr, uint16_t dport);
	
}

#endif

================================================
FILE: crashsdk/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.summer.crashsdk" />


================================================
FILE: crashsdk/src/main/java/com/summer/crashsdk/CrashSDK.java
================================================
package com.summer.crashsdk;

import android.content.Context;
import android.os.Build;
import android.os.Debug;
import android.util.Log;

import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

public final class CrashSDK {

    private static final String TAG = "CrashSDK";

    private static String LOG_DIR;
    private static Date sDate;
    private static SimpleDateFormat sFormat;

    private static Thread.UncaughtExceptionHandler sOldUncaughtExceptionHandler = null;

    /**
     * init crash sdk,
     * @param context
     * @return 0 on success, otherwise failed.
     */
    public static final int init(Context context){

        if(!loadLib()){
            return 1;
        }

        StringBuilder abi = new StringBuilder();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            boolean first = true;
            for(String s:Build.SUPPORTED_ABIS){
                if(!first){
                    abi.append(" ");
                }
                abi.append(s);

                first = false;
            }
        }else{
            abi.append(Build.CPU_ABI);
        }

        setSystemInfo(Build.FINGERPRINT, Build.VERSION.SDK_INT, abi.toString());

        File files = context.getExternalFilesDir(null);
        File dir = new File(files.getAbsolutePath() + File.separator + "tombstones");
        if(!dir.exists()){
            try{
                if(!dir.mkdirs()){
                    Log.e(TAG,"init crash log directory failed");
                }
            }catch (Throwable t){
                t.printStackTrace();
                Log.e(TAG,"init crash log directory failed");
            }
        }

        LOG_DIR = dir.getAbsolutePath();
        setLogDir(dir.getAbsolutePath());

        sDate = new Date();
        sFormat = new SimpleDateFormat("yyyyMMdd_HH_mm_ss");

        initJavaCrashHandler();

        return initNative();
    }

    private static boolean loadLib(){
        try {
            System.loadLibrary("crashsdk21");
        }catch (Throwable t){
            t.printStackTrace();
            try {
                System.loadLibrary("crashsdk");
            }catch (Throwable t2){
                t2.printStackTrace();
                return false;
            }
        }

        return true;
    }

    public static boolean initJavaCrashHandler(){

        sOldUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();

        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {

            @Override
            public void uncaughtException(Thread t, Throwable e) {

                StringBuilder sb = new StringBuilder();
                Debug.MemoryInfo mi = new Debug.MemoryInfo();
                Debug.getMemoryInfo(mi);
                sb.append("Pss: ").append(mi.getTotalPss()).append("\n\r");
                sb.append("Native heap size: ").append(Debug.getNativeHeapSize()).append("\n\r");
                sb.append("Native heap alloced: ").append(Debug.getNativeHeapAllocatedSize()).append("\n\r");
                sb.append("Native head free: ").append(Debug.getNativeHeapFreeSize()).append("\n\r");
                sb.append("FATAL died, thread: ").append((t==null?"":t.getName())).append("\n\r")
                        .append(" reason: ").append(e==null?"":e.getMessage()).append("\n\r")
                        .append(" stack: ").append(Log.getStackTraceString(e));

                Log.e(TAG, sb.toString());

                writeString2File(getLogFilePath(), sb.toString());

                if(sOldUncaughtExceptionHandler != null){
                    sOldUncaughtExceptionHandler.uncaughtException(t, e);
                }
            }
        });

        return true;
    }

    private static final String getLogFilePath() {
        sDate.setTime(System.currentTimeMillis());
        return LOG_DIR + (LOG_DIR.endsWith(File.separator)?"":File.separator) + sFormat.format(sDate) + ".crash";
    }

    private static final boolean writeString2File(String targetPath,String content){
        Log.d(TAG,"write string: " + targetPath + " " + content);
        PrintWriter out = null;
        BufferedWriter bw = null;
        FileWriter fw = null;
        try {
            fw = new FileWriter(targetPath, true);
            bw = new BufferedWriter(fw);
            out = new PrintWriter(bw);
            out.print(content);
            out.flush();
            return true;
        }catch (Exception e) {
            Log.d(TAG,"write content failed: ");
            e.printStackTrace();
        }finally {
            safeClose(out);
            safeClose(bw);
            safeClose(fw);
        }

        return false;
    }

    public static final void safeClose(Closeable i){
        if(i != null) {
            try {
                i.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * call from native
     * get the path where crash info saved
     * @return
     */
    public static String getTombstonesDirectory(){
        return LOG_DIR;
    }

    public static void doCrash(int id){
        test(id);
    }

    private static native int initNative();
    private static native void setSystemInfo(String fingerprint, int version, String supportedABI);
    private static native void setLogDir(String dir);

    private static native int test(int id);

}


================================================
FILE: crashsdk/src/main/res/values/strings.xml
================================================
<resources>
    <string name="app_name">CrashSDK</string>
</resources>


================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.

# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.

# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html

# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m

# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true


================================================
FILE: settings.gradle
================================================
include ':app', ':crashsdk'
gitextract_avj4b6vk/

├── .gitignore
├── LICENSE
├── README.md
├── app/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   ├── release/
│   │   ├── Demo.apk
│   │   └── output.json
│   └── src/
│       ├── .gitignore
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── summer/
│           │           └── demo/
│           │               └── crashsdkdemo/
│           │                   ├── CrashInfoWindow.java
│           │                   ├── CrashRecordWindow.java
│           │                   ├── ExceptionHandler.java
│           │                   ├── FileUtils.java
│           │                   ├── IOUtils.java
│           │                   └── MainActivity.java
│           └── res/
│               ├── drawable/
│               │   └── ic_launcher_background.xml
│               ├── drawable-v24/
│               │   └── ic_launcher_foreground.xml
│               ├── mipmap-anydpi-v26/
│               │   ├── ic_launcher.xml
│               │   └── ic_launcher_round.xml
│               └── values/
│                   ├── colors.xml
│                   ├── strings.xml
│                   └── styles.xml
├── build.gradle
├── crashsdk/
│   ├── .gitignore
│   ├── build.gradle
│   ├── proguard-rules.pro
│   └── src/
│       ├── .gitignore
│       ├── jni/
│       │   ├── Android.mk
│       │   ├── Application.mk
│       │   ├── Application_21.mk
│       │   ├── CrashSDK.cpp
│       │   ├── Test.cpp
│       │   ├── crash/
│       │   │   ├── CrashMonitor.cpp
│       │   │   ├── CrashMonitor.h
│       │   │   ├── Defines.h
│       │   │   └── unwind/
│       │   │       ├── Arch.h
│       │   │       ├── Context.cpp
│       │   │       ├── Context.h
│       │   │       ├── DataTypes.h
│       │   │       ├── _Unwind.cpp
│       │   │       ├── _Unwind.h
│       │   │       ├── arm/
│       │   │       │   ├── Config_arm.h
│       │   │       │   ├── Context_arm.cpp
│       │   │       │   ├── Context_arm.h
│       │   │       │   ├── Exidx.cpp
│       │   │       │   └── Exidx.h
│       │   │       ├── eh_frame/
│       │   │       │   ├── DWExpression.cpp
│       │   │       │   ├── DWExpression.h
│       │   │       │   ├── DWInstruction.cpp
│       │   │       │   ├── DWInstruction.h
│       │   │       │   ├── EHFrame.cpp
│       │   │       │   └── EHFrame.h
│       │   │       └── x86/
│       │   │           ├── Config_x86.h
│       │   │           ├── Context_x86.cpp
│       │   │           └── Context_x86.h
│       │   └── utils/
│       │       ├── Log.h
│       │       ├── Utils.cpp
│       │       └── Utils.h
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── com/
│           │       └── summer/
│           │           └── crashsdk/
│           │               └── CrashSDK.java
│           └── res/
│               └── values/
│                   └── strings.xml
├── gradle.properties
└── settings.gradle
Condensed preview — 63 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (183K chars).
[
  {
    "path": ".gitignore",
    "chars": 155,
    "preview": "*.iml\n.idea\n.gradle\n/gradlew.bat\n/gradlew\n/gradle/\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/bu..."
  },
  {
    "path": "LICENSE",
    "chars": 18025,
    "preview": "GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundati..."
  },
  {
    "path": "README.md",
    "chars": 2530,
    "preview": "# What it for\n\n  Catch crashes in Java & Native(arm/x86), and dump crash information to a file located in sdcard, crash..."
  },
  {
    "path": "app/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "chars": 836,
    "preview": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 26\n    defaultConfig {\n        applicationId \"c..."
  },
  {
    "path": "app/proguard-rules.pro",
    "chars": 751,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar..."
  },
  {
    "path": "app/release/output.json",
    "chars": 200,
    "preview": "[{\"outputType\":{\"type\":\"APK\"},\"apkInfo\":{\"type\":\"MAIN\",\"splits\":[],\"versionCode\":1},\"path\":\"app-release.apk\",\"properties..."
  },
  {
    "path": "app/src/.gitignore",
    "chars": 19,
    "preview": "/androidTest\n/test\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 882,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=..."
  },
  {
    "path": "app/src/main/java/com/summer/demo/crashsdkdemo/CrashInfoWindow.java",
    "chars": 2114,
    "preview": "package com.summer.demo.crashsdkdemo;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.os...."
  },
  {
    "path": "app/src/main/java/com/summer/demo/crashsdkdemo/CrashRecordWindow.java",
    "chars": 4208,
    "preview": "package com.summer.demo.crashsdkdemo;\n\nimport android.content.Context;\nimport android.os.Handler;\nimport android.os.Loop..."
  },
  {
    "path": "app/src/main/java/com/summer/demo/crashsdkdemo/ExceptionHandler.java",
    "chars": 214,
    "preview": "package com.summer.demo.crashsdkdemo;\n\n/**\n * Created by summer on 14/06/2018.\n */\n\npublic class ExceptionHandler {..."
  },
  {
    "path": "app/src/main/java/com/summer/demo/crashsdkdemo/FileUtils.java",
    "chars": 916,
    "preview": "package com.summer.demo.crashsdkdemo;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nim..."
  },
  {
    "path": "app/src/main/java/com/summer/demo/crashsdkdemo/IOUtils.java",
    "chars": 411,
    "preview": "package com.summer.demo.crashsdkdemo;\n\nimport java.io.Closeable;\nimport java.io.IOException;\n\n/**\n * Created by summer o..."
  },
  {
    "path": "app/src/main/java/com/summer/demo/crashsdkdemo/MainActivity.java",
    "chars": 5222,
    "preview": "package com.summer.demo.crashsdkdemo;\n\nimport android.app.Activity;\nimport android.graphics.Color;\nimport android.os.Bun..."
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "chars": 5606,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:wi..."
  },
  {
    "path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "chars": 1880,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"..."
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "chars": 272,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b..."
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "chars": 272,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b..."
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "chars": 208,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"color..."
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "chars": 68,
    "preview": "<resources>\n    <string name=\"app_name\">Crash</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "chars": 197,
    "preview": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"android:Theme.Holo.Light.DarkAction..."
  },
  {
    "path": "build.gradle",
    "chars": 546,
    "preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {..."
  },
  {
    "path": "crashsdk/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "crashsdk/build.gradle",
    "chars": 551,
    "preview": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 26\n\n    sourceSets {\n        main {\n            jni..."
  },
  {
    "path": "crashsdk/proguard-rules.pro",
    "chars": 793,
    "preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar..."
  },
  {
    "path": "crashsdk/src/.gitignore",
    "chars": 24,
    "preview": "/test\n/androidTest\n/obj\n"
  },
  {
    "path": "crashsdk/src/jni/Android.mk",
    "chars": 1516,
    "preview": "LOCAL_PATH := $(call my-dir)\n\ninclude $(CLEAR_VARS)\n\nifeq ($(TARGET_PLATFORM), android-14)\n    LOCAL_MODULE    := crashs..."
  },
  {
    "path": "crashsdk/src/jni/Application.mk",
    "chars": 78,
    "preview": "APP_PLATFORM = android-14\nAPP_ABI := armeabi-v7a x86\nAPP_STL := gnustl_static\n"
  },
  {
    "path": "crashsdk/src/jni/Application_21.mk",
    "chars": 74,
    "preview": "APP_PLATFORM = android-21\nAPP_ABI := armeabi-v7a\nAPP_STL := gnustl_static\n"
  },
  {
    "path": "crashsdk/src/jni/CrashSDK.cpp",
    "chars": 1160,
    "preview": "//\n// Created by Summer on 09/06/2018.\n//\n\n#include <jni.h>\n#include \"Defines.h\"\n#include \"Log.h\"\n#include \"CrashMonitor..."
  },
  {
    "path": "crashsdk/src/jni/Test.cpp",
    "chars": 1928,
    "preview": "//\n// Created by Summer on 09/06/2018.\n//\n\n#ifndef CRASHSDKDEMO_TEST_H\n#define CRASHSDKDEMO_TEST_H\n\n#include <jni.h>\n#in..."
  },
  {
    "path": "crashsdk/src/jni/crash/CrashMonitor.cpp",
    "chars": 10936,
    "preview": "#include \"CrashMonitor.h\"\n#include \"Defines.h\"\n#include \"Log.h\"\n#include <cxxabi.h>\n#include <ucontext.h>\n#include <sys/..."
  },
  {
    "path": "crashsdk/src/jni/crash/CrashMonitor.h",
    "chars": 1431,
    "preview": "#ifndef CRASH_MONITOR_H\n#define CRASH_MONITOR_H\n\n#include <jni.h>\n#include <signal.h>\n#include <unwind.h>\n#include <dlfc..."
  },
  {
    "path": "crashsdk/src/jni/crash/Defines.h",
    "chars": 320,
    "preview": "#ifndef DEFINES_H\n#define DEFINES_H\n\n#define PTAG(tag) \"CrashSDK.\" tag\n\n#define SAFE_FREE(p) if(p != NULL) {free(p);p=NU..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/Arch.h",
    "chars": 565,
    "preview": "#ifndef Unwind_arch_h\n#define Unwind_arch_h\n\n#if defined __aarch64__\n#define ARCH_NAME \"__aarch64__\"\n#elif defined __arm..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/Context.cpp",
    "chars": 3449,
    "preview": "#include \"Arch.h\"\n#include \"Context.h\"\n#include \"Log.h\"\n#include \"EHFrame.h\"\n\nconst char* Context::TAG = PTAG(\"Context\")..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/Context.h",
    "chars": 1689,
    "preview": "#ifndef Context_h\n#define Context_h\n\n#include \"Defines.h\"\n#include \"Arch.h\"\n#include \"DataTypes.h\"\n#include <ucontext.h>..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/DataTypes.h",
    "chars": 3461,
    "preview": "#ifndef DataTypes_h\n#define DataTypes_h\n\n#include \"Arch.h\"\n#include <stdint.h>\n\ntypedef struct table_entry{\n\tint32_t sta..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/_Unwind.cpp",
    "chars": 1879,
    "preview": "#include \"_Unwind.h\"\n#include \"Log.h\"\n#include \"EHFrame.h\"\n\n\n#define MAX_STACK_DEEP 200\n\nconst char* Unwind::TAG = PTAG(..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/_Unwind.h",
    "chars": 697,
    "preview": "#ifndef _UNWIND_H\n#define _UNWIND_H\n\n#include \"Arch.h\"\n\ntypedef struct RegState{\n\tconst char* regName;\n\tvoid* val;\n}RegS..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/arm/Config_arm.h",
    "chars": 320,
    "preview": "#ifndef CONFIG_arm_H\n#define CONFIG_arm_H\n\n#ifdef __arm__\n\n#include<stdint.h>\n\n/* This matches the value used by GCC (se..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/arm/Context_arm.cpp",
    "chars": 3608,
    "preview": "#include \"Context_arm.h\"\n#include \"Log.h\"\n#include \"Utils.h\"\n#if __ANDROID_API__ >= 21\n#include \"EHFrame.h\"\n#endif\n#incl..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/arm/Context_arm.h",
    "chars": 830,
    "preview": "#ifndef Context_arm_h\n#define Context_arm_h\n\n#include \"Context.h\"\n#include \"Exidx.h\"\n\ntypedef enum{\n\tUNW_ARM_R0,\n\tUNW_AR..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/arm/Exidx.cpp",
    "chars": 12317,
    "preview": "#include \"Exidx.h\"\n#include \"Log.h\"\n#include \"Defines.h\"\n#include <link.h>\n\nconst char* Exidx::TAG = PTAG(\"Exidx\");\n\nint..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/arm/Exidx.h",
    "chars": 1321,
    "preview": "#ifndef Exidx_h\n#define Exidx_h\n\n#include \"Context.h\"\n\n#define ARM_EXBUF_START(x)\t(((x) >> 4) & 0x0f)\n#define ARM_EXBUF_..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/eh_frame/DWExpression.cpp",
    "chars": 19807,
    "preview": "#include \"DWExpression.h\"\n#include \"Defines.h\"\n#include \"Log.h\"\n\n/* The \"pick\" operator provides an index range of 0..25..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/eh_frame/DWExpression.h",
    "chars": 513,
    "preview": "#ifndef DWExpression_h\n#define DWExpression_h\n\n#include \"Context.h\"\n#include \"EHFrame.h\"\n\nclass DWExpression{\n\npublic:..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/eh_frame/DWInstruction.cpp",
    "chars": 11092,
    "preview": "#include \"DWInstruction.h\"\n#include \"Defines.h\"\n#include \"Log.h\"\n\n#define DWARF_CFA_OPCODE_MASK 0xc0\n#define DWARF_CFA_O..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/eh_frame/DWInstruction.h",
    "chars": 381,
    "preview": "#ifndef DWInstruction_H\n#define DWInstruction_H\n\n#include \"Context.h\"\n#include \"EHFrame.h\"\n\nclass DWInstruction{\n\npublic..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/eh_frame/EHFrame.cpp",
    "chars": 20037,
    "preview": "#include \"EHFrame.h\"\n#include \"Defines.h\"\n#include \"Log.h\"\n#include \"DWInstruction.h\"\n#include \"DWExpression.h\"\n#include..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/eh_frame/EHFrame.h",
    "chars": 2558,
    "preview": "#ifndef EHFrame_H\n#define EHFrame_H\n\n#include \"Context.h\"\n\n#define EH_VERSION 1\n\n/* DWARF Pointer-Encoding (PEs).\n\n   Po..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/x86/Config_x86.h",
    "chars": 320,
    "preview": "#ifndef CONFIG_x86_H\n#define CONFIG_x86_H\n\n#ifdef __i386__\n\n#include<stdint.h>\n\n/* This matches the value used by GCC (s..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/x86/Context_x86.cpp",
    "chars": 3036,
    "preview": "#include \"Context_x86.h\"\n#include \"Log.h\"\n#include \"Defines.h\"\n#include \"EHFrame.h\"\n\n#ifdef __i386__\n\nconst char* Contex..."
  },
  {
    "path": "crashsdk/src/jni/crash/unwind/x86/Context_x86.h",
    "chars": 3442,
    "preview": "#ifndef Context_x86_h\n#define Context_x86_h\n\n#ifdef __i386__\n\n#include \"Context.h\"\n\n#define EAX\t0\n#define ECX\t1\n#define..."
  },
  {
    "path": "crashsdk/src/jni/utils/Log.h",
    "chars": 1007,
    "preview": "#ifndef LOG_C\n#define LOG_C\n\n#include <android/log.h>\n\n#ifdef DEBUG\n#define LOGE(TAG,fmt...)\tLog(0,ANDROID_LOG_ERROR,TAG..."
  },
  {
    "path": "crashsdk/src/jni/utils/Utils.cpp",
    "chars": 1094,
    "preview": "#include \"Utils.h\"\n#include <string.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include \"Log.h\"\n#include \"Defines.h\"\n\ntem..."
  },
  {
    "path": "crashsdk/src/jni/utils/Utils.h",
    "chars": 508,
    "preview": "#ifndef UTILS_H\n#define UTILS_H\n\n#include <stdint.h>\n#include <stdio.h>\n\nnamespace vpnlib{\n\n\ttemplate <typename T>\n\tinli..."
  },
  {
    "path": "crashsdk/src/main/AndroidManifest.xml",
    "chars": 106,
    "preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.summer.crashsdk\" />\n"
  },
  {
    "path": "crashsdk/src/main/java/com/summer/crashsdk/CrashSDK.java",
    "chars": 5577,
    "preview": "package com.summer.crashsdk;\n\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.Debug;\nimport a..."
  },
  {
    "path": "crashsdk/src/main/res/values/strings.xml",
    "chars": 71,
    "preview": "<resources>\n    <string name=\"app_name\">CrashSDK</string>\n</resources>\n"
  },
  {
    "path": "gradle.properties",
    "chars": 730,
    "preview": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will o..."
  },
  {
    "path": "settings.gradle",
    "chars": 28,
    "preview": "include ':app', ':crashsdk'\n"
  }
]

About this extraction

This page contains the full source code of the SummerOak/CrashSDK GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 64 files (160.9 KB), approximately 53.1k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!