Android 開機時間慢問題debug

開機流程


Step 1啓動電源以及系統啓動

當電源按下,引導芯片代碼開始從預定義的地方(固化在ROM)開始執行。加載引導程序到RAM,然後執行。

Step2 : bootloader

引導程序是在Android操作系統開始運行前的一個小程序。引導程序是運行的第一個程序,因此它是針對特定的主板與芯片的。

引導程序分兩個階段執行。第一個階段,檢測外部的RAM以及加載對第二階段有用的程序;第二階段,引導程序設置網絡、內存等等。這些對於運行內核是必要的,爲了達到特殊的目標,引導程序可以根據配置參數或者輸入數據設置內核。

Android引導程序可以在bootable\bootloader



Step3: kernel

內核啓動時,設置緩存、被保護存儲器、計劃列表,加載驅動。當內核完成系統設置,它首先在系統文件中尋找”init”文件,然後啓動root進程或者系統的第一個進程。

Step4init

init是第一個進程,我們可以說它是root進程或者說有進程的父進程。init進程有兩個責任,一是掛載目錄,比如/sys/dev/proc,二是運行init.rc腳本。

  • init進程可以在/system/core/init找到

  • init.rc文件可以在/system/core/rootdir/init.rc 

Step 5 : Native service 啓動

在此階段,會啓動android Native service 包括 Zygote,sufaceFlinger,media server,bootanimation 等。

Step 6Javaservice 啓動

在此階段,android 會啓動java的service。並進行package的掃描,包括所有的app以及其所有的package,並且會 check所有的app是否進行了oat的優化如果沒有則進行oat的優化。

Step 7:啓動Home界面

一旦系統服務在內存中跑起來了,Android便啓動home界面。Home界面啓動完成後,會check wallpaper和 keygurd 是否 draw 完,如果已經draw完,則會設置service.bootanim.exit
爲1,然後bootanimation 結束,界面顯示完畢。

Debug

從上面的流程知道,開機問題分爲kernel
部分和user
space 
部分,下面討論之。

Kernel

Kernel 又可分爲兩部分,包括bootloader 部分和driver 加載部分。

  1. bootloader

    我們可以從dmesg 中發現bootloderKPI並且計算其時間如:

     

    [   0.416193] KPI: Bootloader start count = 23762   //A LK 開始時間

    [   0.416205] KPI: Bootloader end count = 239354   //B LK 結束時間

    [   0.416212] KPI: Bootloader display count = 37127

    [   0.416219] KPI: Bootloader load kernel count = 2321

    [   0.416226] KPI: Kernel XXX timestamp = 260735 // C bootloader 完成時間

    [   0.416232] KPI: Kernel XXX Clock frequency = 32768   //D clock

     

    我們可以通過下面的算法來確認相關的時間。如果發現時間太長,請debug 相關部分。

    Step1 時間:A/D23762 /32768=0.72s

    LK 時間:(B-A)/D=(23935423762 /327686.57s

    Bootloader 時間:C/Dkmsg(C)= 260735 /32768-0.416226=7.54s

    需要注意的是如果是eng 版本Bootloader的時間會多增加5s

  2. driver

    在上面linux啓動階段(step3),linux會加載很多driver,以及console等。加載console是比較耗時的操作(0.5s4s),所以如果沒有必要,可以去掉console來優化啓動時間。對於其他driver需要注意的是touchdrivercamera sensorLCD driver

    我們也可以通過添加打印module initlog,來check每個module初始化時的時間。從而找到花費時間比較多的module

--- a/init/main.c

+++ b/init/main.c

@@ -785,7 +785,7 @@ int __init_or_module
do_one_initcall(initcall_t fn)

        if (initcall_blacklisted(fn))

                return
-EPERM;

-       if (initcall_debug)

+       if (1)

                ret =
do_one_initcall_debug(fn);



static int __init_or_module do_one_initcall_debug(initcall_t fn)
{
 ktime_t calltime, delta, rettime;
 unsigned long long duration;
 int ret;

 printk(KERN_DEBUG "calling  %pF @ %i\n", fn, task_pid_nr(current));
 calltime = ktime_get();
 ret = fn();
 rettime = ktime_get();
 delta = ktime_sub(rettime, calltime);
 duration = (unsigned long long) ktime_to_ns(delta) >> 10;
 printk(KERN_DEBUG "initcall %pF returned %d after %lld usecs\n",
   fn, ret, duration);

 return ret;
}




User Space

adb logcat -v threadtime -b events > logcat_envents.txt

adb logcat -v threadtime > logcat.txt


Boot Event

我們可以通過查看event來確定是哪部分花時比較多。

// user space 開始時間

07-06 22:18:00.136 I/boot_progress_start(  407): 15528  //systemclock.uptimemillis(),開機到當前時間,毫秒。

//Zygote 進程preload 開始時間 32bit zygote

07-06 22:18:03.846 I/boot_progress_preload_start(  407): 19238

//Zygote 進程preload 開始時間64bit zygote

07-06 22:18:04.551 I/boot_progress_preload_start(  408): 19943

//Zygote 進程preload 結束時間32bit zygot

07-06 22:18:06.313 I/boot_progress_preload_end(  407): 21705

//Zygote 進程preload 結束時間64bit zygote

07-06 22:18:06.356 I/boot_progress_preload_end(  408): 21747

//System server 開始運行時間

07-06 22:18:06.462 I/boot_progress_system_run( 2182): 21853

//Package Scan 開始

07-06 22:18:06.784 I/boot_progress_pms_start( 2182): 22176

//System 目錄開始scan

07-06 22:18:06.899 I/boot_progress_pms_system_scan_start(2182): 22290

//data 目錄開始scan

07-06 22:18:38.644 I/boot_progress_pms_data_scan_start(2182): 54036

//package scan 結束時間

07-06 22:18:38.660 I/boot_progress_pms_scan_end( 2182):54052

//package manager ready

07-06 22:18:38.882 I/boot_progress_pms_ready( 2182): 54274

//Activity manager ready,這個事件之後便會啓動home Activity

07-06 22:21:40.221 I/boot_progress_ams_ready( 2182): 235613

//HomeActivity 啓動完畢,系統將檢查目前所有的window是否畫完,如果所有的window(包括wallpaper Keyguard 等)都已經畫好,系統會設置屬性service.bootanim.exit值爲1.並且trigger下面的event


07-06 22:21:52.740 I/boot_progress_enable_screen( 2182):248132


Code://


void enableScreenAfterBoot() {
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,SystemClock.uptimeMillis()); //sent  boot_progress_enable_screen

        mWindowManager.enableScreenAfterBoot(); //結束 bootanimation

        synchronized (this) {
            updateEventDispatchingLocked();
        }
    }


 public void enableScreenAfterBoot() {
        synchronized(mWindowMap) {
            if (DEBUG_BOOT) {
                RuntimeException here = new RuntimeException("here");
                here.fillInStackTrace();
               ....
            }
            if (mSystemBooted) {
                return;
            }
            
            mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30*1000);
        }

        mPolicy.systemBooted();

        performEnableScreen(); //here 設置bootanimation exit 
    }

 public void performEnableScreen() {
        synchronized(mWindowMap) {
           ...
            if (mDisplayEnabled) {
                return;
            }
            if (!mSystemBooted && !mShowingBootMessages) {
                return;
            }

            // Don't enable the screen until all existing windows have been drawn.
            if (!mForceDisplayEnabled && checkWaitingForWindowsLocked()) {
                return;
            }

if (!mBootAnimationStopped) {
                // Do this one time.
                try {
                    IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
                    if (surfaceFlinger != null) {
                       ...
                        Parcel data = Parcel.obtain();
                        data.writeInterfaceToken("android.ui.ISurfaceComposer");
                        surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
                                data, null, 0);
                        data.recycle();
                    }
                } catch (RemoteException ex) {
                    Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!");
                }
                mBootAnimationStopped = true;
            }

           ....
    }


status_t BnSurfaceComposer::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
       .....
        case BOOT_FINISHED: {
            CHECK_INTERFACE(ISurfaceComposer, data, reply);
            bootFinished();
            return NO_ERROR;
        }


void SurfaceFlinger::bootFinished()
{
    const nsecs_t now = systemTime();
    const nsecs_t duration = now - mBootTime;
    ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
    mBootFinished = true;

    // wait patiently for the window manager death
    const String16 name("window");
    sp<IBinder> window(defaultServiceManager()->getService(name));
    if (window != 0) {
        window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
    }

    // stop boot animation
    // formerly we would just kill the process, but we now ask it to exit so it
    // can choose where to stop the animation.
    property_set("service.bootanim.exit", "1");
}

Bootanimation

在開機啓動過程中,bootanimationsurfaceflinger實例化時啓動,在其運行過程中會擇機檢查是否可以退出(service.bootanim.exit1),如果可以退出則會根據bootanimation

config退出。有時,當service.bootanim.exit1時,bootanimation沒有及時退出。我們就需要檢查其原因。

http://qoofan.com/read/R84bR212ld.html

發佈了4 篇原創文章 · 獲贊 7 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章