btrace(AKA RheaTrace) is a high performance Android trace tool which is based on Systrace

Overview

btrace

README 中文版

btrace(AKA RheaTrace) is a high performance Android trace tool which is based on Systrace, it support to define custom events automatically during building apk and using bhook to provider more native events like IO.

Key features

Support to define custom events automatically, inject Trace#beginSection(String) and Trace#endSection() into app methods during building apk.

Provider extra native events like IO to determine time-consuming reasons for methods conveniently.

Support to only capture trace events of main thread on demand.

Easily use, high stability, lower latency than systrace.

Getting started

Import rhea-gradle-plugin as a dependency in your main build.gradle in the root of your project.

buildscript {
    repositories {
        ...
        mavenCentral()
        ...
    }
    dependencies {
        classpath 'com.bytedance.btrace:rhea-gradle-plugin:1.0.1'
    }
}

allprojects {
    repositories {
        ...
        mavenCentral()
        ...
    }
}

Then "apply" the plugin and import dependencies by adding the following lines to your app/build.gradle.

dependencies {
    //rheatrace core lib
    implementation "com.bytedance.btrace:rhea-core:1.0.1"
}
...
rheaTrace {

   compilation {
      //for reducing apk size, you can use method id to trace custom events, default false.
      traceWithMethodID = false 
      //decide which methods you don't want trace, default null.
      traceFilterFilePath = "${project.rootDir}/rhea-trace/traceFilter.txt"
      //keep name of custom event with specific method id, default null.
      applyMethodMappingFilePath = "${project.rootDir}/rhea-trace/keep-method-id.txt"
  }

   runtime {
      //capture trace events of main thread only, default false.
      mainThreadOnly true 
      //start to capture trace events when launch app, default true.
      startWhenAppLaunch true
      //set a specified size of ring buffer which used to store atrace data in memory.
      atraceBufferSize "500000"
   }
}
...
apply plugin: 'com.bytedance.rhea-trace'

About rheaTrace , Learn more from the RheaTrace Gradle Config.

At last, check python version in your computer, RheaTrace only support python 2.7 because of systrace, and then config systrace environment into ~/.bash_profile file.

export PATH=${PATH}:/Users/${user_name}/Library/Android/sdk/platform-tools/systrace

Usage

Open terminal, change directory to RheaTrace/scripts/python/rheatrace, execute command below.

python rheatrace.py -v

Then you will see output below.

Current version is 1.0.1.

RheaTrace retains the Systrace command parameters, if you have used Systrace before, you will quickly learn how to use RheaTrace.

Syntax

To generate the HTML report for app, you need to run rheatrace from the command line using the following syntax:

python rheatrace.py [options] [categories]

For example, the following command calls rheatrace to record device activity and generate a HTML report named mynewtrace.html. This list of categories is a reasonable default list for most devices.

python rheatrace.py -a rhea.sample.android -t 5 -o ./output/mynewtrace.html sched freq idle am wm gfx view binder_driver hal dalvik camera input res

Global options

Global options Description
-h | --help Show the help message.
-l | --list-categories Lists the tracing categories available to your connected device.

Commands and command options

Commands and options Description
-o file Write the HTML trace report to the specified file. If you don't specify this option, rheatrace saves your report to the same directory as rheatrace.py and names it systrace.html.
-t N | --time=N Trace device activity for N seconds. If you don't specify this option, rheatrace prompts you to end the trace by pressing the Enter key from the command line.
-b N | --buf-size=N Use a trace buffer size of N kilobytes. This option lets you limit the total size of the data collected during a trace.
-k functions|--ktrace=functions Trace the activity of specific kernel functions, specified in a comma-separated list.
-a app-name|--app=app-name Enable tracing for specified app, this option must be set. The app must contain tracing instrumentation calls from the Trace class. You should specify this option whenever you profile your app—many libraries, such as RecyclerView, include tracing instrumentation calls that provide useful information when you enable app-level tracing.
--from-file=file-path Create an interactive HTML report from a file, such as TXT files that include raw trace data, instead of running a live trace.
-e device-serial|--serial=device-serial Conduct the trace on a specific connected device, identified by its device serial number.
categories Include tracing information for the system processes you specify, such as gfx for system processes that render graphics. You can run rheatrace with the -l command to see a list of services available to your connected device.

Extensive options

Global options Description
-r | --restart Before tracing app activities, force to stop the app and restart it.
-v | --version Check the current rheatrace version.
--advanced-sys-time | --advanced-sys-time How long in advance to open systrace, default value is 2 seconds.
--debug | --debug Whether to enable debug log output.

Easter Egg Feature

RheaTrace provides Gradle compilation configuration to change the tracking behavior of the App when it is running, but it needs to be repackaged every time, which work low efficiency. So we provide file configuration ways to change tracking behavior.

Create a Properties format file named rheatrace.config, the configurations that can be written are:

io=true
classLoad=true
memory=true
mainThreadOnly=true
atraceBufferSize=100000
startWhenAppLaunch=true

Then, push the file to the device directory sdcard/rhea-trace/${application package name}, and then restart the application to take effect.

options defaults description
io true Whether to enable tracking io native related methods.
classLoad fasle Whether to enable tracking class loading events, only supports Android 8.0 and above, and the app build type is debugable
memory fasle Whether to enable tracking memory access events, only supports Android 8.0 and above, and the app build type is debugable.
mainThreadOnly fasle Whether to capture trace events only on the main thread, if you only care about the main thread trace events, please set it to true.
atraceBufferSize 100000 Specify the size of the ring buffer to store atrace data in the memory. If its value is too small, the trace data will be incompletely written. If you capture multi-threaded trace data, it is recommended to set the value to about one million; the minimum value is 10,000. The maximum value is 5 million.
startWhenAppLaunch true Start to capture tracking events at the beginning of the app startup. If you do startup optimization, it is recommended to keep the value as true.

Known Issues

  1. RheaTrace only support python2.7, please check your python environment.
  2. RheaTrace does not currently support Windows.
  3. RheaTrace only support capture main process trace events.
  4. RheaTrace need read and write permissions of external storage, so you should grant the permission manually.
  5. if you can't open output file systrace.html directly, please using perfetto to load it.

Support

  1. Communicate on GitHub issues.
  2. Study the source code.
  3. Check wiki or FAQ for help.
  4. Contact us [email protected].
  5. Join QQ group chat.

Contributing

Contributing Guide

License

Apache License

Comments
  • Can't find merged manifest!

    Can't find merged manifest!

    andrid studio fox classpath 'com.android.tools.build:gradle:3.0.1' distributionUrl=https://services.gradle.org/distributions/gradle-4.1-bin.zip

    
    :app:javaPreCompileDebug UP-TO-DATE
    :app:compileDebugJavaWithJavac UP-TO-DATE
    :app:compileDebugNdk NO-SOURCE
    :app:compileDebugSources UP-TO-DATE
    :app:mergeDebugShaders UP-TO-DATE
    :app:compileDebugShaders UP-TO-DATE
    :app:generateDebugAssets UP-TO-DATE
    :app:mergeDebugAssets UP-TO-DATE
    :app:extractTryWithResourcesSupportJarDebug UP-TO-DATE
    :app:transformClassesWithStackFramesFixerForDebug UP-TO-DATE
    :app:transformClassesWithDesugarForDebug UP-TO-DATE
    :app:transformClassesWithRheaTraceForDebug FAILED
    :app:mergeDebugJniLibFolders UP-TO-DATE
    :app:transformNativeLibsWithMergeJniLibsForDebug UP-TO-DATE
    :app:processDebugJavaRes NO-SOURCE
    :app:transformResourcesWithMergeJavaResForDebug UP-TO-DATE
    :app:validateSigningDebug
    
    FAILURE: Build failed with an exception.
    
    * What went wrong:
    Execution failed for task ':app:transformClassesWithRheaTraceForDebug'.
    > Can't find merged manifest!
    
    * Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
    
    * Get more help at https://help.gradle.org
    
    BUILD FAILED in 0s
    

    不支持低版本插件?

    opened by ff-frida 2
  • Gradle project sync failed

    Gradle project sync failed

    I have git cloned btrace and open it using Android Studio, Gradle 6.7.1. Then Gradle project sync failed with this message:

    A problem occurred configuring project ':app'. Could not create task ':app:minifyReleaseWithProguard'. Cannot query the value of this provider because it has no value available.

    What should I do to sync btrace with Gradle files successfully?

    opened by Bingist 2
  •  执行脚本报错:app 'rhea.sample.android' is not installed

    执行脚本报错:app 'rhea.sample.android' is not installed

    python rheatrace.py -a rhea.sample.android -t 5 -o ./output/mynewtrace.html sched freq idle am wm gfx view binder_driver hal dalvik camera input
    2022-03-07 11:06:20,896 - rhea_trace - main_impl - WARNING: - app 'rhea.sample.android' is not installed, please check your inputs.

    opened by liruchun333 1
  • Can't find systrace in environment path

    Can't find systrace in environment path

    如图 正常-v可以用 但是第二天命令就不行了 是什么原因呢? 环境:Mac m1 Python 2.7.18 (default, Oct 2 2021, 04:20:38)

    mmb@mmbdeMac-mini rheatrace % python rheatrace.py -v Current version is 1.0.1. mmb@mmbdeMac-mini rheatrace % python rheatrace.py -a rhea.sample.android -t 5 -o ./output/mynewtrace.html sched freq Can't find systrace in environment path

    如下systrace 也是正常使用 python systrace.py --time=10 -o mynewtrace.html gfx

    opened by ivalimmb 1
  • At btrace Demo executed:

    At btrace Demo executed:

    Terminal print Error:

    (TimeoutThread-2-for-MainThread) Exception on WriteFile(192.168.3.239:5555, /d/tracing/events/android_fs/enable, 1, retries=3, timeout=30), attempt 2 of 4: AdbShellCommandFailedError("(device: 192.168.3.239:5555) shell command run via adb failed on the device:\n command: echo -n 1 > /d/tracing/events/android_fs/enable\n exit status: 1\n output:\n - /system/bin/sh: can't create /d/tracing/events/android_fs/enable: No such file or directory\n",)

    opened by 13120241790 1
  • 增加只转译 atrace 的命令

    增加只转译 atrace 的命令

    增加只转译 atrace 的命令。 python rheatrace.py --only-decode-atrace -a rhea.sample.android 最后会生成可使用 perfetto 解析的 atrace-standard 文件。

    之前的 rheatrace 脚本只支持带有 systrace 的基础上才能进行转换。

    opened by lcmoxiao 0
  • compatible with the version without special handling of throw events

    compatible with the version without special handling of throw events

    在一些程序中,会有一些代码在try cath逻辑中抛出异常,这是很常见的写法,比如由一个观察者统一处理异常,如下图: In some programs, there will be some code throwing exceptions in the try cath logic. This is a very common way of writing. For example, an observer handles exceptions uniformly, as shown in the following figure:

    private fun methodB(flag: Boolean): Boolean {
            val start = System.currentTimeMillis()
            try {
                var tmp: Long = 0
                for (i in 0 until 200 * 1000 * 1000) {
                    tmp += i
                }
                throw Exception()
            } catch (e: Exception) {
                e.printStackTrace()
                if (flag) {
                    var tmp: Long = 0
                    var i = 0
                    while (i < 200 * 1000 * 1000) {
                        tmp += i
                        i++
                    }
                    methodC()
                    Log.i(
                        TAG, String.format(
                            "methodB throw duration %s",
                            System.currentTimeMillis() - start
                        )
                    )
                    return false
                }
            }
            Log.i(
                TAG, String.format(
                    "methodB duration %s",
                    System.currentTimeMillis() - start
                )
            )
            return true
        }
    

    这会造成重复的对方法结束点插桩: This will cause repeated instrumentation of the end point of the method: image

    这样TraceView无法判断哪一个End事件为方法的结束点,造成方法识别错误: In this way, TraceView cannot determine which End event is the end point of the method, resulting in a method identification error: image 通过一个简单的算法可以解决这样的问题,保证保留的End事件是最后一个End事件,方法的Trace数据完整性是符合预期的: A simple algorithm can solve this problem, to ensure that the retained End event is the last End event, and the integrity of the trace data of the method is in line with expectations: image

    我的Demo程序是这样的,大家可以参考实验,在onWindowFocusChanged中(真正开发的时候可不能在主线程这么搞事情哟😄)执行了一个耗时方法,嵌套了两次方法B执行: My demo program is like this. You can refer to the experiment. In onWindowFocusChanged (you can't do this in the main thread when you are really developing 😄), a time-consuming method is executed, and the execution of method B is nested twice:

    override fun onWindowFocusChanged(hasFocus: Boolean) {
            DemoMethods.methodA()
            super.onWindowFocusChanged(hasFocus)
        }
    
    package rhea.sample.android.app
    
    import android.util.Log
    import java.lang.Exception
    
    object DemoMethods {
        private const val TAG = "DemoMethods"
        fun methodA() {
            val start = System.currentTimeMillis()
            var tmp: Long = 0
            for (i in 0 until 200 * 1000 * 1000) {
                tmp += i
            }
            methodB(false)
            methodB(true)
            var tmp2: Long = 0
            for (i in 0 until 200 * 1000 * 1000) {
                tmp2 += i
            }
            Log.i(
                TAG, String.format(
                    "methodA duration %s",
                    System.currentTimeMillis() - start
                )
            )
        }
    
        private fun methodB(flag: Boolean): Boolean {
            val start = System.currentTimeMillis()
            try {
                var tmp: Long = 0
                for (i in 0 until 200 * 1000 * 1000) {
                    tmp += i
                }
                throw Exception()
            } catch (e: Exception) {
                e.printStackTrace()
                if (flag) {
                    var tmp: Long = 0
                    var i = 0
                    while (i < 200 * 1000 * 1000) {
                        tmp += i
                        i++
                    }
                    methodC()
                    Log.i(
                        TAG, String.format(
                            "methodB throw duration %s",
                            System.currentTimeMillis() - start
                        )
                    )
                    return false
                }
            }
            Log.i(
                TAG, String.format(
                    "methodB duration %s",
                    System.currentTimeMillis() - start
                )
            )
            return true
        }
    
        private fun methodC() {
            val start = System.currentTimeMillis()
            var tmp: Long = 0
            for (i in 0 until 200 * 1000 * 1000) {
                tmp += i
            }
            Log.i(
                TAG, String.format(
                    "methodC duration %s",
                    System.currentTimeMillis() - start
                )
            )
        }
    }
    
    opened by qkmaosjtu 0
  • 为什么插桩的时候,开始和结束都是beginSection

    为什么插桩的时候,开始和结束都是beginSection

     /**
         * the method would be injected in the enter of app methods.
         *
         * @param methodId method id.
         */
        public static void i(String methodId) {
            RheaTrace.t(methodId);
        }
    
        /**
         * the method would be injected in the exits of app methods.
         *
         * @param methodId method id.
         */
        public static void o(String methodId) {
            RheaTrace.t(methodId);
        }
    
    static void t(String methodId) {
            if (!isMainProcess) {
                return;
            }
            if (mainThreadOnly) {
                if (Thread.currentThread() == sMainThread) {
                    Trace.beginSection(methodId);
                }
            } else {
                Trace.beginSection(methodId);
            }
        }
    
    opened by JuneLeo 1
  • Windows & RheaTrace 踩坑日记

    Windows & RheaTrace 踩坑日记

    写了一点关于 Windows 和 BTrace 的踩坑日记,希望可以帮助大家少走一些弯路。

    1. 运行 rheatrace.py 报错: IOError: [Errno 22] invalid mode ('a') or filename:

    错误原因: Windows 中文件名不能含有 : 等特殊字符

    rhea_logger.py 文件中

    fh = logging.FileHandler(r'{0}/_log_{1}.log'.format(output_dir, time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime()), encoding='utf-8'))
    

    修改为

    fh = logging.FileHandler(r'{0}/_log_{1}.log'.format(output_dir, str(int(time.time())), encoding='utf-8'))
    

    2. 运行 rheatrace.py 过程中, 调用 systrace 报错: subprocess.Popen WindowsError: [Error 2]

    报错原因: Windows 安装 Python 2.7 之后的可执行文件名为 python.exe 而不是 python2.7.exe

    systrace_capturer.py 文件中

    cmd = ["python2.7", systrace_path]
    

    修改为

    cmd = ["python", systrace_path]
    

    P.S: 如果你有Python 3 可以将 Python 2.7 的可执行文件改名为 python2.exe 然后将以上命令修改为

    cmd = ["python2", systrace_path]
    

    3. 运行过程中提示 Unfortunately, start trace failed 或者 failed to write rhea-atrace.gz file completely

    报错原因: 无法创建文件和文件夹 (无权限)

    在 app 目录下的 **AndroidManifest.xml`` 中声明以下权限:

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

    然后通过权限请求库例如 XXPermissions 进行动态申请:

    XXPermissions.with(this)
        .permission(Manifest.permission.MANAGE_EXTERNAL_STORAGE)
        .request(new OnPermissionCallback() {
            @Override
            public void onGranted(@NonNull List<String> permissions, boolean all) {
                
            }
        });
    

    重新打开软件后 Toast 消失, 运行 rheatrace.py 无错误提示

    4. 运行 rheatrace.py 过程中, 调用 gzip 报错: subprocess.Popen WindowsError: [Error 2]

    报错原因: Windows 下没有 gzip 程序

    Gzip for Windows 网页 下载 gzip 并添加到 PATH 环境变量后重新打开命令行运行 rheatrace.py 即可

    opened by HenryWu01 0
  • 运行python报错 failed to capture systrace, check your inputs firstly

    运行python报错 failed to capture systrace, check your inputs firstly

    经调试,实际报错信息如下:

    Traceback (most recent call last):
      File "/Users/apple/Library/Android/sdk/platform-tools/systrace/systrace.py", line 48, in <module>
        from systrace import run_systrace
      File "/Users/apple/Library/Android/sdk/platform-tools/systrace/catapult/systrace/systrace/run_systrace.py", line 42, in <module>
        from systrace import systrace_runner
      File "/Users/apple/Library/Android/sdk/platform-tools/systrace/catapult/systrace/systrace/systrace_runner.py", line 16, in <module>
        from systrace.tracing_agents import battor_trace_agent
      File "/Users/apple/Library/Android/sdk/platform-tools/systrace/catapult/systrace/systrace/tracing_agents/battor_trace_agent.py", line 10, in <module>
        from battor import battor_wrapper
      File "/Users/apple/Library/Android/sdk/platform-tools/systrace/catapult/common/battor/battor/battor_wrapper.py", line 24, in <module>
        from serial.tools import list_ports
      File "/Users/apple/Library/Android/sdk/platform-tools/systrace/catapult/third_party/pyserial/serial/tools/list_ports.py", line 29, in <module>
        from serial.tools.list_ports_posix import *
      File "/Users/apple/Library/Android/sdk/platform-tools/systrace/catapult/third_party/pyserial/serial/tools/list_ports_posix.py", line 50, in <module>
        from serial.tools.list_ports_osx import comports
      File "/Users/apple/Library/Android/sdk/platform-tools/systrace/catapult/third_party/pyserial/serial/tools/list_ports_osx.py", line 31, in <module>
        kIOMasterPortDefault = ctypes.c_void_p.in_dll(iokit, "kIOMasterPortDefault")
    ValueError: dlsym(RTLD_DEFAULT, kIOMasterPortDefault): symbol not found
    
    opened by aheadlcx 0
  • Demo运行报错.   unfortunately, stop trace failed!

    Demo运行报错. unfortunately, stop trace failed!

    2022-10-20 11:24:31.519 2729-2773/rhea.sample.android E/BehaviorCollectManager: Fail to acquire dataAnalyzerService... 2022-10-20 11:24:31.640 2729-2729/rhea.sample.android E/rhea:atrace: failed to create directory /storage/emulated/0/rhea-trace/rhea.sample.android 2022-10-20 11:24:31.652 2729-2900/rhea.sample.android E/AwareLog: AtomicFileUtils: readFileLines file not exist: android.util.AtomicFile@4a40ad0 2022-10-20 11:24:31.844 2729-2902/rhea.sample.android E/.sample.androi: open file error

    opened by zhangqilu 1
Releases(v1.0.2)
  • v1.0.2(Dec 28, 2021)

    • 修复因编译问题引起的 App 类找不到崩溃问题。

    • 修复某些情况下 trace 闭合异常问题。

    • Fix the crash problem that the App class cannot be found due to compilation problems.

    • Fix the abnormal problem of trace closure in some cases.

    Source code(tar.gz)
    Source code(zip)
  • v1.0.1(Dec 21, 2021)

Owner
Bytedance Inc.
Bytedance Inc.
A surgical debugging tool to uncover the layers under your app.

Scalpel DEPRECATED! Android Studio 4.0's layout inspector now includes a live-updating 3D view. Use it! A surgical debugging tool to uncover the layer

Jake Wharton 2.8k Jan 9, 2023
A model-agnostic visual debugging tool for machine learning

Manifold This project is stable and being incubated for long-term support. Manifold is a model-agnostic visual debugging tool for machine learning. Un

Uber Open Source 1.6k Dec 25, 2022
Colored logcat script which only shows log entries for a specific application package.

PID Cat An update to Jeff Sharkey's excellent logcat color script which only shows log entries for processes from a specific application package. Duri

Jake Wharton 4.7k Dec 29, 2022
This app will show grid overlay over whole system which helps you to verify your excellent app design.

GridWichterle for Android This app will show grid overlay over whole system which helps you to verify your excellent app design. Download: What is the

Inmite s.r.o. 407 Jan 9, 2023
Cordova plugin for Android Serial USB communication (easily connect an Arduino board to an Android device).

PR-DC cordova-plugin-serialusb Cordova plugin for Android Serial USB communication. This plugin makes a connection to the external board trivial, for

PR-DC 3 May 8, 2022
Stetho is a debug bridge for Android applications, enabling the powerful Chrome Developer Tools and much more.

Stetho Stetho is a sophisticated debug bridge for Android applications. When enabled, developers have access to the Chrome Developer Tools feature nat

Facebook 12.6k Jan 7, 2023
A library for debugging android databases and shared preferences - Make Debugging Great Again

Android Debug Database Android Debug Database is a powerful library for debugging databases and shared preferences in Android applications Android Deb

AMIT SHEKHAR 8.1k Dec 29, 2022
🔪Swiss-army knife for Android testing and development 🔪 ⛺

ADB Enhanced ADB-Enhanced is a Swiss-army knife for Android testing and development. A command-line interface to trigger various scenarios like screen

Ashish Bhatia 938 Dec 20, 2022
Under the Hood is a flexible and powerful Android debug view library. It uses a modular template system that can be easily extended to your needs, although coming with many useful elements built-in.

Under the Hood - Android App Debug View Library Under the Hood is a flexible and powerful Android debug view library. It uses a modular template syste

Patrick Favre-Bulle 217 Nov 25, 2022
Android library to record the network calls through the interceptor mechanism of the http clients.

Android Snooper Introduction Android Snooper is a library which helps in debugging issues while running the applications on android devices. One of th

Prateek 151 Nov 25, 2022
A Read-Eval-Print-Loop server for Android and SQLite

Android DebugPort Android DebugPort is a drop-in utility which allows you to write and execute code within your app's context, at runtime, and from th

Jason Feinstein 148 Nov 14, 2022
traffic debugging library for android

TrafficMonitor About Display traffic per Activity.Observing traffic is used by TrafficStats API. OkHttp Interceptor observer is implementing. Demo Bai

Tetsuya Masuda 17 Feb 4, 2022
Easy android exception tracer and handler.

Introduction Lup is a small android library that can help you to tracking bug that causes application stopped working (force close). Whiting this libr

icodeu 4 Sep 29, 2022
Sources for the LiveBoot app for rooted Android devices

This is the sauce for the LiveBoot app. License Copyright © 2011-2020 Jorrit Chainfire Jongma This code is released under the GPLv3. LICENSE, COPYING.

Chainfire 131 Jan 9, 2023
android logcat

android logcat

Ji Sungbin 3 Dec 2, 2022
A local ADB shell for Android!

LADB A local ADB shell for Android! How does it work? LADB bundles an ADB server within the app libraries. Normally, this server cannot connect to the

Tyler 1.1k Jan 2, 2023
Pluto: An on-device debugging framework for Android applications

Pluto is an on-device debugging framework for Android applications, which helps in the inspection of HTTP requests/responses, captures Crashes, and ANRs, and manipulates application data on the go.

Pluto 550 Dec 27, 2022