第一是能够在DDMS中看到进程和线程
首先要将default.prop的debuggable修改为1
使用
https://github.com/liudongmiao/bootimg
实现解压bootimg的修改和打包。
使用原来的 先 --unpack-bootimg再--unpack-ramdisk
修改完毕以后
先--repack-ramdisk再--repack-bootimg
使用twrp等刷回去调试
调试的时候选中 进程。然后使用Method profile截取信息。记得选择下面的那个全部方法截取,选择上面的采样截取有些函数不能够完全截取到。
截取出来的trace文件可以使用
dmtracedump.exe -ho 11.trace >aa.txt dump成txt或者html格式
但是这里面的没区分开来。我们用python根据线程区分出来
def func():
file=open("aa.txt",'r')
if not file:
print("openfile error")
return
line=file.readline()
threaddict={}
indexdict={}
revdict={}
func_container=[]
index=0
number=0
while line:
while True:
if not line[0].isdigit():
break
if " unr " in line:
break
if not (" ent " in line or " xit " in line):
isfunc=False
pos=line.find(' ')
threadid=line[0:pos]
threadname=line[pos+1:len(line)-1]
threadname=threadname.replace(":","_")
threaddict[threadid]=threadid+"_"+threadname
indexdict[threadid]=index
revdict[index]=threadid
'''
if threadid=="2599":
print "number = + ",number
print threadid
print threadname
print threaddict[threadid]
'''
index=index+1
func_container.append([])
break
idpos=line.find(' ')
tid=line[0:idpos]
pointpos=line.find('.')
funcname=line[pointpos:len(line)-1]
prefix="ENTER"
if " ent " not in line:
prefix="LEAVE"
func_container[indexdict[tid]].append(prefix+funcname)
break;
number=number+1
line=file.readline()
index=0
for lst in func_container:
filename_org=threaddict[revdict[index]]
filename_filter=filename_org+"_filter"
filename_org=filename_org+".log"
filename_filter=filename_filter+".log"
index=index+1
fileorg=open(filename_org,"w")
filefilter=open(filename_filter,"w")
for item in lst:
if "immomo" in item:
filefilter.write(item+"\n")
fileorg.write(item+"\n")
filefilter.close()
fileorg.close()
func()
这个会根据线程将所有的调用CALL区分开来。而且是按顺序的。比如我要找一个按钮事件。
开始method profile 点击按钮 停止method profile 得到这个trace然后搜索OnClick基本就能看懂整个流程了
哈哈哈哈 随便分析
DDMS的MethodProfiling流程
由DDMS发送调用
startMethodTracer 发送请求给 android的服务进程
/**
* Send a MPRS (Method PRofiling Start) request to the client.
*
* The arguments to this method will eventually be passed to
* android.os.Debug.startMethodTracing() on the device.
*
* @param fileName is the name of the file to which profiling data
* will be written (on the device); it will have {@link DdmConstants#DOT_TRACE}
* appended if necessary
* @param bufferSize is the desired buffer size in bytes (8MB is good)
* @param flags see startMethodTracing() docs; use 0 for default behavior
*/
android.os.Debug.startMethodTracing()
结果startMethodTracing又调用了VMDebug
public static void startMethodTracing(String traceName, FileDescriptor fd,
int bufferSize, int flags) {
VMDebug.startMethodTracing(traceName, fd, bufferSize, flags, false, 0);
}
import dalvik.system.VMDebug;
VMDebug 根据参数调用了
private static native void startMethodTracingDdmsImpl(int bufferSize, int flags, boolean samplingEnabled, int intervalUs);
private static native void startMethodTracingFd(String traceFileName, FileDescriptor fd, int bufferSize, int flags, boolean samplingEnabled, int intervalUs);
private static native void startMethodTracingFilename(String traceFileName, int bufferSize, int flags, boolean samplingEnabled, int intervalUs);
这几个都是native函数
art\runtime\native\dalvik_system_VMDebug.cc
startMethodTracingFilename{
调用Trace的
Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, Trace::TraceOutputMode::kFile,
samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing,
intervalUs);
}
Start函数位于art/runtime/trace.cc
主要是
runtime->GetInstrumentation()->AddListener(the_trace_,
instrumentation::Instrumentation::kMethodEntered |
instrumentation::Instrumentation::kMethodExited |
instrumentation::Instrumentation::kMethodUnwind);
// TODO: In full-PIC mode, we don't need to fully deopt.
runtime->GetInstrumentation()->EnableMethodTracing(kTracerInstrumentationKey);
在当前runtime的instrumentation增加了一个Listener
这个listener被添加到了
art\runtime\instrumentation.cc的 method_entry_listeners_链表中
主要有这几个
std::list<InstrumentationListener*> method_entry_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> method_exit_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> method_unwind_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> branch_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> invoke_virtual_or_interface_listeners_
GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> field_read_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> field_write_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> exception_caught_listeners_ GUARDED_BY(Locks::mutator_lock_);
在Instrumentation中每次有方法进入的时候会执行 MethodEnterEventImpl函数
void Instrumentation::MethodEnterEventImpl(Thread* thread, mirror::Object* this_object,
ArtMethod* method,
uint32_t dex_pc) const {
if (HasMethodEntryListeners()) {
for (InstrumentationListener* listener : method_entry_listeners_) {
if (listener != nullptr) {
listener->MethodEntered(thread, this_object, method, dex_pc);
}
}
}
}
会对每个加载的Listener执行MethodEntered函数
也就是Trace.cc的Trace类
后续再看采集了什么数据。先看看是如何进入函数的。MethodEnterEvent.h调用MethodEnterEvent进入MethodEnterEventImpl
主要是在art\runtime\interpreter\interpreter.cc中Execute 调用了
static inline JValue Execute(
Thread* self,
const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame,
JValue result_register,
bool stay_in_interpreter = false) SHARED_REQUIRES(Locks::mutator_lock_) {
if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
method, 0);
}
调用Execute的地方
有几个
EnterInterpreterFromInvoke
ArtMethod::Invoke中 满足条件if (UNLIKELY(!runtime->IsStarted() || Dbg::IsForcedInterpreterNeededForCalling(self, this)))
EnterInterpreterFromDeoptimize
EnterInterpreterFromEntryPoint
ArtInterpreterToInterpreterBridge