? Android Program Debugging Tool
The highlights of the code debugging tools that Google provides us:TraceView and dmtracedump . With these two tools, we debug the program to analyze the bug is very handy. TraceView helps us analyze program performance and dmtracedump generate function call graphs. Unfortunately, the dmtracedump provided by Google is a failed tool and cannot be plotted, this article describes the solution in detail and implements the drawing.
? generate a. trace file
The Android.os.Debug class, which is an important two method of debug.startmethodtracing () and debug.stopmethodtracing (). These two methods are used to create a. trace file, starting with debug.startmethodtracing () and ending with debug.stopmethodtracing (), During all the call procedures are saved in the. trace file, including information such as the name of the function called and the time of execution.
Add the following code to the location of the debug start code, and to the terminating position.
- Debug.startmethodtracing ("test");
- Debug.stopmethodtracing ();
Where the parameter test is the name of the trace file to be created, Test.trace. The default path is/sdcard/test.trace, or you can make your own/data/log/test, indicating that the file is in/data/log/test.trace.
? traceview
Execute in the SDK:
./traceview Test.trace
We can get
1. Start and stop times for each thread in the program to invoke the method
2. Information and efficiency analysis of function execution
? dmtracedump
The original intention of Dmtracedump was to combine the entire invocation process with the time analysis in the form of a function call graph. But Google this project has been in a state of broken, delay can not be perfected. Now dmtracdump only the-o option can be used, the function call information is listed on the terminal, and the TraceView function is similar if executed./dmtracedump–g test.png Test.trace will be stuck.
At first I thought it was test.trace file, the file terminator slightly modified, drew a shocking picture:
Later, the search on the network of cattle have provided an alternative to dmtracedump (this is the original link), is a Python script, with the help of dot to draw vector diagram. The Python script must be aware of the alignment format, and the alignment indentation is his logical structure.
- #!/usr/bin/env python
- """
- Turn the TraceView data into a JPG pic, showing methods call relationship
- """
- Import Sys
- Import OS
- Import struct
- Import re
- ################################################################################
- ######################## Global Variable #####################################
- ################################################################################
- Target_thread=1 #the thread, want, filt out and other threads
- #all_actions = ["Enter", "Exit", "Exception", "reserved"]
- All_threads = {}
- All_methods = {}
- All_records = []
- Parent_methods = {}
- Child_methods = {}
- Method_calls = {}
- ################################################################################
- ############################## Methods #####################################
- ################################################################################
- def add_one_thread (line):
- Fields = Line.split ("/t")
- All_threads[int (fields[0],10)]=fields
- def add_one_method (line):
- Fields = Line.split ("/t")
- All_methods[int (fields[0],16)]=fields
- def Add_one_record (one):
- Thread_id,=struct.unpack ("B", One[:1])
- if (thread_id = = Target_thread):
- Tmp,=struct.unpack ("L", One[1:5])
- Method_id= (TMP/4) * 4;
- method_action= tmp% 4;
- Time_offset,=struct.unpack ("L", One[5:])
- All_records.append ([thread_id, method_id, Method_action, Time_offset])
- def handle_one_call (parent_method_id,method_id):
- If not (Parent_methods.has_key (parent_method_id)):
- Parent_methods[parent_method_id]=1
- If not (Child_methods.has_key (method_id)):
- Child_methods[method_id]=1
- If Method_calls.has_key (parent_method_id):
- If Method_calls[parent_method_id].has_key (method_id):
- Method_calls[parent_method_id][method_id]+=1
- Else
- Method_calls[parent_method_id][method_id]=1
- Else
- method_calls[parent_method_id]={}
- Method_calls[parent_method_id][method_id]=1
- def gen_funcname (method_id):
- R1=re.compile (R ' [/{1}lt;>] ')
- Str1=r1.sub ("_", All_methods[method_id][1])
- Str2=r1.sub ("_", All_methods[method_id][2])
- Return str1+ "_" +str2
- Def gen_dot_script_file ():
- Myoutfile = open ("Graph.dot", "W")
- Myoutfile.write ("digraph vanzo {/n/n");
- For one in All_methods.keys ():
- If Parent_methods.has_key (one):
- Myoutfile.write (Gen_funcname (one) + "[shape=rectangle];/n")
- Else
- If Child_methods.has_key (one):
- Myoutfile.write (Gen_funcname (one) + "[shape=ellipse];/n")
- For one in Method_calls.keys ():
- For Method_calls[one]:
- Myoutfile.write (Gen_funcname (one) + ' + ' + gen_funcname (both) + ' [label= "' + str (method_calls[one][two]) + '" fonts Ize= "];/n")
- Myoutfile.write ("/n}/n");
- Myoutfile.close
- ################################################################################
- ########################## Script starts from here #############################
- ################################################################################
- If Len (SYS.ARGV) < 2:
- print ' No input file specified. '
- Sys.exit ()
- If not (Os.path.exists (sys.argv[1])):
- Print "Input File not exists"
- Sys.exit ()
- #Now Handle the text part
- Current_section=0
- For line in open (Sys.argv[1]):
- Line2 = Line.strip ()
- if (Line2.startswith ("*")):
- if (Line2.startswith ("*version")):
- Current_section=1
- Else
- if (Line2.startswith ("*threads")):
- current_section=2
- Else
- if (Line2.startswith ("*methods")):
- Current_section=3
- Else
- if (Line2.startswith ("*end")):
- Current_section=4
- Break
- Continue
- If current_section==2:
- Add_one_thread (line2)
- If current_section==3:
- Add_one_method (line2)
- #Now Handle the binary part
- Mybinfile = open (Sys.argv[1], "RB")
- AllData = Mybinfile.read ()
- Mybinfile.close ()
- Pos=alldata.find ("SLOW")
- Offset,=struct.unpack ("H", Alldata[pos+6:pos+8])
- Pos2=pos+offset #pos2 is where the record begin
- Numofrecords = Len (alldata)-Pos2
- Numofrecords = NUMOFRECORDS/9
- For I in Xrange (numofrecords):
- Add_one_record (Alldata[pos2 + i * 9:pos2 + i * 9 + 9])
- MY_STACK=[0]
- For Onerecord in All_records:
- THREAD_ID=ONERECORD[0];
- METHOD_ID=ONERECORD[1];
- ACTION=ONERECORD[2];
- TIME=ONERECORD[3];
- if (action==0):
- if (len (my_stack) > 1):
- PARENT_METHOD_ID=MY_STACK[-1]
- Handle_one_call (parent_method_id,method_id)
- My_stack.append (method_id)
- Else
- if (action==1):
- if (len (my_stack) > 1):
- My_stack.pop ()
- Gen_dot_script_file ()
- Os.system ("Dot-tjpg graph.dot-o output.jpg;rm-f Graph.dot");
Modify,/T becomes \t,/n to \ n. Added in the source oncreate of the calculator
- Debug.startmethodtracing ("Calc");
- LOG.V (log_tags "+++++++++++++++++++++++++test++++++++++++++++");
- Debug.stopmethodtracing ();
Run the script to get Calc.trace, draw the Out.jpg
Drawing crashes when the resulting trace file is more complex. Modify the script at the end of the dot command when the parameters, JPG format is too big to crash,-tjpg changed to-TPNG:GD, draw a large PNG.
I did an experiment with my camera and got a very large PNG, which is one of the corners: