Android adb bugreport工具分析和使用- IT閱讀 - ITREAD01 ...

文章推薦指數: 80 %
投票人數:10人

adb bugreport > bugreport.txt. 即可生成bugreport文件。

但是有一個問題是,這個生成的文件有的時候異常龐大,能夠達到15M+,想一想對於一個txt文本 ... IT技術 Androidadbbugreport工具分析和使用 IT技術 · 發表2016-10-11 bugreport是什麽,怎麽用? android系統想要成為一個功能完備,生態繁榮的操作系統,那就必須提供完整的應用開發環境。

而在應用開發中,app程序的調試分析是日常生產中進程會進行的工作。

Android為了方便開發人員分析整個系統平臺和某個app在運行一段時間之內的所有信息,專門開發了bugreport工具。

這個工具使用起來十分簡單,只要在終端執行(linux或者win): adbbugreport>bugreport.txt 即可生成bugreport文件。

但是有一個問題是,這個生成的文件有的時候異常龐大,能夠達到15M+,想一想對於一個txt文本格式的文件內容長度達到了15M+是一個什麽概念,如果使用文本工具打開查看將是一個噩夢。

因此Google針對android5.0(api21)以上的系統開發了一個叫做batteryhistorian的分析工具,這個工具就是用來解析這個txt文本文件,然後使用web圖形的形式展現出來,這樣出來的效果更加人性化,更加可讀。

它的基本界面像下面這個樣子: 目前google已經將betteryhistorian開源了,開源項目的地址: https://github.com/google/battery-historian google寫了一個比較詳細的說明文檔,大家可以自行查閱一下。

這個工具可以查看以下信息: Brightness CPUrunning Chargingon Chargingstatus Health JobScheduler Kernelonlyuptime Level packageactive Partialwakelock Phonescanning Phonestate Plug Plugged Screen Temperature Topapp Voltage WIFIon Wifirunning Wifisupplicant 數據還是比較詳細的。

當然,同樣的bugreport數據也可以有不同的解析和閱讀方式,你如果不太喜歡google的batteryhistorian的話,你還有別的選擇,那就是選擇Sony開源的ChkBugReport,這個工具提供了不同於batteryhistorian的視角去解讀bugreport文件,見面簡單明了: 這個項目的文檔: http://developer.sonymobile.com/2012/01/25/new-bugreport-analysis-tool-released-as-open-source/ 開源地址首頁: https://github.com/sonyxperiadev/ChkBugReport 這裏說明一下,筆者使用過ChkBugReport這個工具,感覺很不錯,最好結合google的batteryhistorian;另外ChkBugReport這個工具還有一點bug,不過不影響使用。

bugreport的原理是什麽? 下面我們簡要分析一下adbbugreport運行的原理。

我們知道,使用bugreport只要執行adbbugreport命令就可以了,因此我們的分析肯定是從adbd這個daemon進程開始,我們查看這個進程的代碼的時候發現這裏處理了bugreport選項: [email protected]/core/adb/commandline.cpp 我們可以清楚地看到,這裏判斷如果附帶的參數是bugreport的話,那就直接調用send_shell_command函數處理,這個函數的代碼比較簡單,我們就不分析了,這個函數的功能就是使用shell執行參數中的命令,因此我們這裏相當於執行了bugreport命令。

在android設備中,bugreport命令存在於system/bin/目錄下,這是一個可執行文件,所以我們要查看這個可執行文件實現的地方,它的實現代碼在/frameworks/native/cmds/bugreport/目錄下: 我們看到,bugreport的實現是比較簡單的,只有一個Android.mk和一個cpp實現代碼,我們先看一下Android.mk文件: 這裏我們看到該目錄下的代碼會被編譯成一個名字叫做bugreport的可執行文件,這就是我們想要找的。

現在我們看一下bugreport.cpp文件的實現,這個文件中代碼比較簡單,只有一個main函數: //Thisprogramwilltriggerthedumpstateservicetostartacallto //dumpstate,thenconnecttothedumpstatelocalclienttoreadthe //output.Allofthedumpstateoutputiswrittentostdout,including //anyerrorsencounteredwhilereading/writingtheoutput. intmain(){ //Startthedumpstateservice. property_set("ctl.start","dumpstate"); //Socketwillnotbeavailableuntilservicestarts. ints; for(inti=0;i<20;i++){ s=socket_local_client("dumpstate",ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); if(s>=0) break; //Tryagainin1second. sleep(1); } if(s==-1){ printf("Failedtoconnecttodumpstateservice:%s\n",strerror(errno)); return1; } //Setatimeoutsothatifnothingisreadin3minutes,we'llstop //readingandquit.Notimeoutindumpstateislongerthan60seconds, //sothisgiveslotsofleewayincaseofunforeseentimeouts. structtimevaltv; tv.tv_sec=3*60; tv.tv_usec=0; if(setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv))==-1){ printf("WARNING:Cannotsetsockettimeout:%s\n",strerror(errno)); } while(1){ charbuffer[65536]; ssize_tbytes_read=TEMP_FAILURE_RETRY(read(s,buffer,sizeof(buffer))); if(bytes_read==0){ break; }elseif(bytes_read==-1){ //EAGAINreallymeanstimeout,sochangetheerrno. if(errno==EAGAIN){ errno=ETIMEDOUT; } printf("\nBugreportreadterminatedabnormally(%s).\n",strerror(errno)); break; } ssize_tbytes_to_send=bytes_read; ssize_tbytes_written; do{ bytes_written=TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer+bytes_read-bytes_to_send, bytes_to_send)); if(bytes_written==-1){ printf("Failedtowritedatatostdout:read%zd,tryingtosend%zd(%s)\n", bytes_read,bytes_to_send,strerror(errno)); return1; } bytes_to_send-=bytes_written; }while(bytes_written!=0&&bytes_to_send>0); } close(s); return0; } 這裏的代碼非常簡單,主要的邏輯就是: 1.啟動dumpstateservice 2.和dumpstateservice建立socket鏈接 3.從socket中讀取數據,並且答應到stdout中 4.讀取完成之後關閉socket,然後退出 因此,我們分析的重點需要轉移到dumpstate中了。

這裏說明一下,前面啟動dumpstateservice的方法是使用系統屬性來實現,這個屬性的改變消息會被init進程收到,然後init進程會啟動dumpstate這個服務。

dumpstate其實也是一個可執行文件,也存在於system/bin目錄下。

現在我們明白了,其實bugreport就是dumpstate,只是bugreport將dumpstate包裝了一下而已。

現在我們需要分析一下dumpstate的實現,它的實現代碼在:frameworks/native/cmds/dumpstate目錄下,我們看下這個目錄下的代碼結構: 這裏的代碼也是十分簡單,只要少數的幾個實現文件,其中main函數在dumpstate.c文件中,這個main函數我們這裏不詳細分析了,總結下它的主要工作: 1.根據啟動參數,初始化相關資源 2.如果啟動參數中帶有-s的話(init啟動會加上這個參數),就表示使用socket,那麽就啟動socket,並且在這個socket中等待鏈接。

3.如果client端(也就是bugreport進程)鏈接成功,那就初始化所要用到的內存,並且設置優先級為較高優先級,防止被OOM幹掉。

4.然後使用vibrator震動一下(如果設備有這個硬件的話),提示用戶開始截取log了 5.調用dumpstate函數開始真正的dump工作 6.dump完成之後再次調用vibrator震動3次,提示用戶dump完成。

現在我們看下dumpstate函數的實現: /*dumpsthecurrentsystemstatetostdout*/ staticvoiddumpstate(){ unsignedlongtimeout; time_tnow=time(NULL); charbuild[PROPERTY_VALUE_MAX],fingerprint[PROPERTY_VALUE_MAX]; charradio[PROPERTY_VALUE_MAX],bootloader[PROPERTY_VALUE_MAX]; charNetwork[PROPERTY_VALUE_MAX],date[80]; charbuild_type[PROPERTY_VALUE_MAX]; property_get("ro.build.display.id",build,"(unknown)"); property_get("ro.build.fingerprint",fingerprint,"(unknown)"); property_get("ro.build.type",build_type,"(unknown)"); property_get("ro.baseband",radio,"(unknown)"); property_get("ro.bootloader",bootloader,"(unknown)"); property_get("gsm.operator.alpha",network,"(unknown)"); strftime(date,sizeof(date),"%Y-%m-%d%H:%M:%S",localtime(&now)); printf("========================================================\n"); printf("==dumpstate:%s\n",date); printf("========================================================\n"); printf("\n"); printf("Build:%s\n",build); printf("Buildfingerprint:'%s'\n",fingerprint);/*formatisimportantforothertools*/ printf("Bootloader:%s\n",bootloader); printf("Radio:%s\n",radio); printf("Network:%s\n",network); printf("Kernel:"); dump_file(NULL,"/proc/version"); printf("Commandline:%s\n",strtok(cmdline_buf,"\n")); printf("\n"); dump_dev_files("TRUSTYVERSION","/sys/bus/platform/drivers/trusty","trusty_version"); run_command("UPTIME",10,"uptime",NULL); dump_files("UPTIMEMMCPERF",mmcblk0,skip_not_stat,dump_stat_from_fd); dump_file("MEMORYINFO","/proc/meminfo"); run_command("CPUINFO",10,"top","-n","1","-d","1","-m","30","-t",NULL); run_command("PROCRANK",20,"procrank",NULL); dump_file("VIRTUALMEMORYSTATS","/proc/vmstat"); dump_file("VMALLOCINFO","/proc/vmallocinfo"); dump_file("SLABINFO","/proc/slabinfo"); dump_file("ZONEINFO","/proc/zoneinfo"); dump_file("PAGETYPEINFO","/proc/pagetypeinfo"); dump_file("BUDDYINFO","/proc/buddyinfo"); dump_file("FRAGMENTATIONINFO","/d/extfrag/unusable_index"); dump_file("KERNELWAKELOCKS","/proc/wakelocks"); dump_file("KERNELWAKESOURCES","/d/wakeup_sources"); dump_file("KERNELCPUFREQ","/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state"); dump_file("KERNELSYNC","/d/sync"); run_command("PROCESSES",10,"ps","-P",NULL); run_command("PROCESSESANDTHREADS",10,"ps","-t","-p","-P",NULL); run_command("PROCESSES(SELINUXLABELS)",10,"ps","-Z",NULL); run_command("LIBRANK",10,"librank",NULL); do_dmesg(); run_command("LISTOFOPENFILES",10,SU_PATH,"root","lsof",NULL); for_each_pid(do_showmap,"SMAPSOFALLPROCESSES"); for_each_tid(show_wchan,"BLOCKEDPROCESSWAIT-CHANNELS"); if(screenshot_path[0]){ ALOGI("takingscreenshot\n"); run_command(NULL,10,"/system/bin/screencap","-p",screenshot_path,NULL); ALOGI("wrotescreenshot:%s\n",screenshot_path); } //dump_file("EVENTLOGTAGS","/etc/event-log-tags"); //calculatetimeout timeout=logcat_timeout("main")+logcat_timeout("system")+logcat_timeout("crash"); if(timeout<20000){ timeout=20000; } run_command("SYSTEMLOG",timeout/1000,"logcat","-v","threadtime","-d","*:v",NULL); timeout=logcat_timeout("events"); if(timeout<20000){ timeout=20000; } run_command("EVENTLOG",timeout/1000,"logcat","-b","events","-v","threadtime","-d","*:v",NULL); timeout=logcat_timeout("radio"); if(timeout<20000){ timeout=20000; } run_command("RADIOLOG",timeout/1000,"logcat","-b","radio","-v","threadtime","-d","*:v",NULL); run_command("LOGSTATISTICS",10,"logcat","-b","all","-S",NULL); /*showthetraceswecollectedinmain(),ifthatwasdone*/ if(dump_traces_path!=NULL){ dump_file("VMTRACESJUSTNOW",dump_traces_path); } /*onlyshowANRtracesifthey'relessthan15minutesold*/ structstatst; charanr_traces_path[PATH_MAX]; property_get("dalvik.vm.stack-trace-file",anr_traces_path,""); if(!anr_traces_path[0]){ printf("***NOVMTRACESFILEDEFINED(dalvik.vm.stack-trace-file)\n\n"); }else{ intfd=TEMP_FAILURE_RETRY(open(anr_traces_path, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK)); if(fd<0){ printf("***NOANRVMTRACESFILE(%s):%s\n\n",anr_traces_path,strerror(errno)); }else{ dump_file_from_fd("VMTRACESATLASTANR",anr_traces_path,fd); } } /*slowtracesforslowoperations*/ if(anr_traces_path[0]!=0){ inttail=strlen(anr_traces_path)-1; while(tail>0&&anr_traces_path[tail]!='/'){ tail--; } inti=0; while(1){ sprintf(anr_traces_path+tail+1,"slow%02d.txt",i); if(stat(anr_traces_path,&st)){ //Notracesfileatthisindex,donewiththefiles. break; } dump_file("VMTRACESWHENSLOW",anr_traces_path); i++; } } intdumped=0; for(size_ti=0;i0){ if(0==strncmp(build_type,"user",PROPERTY_VALUE_MAX-1)){ //sudoesnotexistonuserbuilds,sotryrunningwithoutit. //Thiswayanyimplementationsofvril-dumpthatdonotrequire //rootcanrunonuserbuilds. run_command("DUMPVENDORRILLOGS",atoi(ril_dumpstate_timeout), "vril-dump",NULL); }else{ run_command("DUMPVENDORRILLOGS",atoi(ril_dumpstate_timeout), SU_PATH,"root","vril-dump",NULL); } } printf("========================================================\n"); printf("==AndroidFrameworkServices\n"); printf("========================================================\n"); /*thefulldumpsysisstartingtotakealongtime,soweneed toincreaseitstimeout.wereallyneedtodothetimeoutsin dumpsysitself...*/ run_command("DUMPSYS",60,"dumpsys",NULL); printf("========================================================\n"); printf("==Checkins\n"); printf("========================================================\n"); run_command("CHECKINBATTERYSTATS",30,"dumpsys","batterystats","-c",NULL); run_command("CHECKINMEMINFO",30,"dumpsys","meminfo","--checkin",NULL); run_command("CHECKINNETSTATS",30,"dumpsys","netstats","--checkin",NULL); run_command("CHECKINPROCSTATS",30,"dumpsys","procstats","-c",NULL); run_command("CHECKINUSAGESTATS",30,"dumpsys","usagestats","-c",NULL); run_command("CHECKINPACKAGE",30,"dumpsys","package","--checkin",NULL); printf("========================================================\n"); printf("==RunningApplicationActivities\n"); printf("========================================================\n"); run_command("APPACTIVITIES",30,"dumpsys","activity","all",NULL); printf("========================================================\n"); printf("==RunningApplicationServices\n"); printf("========================================================\n"); run_command("APPSERVICES",30,"dumpsys","activity","service","all",NULL); printf("========================================================\n"); printf("==RunningApplicationProviders\n"); printf("========================================================\n"); run_command("APPSERVICES",30,"dumpsys","activity","provider","all",NULL); printf("========================================================\n"); printf("==dumpstate:done\n"); printf("========================================================\n"); } 上面的代碼比較長,是因為所要dump的模塊太多,但是基本邏輯還是比較清楚的,我們看到基本的數據來源就是: 1.系統屬性 2./proc和/sys節點文件 3.執行shell命令獲得相關輸出 4.logcat輸出 5.AndroidFrameworkServices信息基本使用dumpsys命令通過binder調用服務中的dump函數獲得信息 這裏我們需要看一下dumpsys命令的實現,這個命令也是比較簡單,實現全部在main函數中: intmain(intargc,char*constargv[]) { signal(SIGPIPE,SIG_IGN); spsm=defaultServiceManager(); fflush(stdout); if(sm==NULL){ ALOGE("Unabletogetdefaultservicemanager!"); aerr<services; Vectorargs; boolshowListOnly=false; if((argc==2)&&(strcmp(argv[1],"-l")==0)){ showListOnly=true; } if((argc==1)||showListOnly){ services=sm->listServices(); services.sort(sort_func); args.add(String16("-a")); }else{ services.add(String16(argv[1])); for(inti=2;i1){ //firstprintalistofthecurrentservices aout<service=sm->checkService(services[i]); if(service!=NULL){ aout<service=sm->checkService(services[i]); if(service!=NULL){ if(N>1){ aout<dump(STDOUT_FILENO,args); if(err!=0){ aerr<dump(STDOUT_FILENO,args); 這句來調用service的dump函數。

dumpstate會調用到所有binder中的service的dump函數,因為dumpstate函數執行了這一句: /*thefulldumpsysisstartingtotakealongtime,soweneed toincreaseitstimeout.wereallyneedtodothetimeoutsin dumpsysitself...*/ run_command("DUMPSYS",60,"dumpsys",NULL); 直接執行dumpsys,沒有參數,並且註釋中也說的很清楚,就是采集所有的信息。

這會執行以下service的dump函數(執行dumpsys|grep“DUMPOFSERVICE”可以看到): DUMPOFSERVICEDockObserver: DUMPOFSERVICESurfaceFlinger: DUMPOFSERVICEaccessibility: DUMPOFSERVICEaccount: DUMPOFSERVICEactivity: DUMPOFSERVICEalarm: DUMPOFSERVICEandroid.security.keystore: DUMPOFSERVICEandroid.service.gatekeeper.IGateKeeperService: DUMPOFSERVICEappops: DUMPOFSERVICEappwidget: DUMPOFSERVICEassetatlas: DUMPOFSERVICEaudio: DUMPOFSERVICEbackup: DUMPOFSERVICEbattery: DUMPOFSERVICEbatteryproperties: DUMPOFSERVICEbatterystats: DUMPOFSERVICEbluetooth_manager: DUMPOFSERVICEcarrier_config: DUMPOFSERVICEclipboard: DUMPOFSERVICEcommontime_management: DUMPOFSERVICEconnectivity: DUMPOFSERVICEconsumer_ir: DUMPOFSERVICEcontent: DUMPOFSERVICEcountry_detector: DUMPOFSERVICEcpuinfo: DUMPOFSERVICEdbinfo: DUMPOFSERVICEdevice_policy: DUMPOFSERVICEdeviceidle: DUMPOFSERVICEdevicestoragemonitor: DUMPOFSERVICEdiskstats: DUMPOFSERVICEdisplay: DUMPOFSERVICEdisplay.qservice: DUMPOFSERVICEdreams: DUMPOFSERVICEdrm.drmManager: DUMPOFSERVICEdropbox: DUMPOFSERVICEethernet: DUMPOFSERVICEfingerprint: DUMPOFSERVICEgfxinfo: DUMPOFSERVICEgraphicsstats: DUMPOFSERVICEimms: DUMPOFSERVICEinput: DUMPOFSERVICEinput_method: DUMPOFSERVICEiPhonesubinfo: DUMPOFSERVICEisms: DUMPOFSERVICEisub: DUMPOFSERVICEjobscheduler: DUMPOFSERVICElauncherapps: DUMPOFSERVICElocation: DUMPOFSERVICElock_settings: DUMPOFSERVICEmedia.audio_flinger: DUMPOFSERVICEmedia.audio_policy: DUMPOFSERVICEmedia.camera: DUMPOFSERVICEmedia.camera.proxy: DUMPOFSERVICEmedia.player: DUMPOFSERVICEmedia.radio: DUMPOFSERVICEmedia.resource_manager: DUMPOFSERVICEmedia.sound_trigger_hw: DUMPOFSERVICEmedia_projection: DUMPOFSERVICEmedia_router: DUMPOFSERVICEmedia_session: DUMPOFSERVICEmeminfo: DUMPOFSERVICEmidi: DUMPOFSERVICEmount: DUMPOFSERVICEnetpolicy: DUMPOFSERVICEnetstats: DUMPOFSERVICEnetwork_management: DUMPOFSERVICEnetwork_score: DUMPOFSERVICEnfc: DUMPOFSERVICEnotification: DUMPOFSERVICEpackage: DUMPOFSERVICEpermission: DUMPOFSERVICEpersistent_data_block: DUMPOFSERVICEphone: DUMPOFSERVICEpower: DUMPOFSERVICEprint: DUMPOFSERVICEprocessinfo: DUMPOFSERVICEprocstats: DUMPOFSERVICErestrictions: DUMPOFSERVICErttmanager: DUMPOFSERVICEsamplingprofiler: DUMPOFSERVICEscheduling_policy: DUMPOFSERVICEsearch: DUMPOFSERVICEsensorservice: DUMPOFSERVICEserial: DUMPOFSERVICEservicediscovery: DUMPOFSERVICEsimphonebook: DUMPOFSERVICEsip: DUMPOFSERVICEstatusbar: DUMPOFSERVICEtelecom: DUMPOFSERVICEtelephony.registry: DUMPOFSERVICEtextservices: DUMPOFSERVICEtrust: DUMPOFSERVICEuimode: DUMPOFSERVICEupdatelock: DUMPOFSERVICEusagestats: DUMPOFSERVICEusb: DUMPOFSERVICEuser: DUMPOFSERVICEvibrator: DUMPOFSERVICEvoiceinteraction: DUMPOFSERVICEwallpaper: DUMPOFSERVICEwebviewupdate: DUMPOFSERVICEwifi: DUMPOFSERVICEwifip2p: DUMPOFSERVICEwifiscanner: DUMPOFSERVICEwindow: 這裏總結以下,上面的bugreport整體邏輯如下圖描述(如果圖片太小看不清,請下載圖片並查看): adbbugreport的其他選項 bugreport本身並沒有什麽選項,主要是通過dumpsys等命令配合完成,詳見batteryhistorian項目主頁:https://github.com/google/battery-historian,以下是個總結: 1).重置電池統計信息: adbshelldumpsysbatterystats--reset 2).Wakelockanalysis全部wakelock信息: adbshelldumpsysbatterystats--enablefull-wake-history 3).Kerneltraceanalysis分析內核,主要分析wakeupsource和wakelockactivities,首先使能kernel分析: $adbroot $adbshell #Settheeventstotrace. $echo"power:wakeup_source_activate">>/d/tracing/set_event $echo"power:wakeup_source_deactivate">>/d/tracing/set_event #Thedefaulttracesizeformostdevicesis1MB,whichisrelativelylowandmightcausethelogstooverflow. #8MBto10MBshouldbeadecentsizefor5-6hoursoflogging. $echo8192>/d/tracing/buffer_size_kb $echo1>/d/tracing/tracing_on 然後獲得log: $echo0>/d/tracing/tracing_on $adbpull/d/tracing/trace #Takeabugreportatthistime. $adbbugreport>bugreport.txt 標籤:Androidandroidgooglebattery文本文件 您可能也會喜歡… 路由器逆向分析------firmware-mod-kit工具安裝和使用說明 esp8266smartconfig-智慧配網分析和使用及注意事項 iw工具安裝和使用 DexExtractor的原理分析和使用說明 filezilla和putty的ssh工具配置和使用: sysbench壓力測試工具簡介和使用(一) ENVI神經網路工具引數和使用方法 Android系統工具之Traceview的使用 抓包工具原理和使用 Cygwin工具安裝和使用指導書 Rust1.7.0的macro巨集-語法分析和使用舉例 Java併發集合(三)-ConcurrentHashMap分析和使用 Java併發集合(二)-ConcurrentSkipListMap分析和使用 聊聊併發(三)Java執行緒池的分析和使用 ViewController的生命週期分析和使用 贊助商廣告 redis之集群配置(主從復制) CentOS查看顯卡及GPU相關信息 linux三劍客正則表達 [ajax學習筆記]ajax初試 【記我憤怒的一次】通過灌水進行註冊北京進京證 內存動態分配與釋放 Mybatis最入門---代碼自動生成(generatorConfig.xml配置) 備忘錄|當我每晚閑暇時我在幹什麽(1) inux系統用戶名和全名有什麽區別 linux服務安裝與使用part2 Android WEB開發 其它 0.16069293022156



請為這篇文章評分?