Traceview(android)
Feature Preview Only: The Traceview and dmtracedump utilities will be available in an upcoming release of the SDK.
提示:The Traceview及dmtracedump工具要在即将发布的下一版本SDK中才能使用。
Traceview is a graphical viewer for execution logs saved by your application. The sections below describe how to use the program.
Traceview是一个查看分析程序运行日志的图形化工具。以下几节将描述这个工具的用法。
Creating Trace Files 建立跟踪文件
To use Traceview, you need to generate log files containing the trace information you want to analyze. To do that, you include the Debug class in your code and call its methods to start and stop logging of trace information to disk. When your application quits, you can then use Traceview to examine the log files for useful run-time information such as method calls and run times.
要使用Traceview进行分析,首先必须要生成你想要分析的跟踪信息日志文件。你可以这样生成跟踪信息日志文件,在你的代码中直接调用 Debug类的方法来记录跟踪信息并输出到日志文件中。当应用程序退出后,你就可以使用Traceview来对日志文件进行分析程序运行时的方法调用及运行次数等情况了。
To create the trace files, include the Debug class and call one of the startMethodTracing() methods. In the call, you specify a base name for the trace files that the system generates. To stop tracing, call stopMethodTracing(). These methods start and stop method tracing across the entire virtual machine. For example, you could call startMethodTracing() in your activity’s onCreate() method, and call stopMethodTracing() in that activity’s onDestroy() method.
为了生成跟踪日志文件,你应该在程序中包含Debug类并调用startMethodTracing()方法来开始进行日志跟踪,在这个调用方法中,你应该为跟踪日志文件指定一个名字。调用stopMethodTracing()方法来停止跟踪。这些方法可以在虚拟机上任意开始和停止方法跟踪。比如:你在activity’s onCreate()方法中调用startMethodTracing()开始跟踪,并在activity’s onDestroy()方法中调用stopMethodTracing()来结束跟踪。
// start tracing to “/tmp/calc”
Debug.startMethodTracing(“/tmp/calc”);
// …
// stop tracing
Debug.stopMethodTracing();
When your application calls startMethodTracing(), the system creates two files:
当你调用startMethodTracing(),系统会生成以下两个文件:
.data — contains binary method trace data
The system then begins buffering the generated trace data, until your application calls stopMethodTracing(), at which time it writes the buffered data to the files. If the system reaches the maximum buffer size before stopMethodTracing() is called, the system stops tracing and sends a notification to the console.
跟踪开始后,系统对生成的跟踪数据是先进行缓存的,直到程序调用了stopMethodTracing()方法时才把缓存的数据写到文件中。如果在调用stopMethodTracing()前,系统已经达到了缓存最大值时,则系统就停止跟踪并发一个通知到控制台。
The format of the trace files is described later in this document.
跟踪文件的格式下面会详细进行说明.
Copying Trace Files to a Host Machine 拷贝跟踪文件到主机
After your application has run and the system has created your trace files
.data and
.key. on a device or emulator, you must copy those files to your development computer. You can use adp pull to copy the files. Here’s an example that shows how to copy two example files, calc.data and calc.key, from the default location on the emulator to the /tmp directory on the emulator host machine:
当程序运行并在手机设备或者模拟器上生成了跟踪文件
.data 和
.key后,你必须把这些文件拷贝到开发环境的主机上。使用adp命令进行拷贝,以下例子说明如何从模拟器默认位置拷贝calc.data 和 calc.key到模拟器的主机的/tmp目录下的。
adb pull /tmp/calc.data /tmp
adb pull /tmp/calc.key /tmp
Viewing Trace Files in Traceview 使用Traceview查看跟踪文件
To run traceview and view the trace files, enter traceview
. For example, to run Traceview on the example files copied in the previous section, you would use:
输入traceview
命令运行traceview工具来查看跟踪文件。例如:通过Traceview工具运行上一节中用到的例子文件,你会看到:
traceview /tmp/calc
Traceview loads the log files and displays their data in a window that has two panels:
A timeline panel — describes when each thread and method started and stopped
A profile panel — provides a summary of what happened inside a method
The sections below provide addition information about the traceview output panes.
Traceview载入日志文件并显示数据在两个面板上:
时间轴面板—描述每个线程和方法的开始和终止。
Profile面板—提供一个方法中发生了什么的摘要。
以下部分提供traceview输出面板的详细信息。
Timeline Panel
The image below shows a close up of the timeline panel. Each thread’s execution is shown in its own row, with time increasing to the right. Each method is shown in another color (colors are reused in a round-robin fashion starting with the methods that have the most inclusive time). The thin lines underneath the first row show the extent (entry to exit) of all the calls to the selected method. The method in this case is LoadListener.nativeFinished() and it was selected in the profile view.
下图显示了时间轴面板的一个结果。每个线程的执行都显示在随着时间渐增右移的各自行上。不同的方法用不同的颜色来表示。第一行下面的细线显示选中方法的调用时长(由进入到退出)。本例中的方法是LoadListener.nativeFinished(),它是在profile面板中选中的。
Profile Panel
The image below shows the profile pane. The profile pane shows a summary of all the time spent in a method. The table shows both the inclusive and exclusive times (as well as the percentage of the total time). Exclusive time is the time spent in the method. Inclusive time is the time spent in the method plus the time spent in any called functions. We refer to calling methods as “parents” and called methods as “children.” When a method is selected (by clicking on it), it expands to show the parents and children. Parents are shown with a purple background and children with a yellow background. The last column in the table shows the number of calls to this method plus the number of recursive calls. The last column shows the number of calls out of the total number of calls made to that method. In this view, we can see that there were 14 calls to LoadListener.nativeFinished(); looking at the timeline panel shows that one of those calls took an unusually long time.
下图是profile面板,面板显示了每一个方法的所花费时间的概要.包括inclusive和exclusive时间(同时用百分比表示)。 Exclusive时间:方法执行所花费的时间。Inclusive时间: 方法执行所花费的时间+方法调用所花费的时间。通常调用方法为“父节点”,被调用方法为“子节点”。当一个方法被选中(单击),它就展开并显示父子节点,背景色为紫色的是父节点, 背景色为黄色的是子节点。表中最后一栏显示的是调用这个方法的次数+递归调用的次数。同时也表示的是调用次数/总调用次数。下图中,我们可以看出 LoadListener.nativeFinished()调用了14次。此时看一下时间轴面板,面板显示表明每次调用都花费了很长的时间。
Data File Format Data文件格式
The data file is a binary file with the extension .data. It is structured as follows (all values are stored in little-endian order):
Data文件是以扩展名.data的二进制文件。它的结构如下:
* File format:
* header
* record 0
* record 1
* …
*
* Header format:
* u4 magic 0x574f4c53 (‘SLOW’)
* u2 version
* u2 offset to data
* u8 start date/time in usec
*
* Record format:
* u1 thread ID
* u4 method ID | method action
* u4 time delta since start, in usec
The application is expected to parse all of the header fields, then seek to “offset to data” from the start of the file. From there it just reads 9-byte records until EOF is reached.
u8 start date/time in usec is the output from gettimeofday(). It’s mainly there so that you can tell if the output was generated yesterday or three months ago.
method action sits in the two least-significant bits of the method word. The currently defined meanings are:
- 0 – method entry
- 1 – method exit
- 2 – method “exited” when unrolled by exception handling
- 3 – (reserved)
An unsigned 32-bit integer can hold about 70 minutes of time in microseconds.
程序从文件开始解析header字段,并查找“偏移数据”,每次只读9-byte,直到EOF结束。以u8开始表示来表示输出的日期/时间,这样,你可以知道是昨天还是三天以前输出的了。方法动作用2个字节来表示,定义如下:
- 0 – 表示进行
- 1 – 表示退出
- 2 – 表示异常退出
- 3 – (保留)
-
32位的无符号整数可以表示70分钟以微秒为单位的时长.
Key File Format Key文件格式
The key file is a plain text file divided into three sections. Each section starts with a keyword that begins with ‘*’. If you see a ‘*’ at the start of a line, you have found the start of a new section.
An example file might look like this:
Key文件是一个由三部分组成的纯文本文件,每一部分用关键字‘*’作为开始,如果你看到某一行以‘*’开始,则表示这是新部分的开始。
文件可能如下:
*version
1
clock=global
*threads
1 main
6 JDWP Handler
5 Async GC
4 Reference Handler
3 Finalizer
2 Signal Handler
*methods
0x080f23f8 java/io/PrintStream write ([BII)V
0x080f25d4 java/io/PrintStream print (Ljava/lang/String;)V
0x080f27f4 java/io/PrintStream println (Ljava/lang/String;)V
0x080da620 java/lang/RuntimeException ()V
[...]
0x080f630c android/os/Debug startMethodTracing ()V
0x080f6350 android/os/Debug startMethodTracing (Ljava/lang/String;Ljava/lang/String;I)V
*endversion section ,
The first line is the file version number, currently 1. The second line, clock=global, indicates that we use a common clock across all threads. A future version may use per-thread CPU time counters that are independent for every thread.
版本部分
第一行是文件版本号,通常是1,第二行clock=global描述所有线程共用的时钟.以后版本可能会用每个独立线程的CPU时钟来表示。
threads section
One line per thread. Each line consists of two parts: the thread ID, followed by a tab, followed by the thread name. There are few restrictions on what a valid thread name is, so include everything to the end of the line.
线程部分
每个线程一行,每行包括两部分:线程ID ,一个tab,线程名。线程名没有限制,到这行结尾都是线程名部分。
methods section
One line per method entry or exit. A line consists of four pieces, separated by tab marks: method-ID [TAB] class-name [TAB] method-name [TAB] signature . Only the methods that were actually entered or exited are included in the list. Note that all three identifiers are required to uniquely identify a method.
方法部分
每行表示一个方法,一行由四部分组成,用tab标识进行分隔:方法ID [TAB]类名[TAB] 方法名[TAB]信号 。无论是方法进入还是退出都会记录在这个列表上,注意:三个标识符是必须的,它唯一表示了方法.
Neither the threads nor methods sections are sorted.
除线程、方法部分外的都是类别部分。
Traceview Known Issues Traceview存在问题
Threads
Traceview logging does not handle threads well, resulting in these two problems:
- If a thread exits during profiling, the thread name is not emitted;
- The VM reuses thread IDs. If a thread stops and another starts, they may get the same ID.
Traceview日志对线程处理得不是很好,存在以下2个问题:
- 如果一个线程存在于作分析图期间,这个线程是不会被发表的。
- 虚拟机上线程ID重用问题,如果一个线程停止后另一个线程开始,它们可能使用了同一个线程ID。
Using dmtracedump dmtracedump用法
The Android SDK includes dmtracedump, a tool that gives you an alternate way of generating graphical call-stack diagrams from trace log files. The tool uses the Graphviz Dot utility to create the graphical output, so you need to install Graphviz before running dmtracedump.
The dmtracedump tool generates the call stack data as a tree diagram, with each call represented as a node. It shows call flow (from parent node to child nodes) using arrows. The diagram below shows an example of dmtracedump output.
Android SDK有一个dmtracedump工具,这个工具可以让从跟踪日志中生图形化的方法调用图和堆栈图。工具使用Graphviz Dot组件来生成图形的,所以要运行dmtracedump就必须先安装Graphviz。
Dmtracedump用树状图来表示堆栈数据,每一个数据用一个节点来表示。用箭头来表示(从父节点到子节点的)调用。下图显示了dmtracedump输出的一个例子。
For each node, dmtracedump shows
callname (, ,), where - — Call reference number, as used in trace logs
- — Inclusive elapsed time (milliseconds spent in method, including all child methods)
- — Exclusive elapsed time (milliseconds spent in method, not including any child methods)
- — Number of calls
The usage for dmtracedump is:
对于每个节点,dmtracedump显示格式:
callname (, ,),其中: - — 编号
- — Inclusive时间总和(微秒为单位,包括子方法时间)
- — Exclusive时间总和(微秒为单位,不包括子方法时间)
- — 调用次数
dmtracedump用法:
dmtracedump [-ho] [-s sortable] [-d trace-base-name] [-g outfile]
The tool then loads trace log data from
.data and
.key. The table below lists the options for dmtracedump.这个工具从
.data and
.key加载日志数据。下表描述dmtracedump的选项列表:Option Description -d Diff with this trace name -g Generate output to -h Turn on HTML output -o Dump the trace file instead of profiling -d URL base to the location of the sortable javascript file -t Minimum threshold for including child nodes in the graph (child’s inclusive time as a percentage of parent inclusive time). If this option is not used, the default threshold is 20%. mksdcard
(xing:因为google放出了video的demo,很多人开始查sd卡的用法,所以抽时间把这个翻译了一下。keywords:android create sdcard)
mksdcard工具是用来创建虚拟的SD卡映像的,它创建SD卡是FAT32格式。创建好的SD卡映像可以被载入模拟器,如同使用一个真正的SD设备。下面是它的用法:
mksdcard [-l label]
[K|M] 下面的表格列出了mksdcard所有的选项和参数
参数 注释 -l 为SD卡创建一个卷标。 size 用一个整数来设定SD卡的大小。缺省单位是byte,可以使用大写的”K”和”M”跟在数值后面改变这个单位,如 1048576K, 1024M(xing:有网友建议不要设置得太小,不然模拟器可能会崩溃。而且命令有提示,模拟器不能用8M的卡。我现在用32M没有任何问题。要注意一点,一旦生成映像,所有的空间都会被分配,就是说如果你使用了1024M作为参数,你的硬盘上就会出现一个1G的文件。) file 映像的文件名。比如sdcard.img。 例:mksdcard -l mycard 32M mycard.img
创建了SD映像之后就可以在模拟器的启动参数里面加入-sdcard来载入它。更多信息参见[Android Emulator].
emulator -sdcard
(xing:文件名最好使用全路径,尤其是在eclipse里面,理论上将sdcard.img放在下是可以载入的,但事实测的时候并没有成功。)
附:如何将文件放到SD卡中
如果你使用eclipse,那就再简单不过了。首先在run dialog里面为emulator增加启动参数-sdcard
,模拟器启动后,在ddms里面就可以看到sdcard这个目录了,然后使用文件传送按钮就可以把文件传到SD卡中。 使用命令行也不麻烦,首先还是要有启动参数,然后使用
adb push
就可以将本地文件发送到模拟器,例如:
adb push temp.img /sdcard/audio
(xing:这个audio目录是使用adb shell创建的,好像在播放视频的时候,模拟器会自己创建video目录。)
dx
The dx tool lets you generate Android bytecode from .class files. The tool converts target files and/or directories to Dalvik executable format (.dex) files, so that they can run in the Android environment. It can also dump the class files in a human-readable format and run a target unit test. You can get the usage and options for this tool by using dx -help.
activityCreator
If you aren’t using the Eclipse IDE and ADT plugin, you can use the the activityCreator script to get started with a new application. When you run the script, it creates the structure of a minimal Android application that you can build on and extend to meet your needs.
For Linux and Mac, the SDK provides activityCreator.py, a Python script, and for Windows activityCreator.bat, a batch script that runs an executable. Regardless of platform, the usage for the script is the same:
activityCreator [--out
] [--ide intellij] your.package.name.ActivityName Option Description –out Specifies where to create the files/folders. –ide intellij Creates project files for IntelliJ When run, the script creates these files:
- AndroidManifest.xml — The application manifest file.
- build.xml — An Ant script to build/package the application.
- res — The resource directory.
- src — The source directory.
- src/your/package/name/ActivityName.java — The Activity class.
- bin — The output folder for the compiled .apk (when built by Ant).
When you are ready, you can use Ant to build the project so that you can run it on the emulator.
If you are using Eclipse with the ADT plugin, you do not need to use activityCreator. You can use the New Project Wizard, provided by the ADT plugin, instead.
Intro/lifecycle
Application的生命周期
In most cases, every Android application runs in its own Linux process. This process is created for the application when some of its code needs to be run, and will remain running until it is no longer needed and the system needs to reclaim its memory for use by other applications.
在多数情况下, 每个Android应用运行在自己的Linux进程中. 当一个应用的某段code需要运行的时候这个进程将会被创建, 直到不再需要该应用或系统要为其他的应用释放内存的时候才停止.
An important and unusual feature of Android is that an application process’s lifetime is not directly controlled by the application itself. Instead, it is determined by the system through a combination of the parts of the application that the system knows are running, how important these things are to the user, and how much overall memory is available in the system.
一个非常重要且少有的特性是, 应用进程的存活时间不是由这个应用直接控制的. 而是由系统决定的, 系统会根据每个已知的正在运行的应用情况来定夺, 包括, 该应用对用户的重要性和系统全部可用内存.
It is important that application developers understand how different application components (in particular Activity, Service, and IntentReceiver) impact the lifetime of the application’s process. Not using these components correctly can result in the system killing the application’s process while it is doing important work.
对于开发人员来讲, 了解每个应用组件(尤其是, Activity, Service, 和IntentReceiver)对于应用进程存活时间的影响是非常重要的. 如果没有正确使用, 可能会导致应用进程在处理重要工作的时候被系统杀掉.
A common example of a process lifecycle bug is an IntentReceiver that starts a thread when it receives an Intent in its onReceiveIntent() method, and then returns from the function. Once it returns, the system considers that IntentReceiver to be no longer active, and thus its hosting process no longer needed (unless other application components are active in it). Thus, it may kill the process at any time to reclaim memory, terminating the spawned thread that is running in it. The solution to this problem is to start a Service from the IntentReceiver, so the system knows that there is still active work being done in the process.
在对应用进程生命周期的理解中, 一个典型的错误就是当一个IntentReceiver 接收到Intent 之后, 会在自己的onReceiveIntent()方法中开起一个线程, 而后return这个方法. 一旦这个方法return, 系统会认为这个IntentReceiver 不在处于活跃状态, 也就认为他的宿主进程不再需要(除非还包有其他活跃的应用组件). 以至于当系统需要回收内存的时候会随时释kill掉这个进程, 中止其中的子线程. 解决这个问题的办法是在IntentReceiver中启动一个Service, 这样系统会知道在这个进程中还有活跃的任务需要完成.
To determine which processes should be killed when low on memory, Android places them into an “importance hierarchy” based on the components running in them and the state of those components. These are, in order of importance:
为了决定在内存较低的时候杀掉哪个进程, Android会根据运行在这些进程内的组件及他们的状态把进程划分成一个”重要程度层次”. 其重要的程度按以下规则排序:
- A foreground process is one holding an Activity at the top of the screen that the user is interacting with (its onResume() method has been called) or an IntentReceiver that is currently running (its onReceiveIntent() method is executing). There will only ever be a few such processes in the system, and these will only be killed as a last resort if memory is so low that not even these processes can continue to run. Generally at this point the device has reached a memory paging state, so this action is required in order to keep the user interface responsive.
-
前端进程
可以是一个持有运行在屏幕最前端并与用户交互的Activity的进程(onResume方法被调用时),也可以是持有一个正在运行的IntentReceiver(也就是说他正在执行自己的onReceiveIntent方法)的进程. 在系统中, 只会有少数这样的进程, 并且除非内存已经低到不够这些进程运行, 否则系统不会主动杀掉这些进程. 这时, 设备通常已经达到了需要内存整理的状态, 所以杀掉这些进程是为了不让用户界面停止响应.
- A visible process is one holding an Activity that is visible to the user on-screen but not in the foreground (its onPause() method has been called). This may occur, for example, if the foreground activity has been displayed with a dialog appearance that allows the previous activity to be seen behind it. Such a process is considered extremely important and will not be killed unless doing so is required to keep all foreground processes running.
-
可视进程
是持有一个被用户可见, 但没有显示在最前端 (onPause方法被调用时) 的Activity的进程. 举例来说, 这种进程通常出现在一个前端Activity以一个对话框出现并保持前一个Activity可见时. 这种进程被系统认为是极其重要的, 并且通常不会被杀掉, 除非为了保持所有前端进程正常运行不得不杀掉这些可见进程.
- A service process is one holding a Service that has been started with the startService() method. Though these processes are not directly visible to the user, they are generally doing things that the user cares about (such as background mp3 playback or background network data upload or download), so the system will always keep such processes running unless there is not enough memory to retain all foreground and visible process.
-
服务进程
是持有一个Service的进程, 该Service是由startService()方法启动的, 尽管这些进程用户不能直接看到, 但是通常他们做的工作用户是十分关注的(例如, 在后台播放mp3或是在后台下载 上传文件), 所以, 除非为了保持所有的前端进程和可视进程正常运行外, 系统是不会杀掉服务进程的.
- A background process is one holding an Activity that is not currently visible to the user (its onStop() method has been called). These processes have no direct impact on the user experience. Provided they implement their activity lifecycle correctly (see Activity for more details), the system can kill such processes at any time to reclaim memory for one of the three previous processes types. Usually there are many of these processes running, so they are kept in an LRU list to ensure the process that was most recently seen by the user is the last to be killed when running low on memory.
-
后台进程
是持有一个不再被用户可见的Activity(onStop()方法被调用时)的进程. 这些进程不会直接影响用户体验. 加入这些进程已经完整的,正确的完成了自己的生命周期(访问Activity查看更多细节), 系统会在为前三种进程释放内存时随时杀掉这些后台进程. 通常会有很多的后台进程在运行, 所以这些进程被存放在一个LRU列表中, 以保证在低内存的时候, 最近一个被用户看到的进程会被最后杀掉.
- An empty process is one that doesn’t hold any active application components. The only reason to keep such a process around is as a cache to improve startup time the next time a component of its application needs to run. As such, the system will often kill these processes in order to balance overall system resources between these empty cached processes and the underlying kernel caches.
-
空进程是
没有持有任何活动应用组件的进程. 保留这种进程的唯一理由是为了提供一种缓存机制, 缩短他的应用下次运行时的启动时间. 就其本身而言, 系统杀掉这些进程的目的是为了在这些空进程和底层的核心缓存之间平衡整个系统的资源.
When deciding how to classify a process, the system picks the most important level of all the components currently active in the process. See the Activity, Service, and IntentReceiver documentation for more detail on how each of these components contribute to the overall lifecycle of a process. The documentation for each of these classes describes in more detail how they impact the overall lifecycle of their application.
当需要给一个进程分类的时候, 系统会在该进程中处于活动状态的所有组件里掉选一个重要等级最高作为分类依据. 查看Activity, Service,和IntentReceiver的文档, 了解每个组件在进程整个生命周期中的贡献. 每一个classes的文档详细描述他们在各自应用的生命周期中所起得作用.
作者:GangWang
出处:http://www.cnblogs.com/GnagWang/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。