מעקב אחר תקינות המערכת

Watchdog עוקב אחר תקינות שירותי הספק ושירות VHAL, ומפסיק כל תהליך לא בריא. כאשר תהליך לא בריא מסתיים, ה-Watchdog משליך את סטטוס התהליך ל- /data/anr כמו ב-Applications Not Responding (ANR) אחרות. פעולה זו מקלה על תהליך איתור הבאגים.

ניטור תקינות שירות הספק

שירותי הספק מנוטרים הן בצד המקורי והן מהצד של Java. כדי ששירות ספק יהיה ניטור, על השירות לרשום תהליך בדיקת תקינות עם Watchdog על ידי ציון פסק זמן מוגדר מראש. Watchdog עוקב אחר תקינותו של תהליך בדיקת תקינות רשום על ידי פינג אותו במרווח יחסית לזמן הקצוב שצוין במהלך הרישום. כאשר תהליך פינג אינו מגיב בתוך הזמן הקצוב, התהליך נחשב כלא בריא.

ניטור בריאות שירות ילידים

ציין את קובץ ה-Watchdog AIDL

  1. כלול carwatchdog_aidl_interface-ndk_platform ב- shared_libs .

    Android.bp

    cc_binary {
        name: "sample_native_client",
        srcs: [
            "src/*.cpp"
        ],
        shared_libs: [
            "carwatchdog_aidl_interface-ndk_platform",
            "libbinder_ndk",
        ],
        vendor: true,
    }
    

הוסף מדיניות SELinux

  1. כדי להוסיף מדיניות SELinux, אפשר לתחום שירות הספק להשתמש ב-binder (מאקרו binder_use ) והוסף את תחום שירות הספק לדומיין הלקוח carwatchdog (מאקרו carwatchdog_client_domain ). עיין בקוד שלהלן עבור sample_client.te ו- file_contexts :

    sample_client.te

    type sample_client, domain;
    type sample_client_exec, exec_type, file_type, vendor_file_type;
    
    carwatchdog_client_domain(sample_client)
    
    init_daemon_domain(sample_client)
    binder_use(sample_client)
    

    file_contexts

    /vendor/bin/sample_native_client  u:object_r:sample_client_exec:s0
    

הטמע מחלקה של לקוח על ידי ירושה של BnCarWatchdogClient

  1. ב- checkIfAlive , בצע בדיקת תקינות. אפשרות אחת היא לפרסם במטפל לולאת השרשור. אם בריא, התקשר ל- ICarWatchdog::tellClientAlive . ראה את הקוד שלהלן עבור SampleNativeClient.h ו- SampleNativeClient.cpp :

    SampleNativeClient.h

    class SampleNativeClient : public BnCarWatchdogClient {
    public:
        ndk::ScopedAStatus checkIfAlive(int32_t sessionId, TimeoutLength
            timeout) override;
        ndk::ScopedAStatus prepareProcessTermination() override;
        void initialize();
    
    private:
        void respondToDaemon();
    private:
        ::android::sp<::android::Looper> mHandlerLooper;
        std::shared_ptr<ICarWatchdog> mWatchdogServer;
        std::shared_ptr<ICarWatchdogClient> mClient;
        int32_t mSessionId;
    };
    

    SampleNativeClient.cpp

    ndk::ScopedAStatus WatchdogClient::checkIfAlive(int32_t sessionId, TimeoutLength timeout) {
        mHandlerLooper->removeMessages(mMessageHandler,
            WHAT_CHECK_ALIVE);
        mSessionId = sessionId;
        mHandlerLooper->sendMessage(mMessageHandler,
            Message(WHAT_CHECK_ALIVE));
        return ndk::ScopedAStatus::ok();
    }
    // WHAT_CHECK_ALIVE triggers respondToDaemon from thread handler
    void WatchdogClient::respondToDaemon() {
      // your health checking method here
      ndk::ScopedAStatus status = mWatchdogServer->tellClientAlive(mClient,
            mSessionId);
    }
    

התחל חוט קלסר ורשום את הלקוח

שם ממשק Damon Car Watchdog הוא android.automotive.watchdog.ICarWatchdog/default .

  1. חפש את הדמון עם השם וקרא ל- ICarWatchdog::registerClient . עיין בקוד שלהלן עבור main.cpp ו- SampleNativeClient.cpp :

    main.cpp

    int main(int argc, char** argv) {
        sp<Looper> looper(Looper::prepare(/*opts=*/0));
    
        ABinderProcess_setThreadPoolMaxThreadCount(1);
        ABinderProcess_startThreadPool();
        std::shared_ptr<SampleNativeClient> client =
            ndk::SharedRefBase::make<SampleNatvieClient>(looper);
    
        // The client is registered in initialize()
        client->initialize();
        ...
    }
    

    SampleNativeClient.cpp

    void SampleNativeClient::initialize() {
        ndk::SpAIBinder binder(AServiceManager_getService(
            "android.automotive.watchdog.ICarWatchdog/default"));
        std::shared_ptr<ICarWatchdog> server =
            ICarWatchdog::fromBinder(binder);
        mWatchdogServer = server;
        ndk::SpAIBinder binder = this->asBinder();
        std::shared_ptr<ICarWatchdogClient> client =
            ICarWatchdogClient::fromBinder(binder)
        mClient = client;
        server->registerClient(client, TimeoutLength::TIMEOUT_NORMAL);
    }
    

ניטור תקינות שירות Java

הטמע לקוח על ידי ירושה של CarWatchdogClientCallback

  1. ערוך את הקובץ החדש באופן הבא:
    private final CarWatchdogClientCallback mClientCallback = new CarWatchdogClientCallback() {
        @Override
        public boolean onCheckHealthStatus(int sessionId, int timeout) {
            // Your health check logic here
            // Returning true implies the client is healthy
            // If false is returned, the client should call
            // CarWatchdogManager.tellClientAlive after health check is
            // completed
        }
    
        @Override
        public void onPrepareProcessTermination() {}
    };
    

רשום את הלקוח

  1. התקשר ל- CarWatchdogManager.registerClient() :
    private void startClient() {
        CarWatchdogManager manager =
            (CarWatchdogManager) car.getCarManager(
            Car.CAR_WATCHDOG_SERVICE);
        // Choose a proper executor according to your health check method
        ExecutorService executor = Executors.newFixedThreadPool(1);
        manager.registerClient(executor, mClientCallback,
            CarWatchdogManager.TIMEOUT_NORMAL);
    }
    

בטל את רישום הלקוח

  1. התקשר ל- CarWatchdogManager.unregisterClient() כאשר השירות יסתיים:
    private void finishClient() {
        CarWatchdogManager manager =
            (CarWatchdogManager) car.getCarManager(
            Car.CAR_WATCHDOG_SERVICE);
        manager.unregisterClient(mClientCallback);
    }
    

ניטור בריאות VHAL

שלא כמו ניטור תקינות שירות הספק, Watchdog מנטר את תקינות שירות VHAL על ידי הרשמה למאפיין הרכב VHAL_HEARTBEAT . Watchdog מצפה שהערך של נכס זה יתעדכן אחת ל-N שניות. כאשר פעימות הלב לא מתעדכנות תוך זמן קצוב זה, Watchdog מפסיק את שירות VHAL.

הערה: Watchdog עוקב אחר תקינות שירות VHAL רק כאשר מאפיין הרכב VHAL_HEARTBEAT נתמך על ידי שירות VHAL.

הטמעה פנימית של VHAL עשויה להשתנות בהתאם לספק. השתמש בדוגמאות הקוד הבאות כהפניות.

  1. רשום את נכס הרכב VHAL_HEARTBEAT .

    בעת הפעלת שירות VHAL, רשום את מאפיין הרכב VHAL_HEARTBEAT . בדוגמה שלמטה, unordered_map , הממפה את מזהה המאפיין ל-config, משמש כדי להחזיק את כל ההגדרות הנתמכות. תצורה עבור VHAL_HEARTBEAT מתווספת למפה, כך שכאשר מתבצע שאילתה VHAL_HEARTBEAT , התצורה המתאימה מוחזרת.

    void registerVhalHeartbeatProperty() {
            const VehiclePropConfig config = {
                    .prop = toInt(VehicleProperty::VHAL_HEARTBEAT),
                    .access = VehiclePropertyAccess::READ,
                    .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
            };
           // mConfigsById is declared as std::unordered_map<int32_t, VehiclePropConfig>.
           mConfigsById[config.prop] = config;
    }
    
  2. עדכן את מאפיין הרכב VHAL_HEARTBEAT .

    בהתבסס על תדירות בדיקת תקינות VHAL (הסבר בהגדרת תדירות בדיקת תקינות VHAL" ), עדכן את מאפיין הרכב VHAL_HEARTBEAT אחת ל-N שניות. אחת הדרכים לעשות זאת היא באמצעות ה- RecurrentTimer כדי לקרוא לפעולה הבודקת את תקינות ה-VHAL ו מעדכן את מאפיין הרכב VHAL_HEARTBEAT תוך זמן קצוב.

    להלן יישום לדוגמה באמצעות RecurrentTimer :

    int main(int argc, char** argv) {
            RecurrentTimer recurrentTimer(updateVhalHeartbeat);
            recurrentTimer.registerRecurrentEvent(kHeartBeatIntervalNs,
                                               static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT));
            … Run service …
            recurrentTimer.unregisterRecurrentEvent(
                    static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT));
    }
    
    void updateVhalHeartbeat(const std::vector<int32_t>& cookies) {
           for (int32_t property : cookies) {
                  if (property != static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT)) {
                         continue;
                  }
    
                  // Perform internal health checking such as retrieving a vehicle property to ensure
                  // the service is responsive.
                  doHealthCheck();
    
                  // Construct the VHAL_HEARTBEAT property with system uptime.
                  VehiclePropValuePool valuePool;
                  VehicleHal::VehiclePropValuePtr propValuePtr = valuePool.obtainInt64(uptimeMillis());
                  propValuePtr->prop = static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT);
                  propValuePtr->areaId = 0;
                  propValuePtr->status = VehiclePropertyStatus::AVAILABLE;
                  propValuePtr->timestamp = elapsedRealtimeNano();
    
                  // Propagate the HAL event.
                  onHalEvent(std::move(propValuePtr));
           }
    }
    
  3. ( אופציונלי ) הגדר את התדירות של בדיקת תקינות VHAL.

    מאפיין המוצר לקריאה בלבד ro.carwatchdog.vhal_healthcheck.interval של Watchdog מגדיר את תדירות בדיקת התקינות של VHAL. תדירות בדיקת תקינות ברירת המחדל (כאשר מאפיין זה אינו מוגדר) היא שלוש שניות. אם שלוש שניות אינן מספיקות לשירות VHAL לעדכן את מאפיין הרכב VHAL_HEARTBEAT , הגדר את תדירות בדיקת התקינות של VHAL בהתאם לתגובת השירות.

ניפוי באגים בתהליכים לא בריאים שהופסקו על ידי כלב השמירה

Watchdog משליך את מצב התהליך ומפסיק תהליכים לא בריאים. בעת סיום תהליך לא בריא, Watchdog רושם את הטקסט carwatchdog terminated <process name> (pid:<process id>) ל-logcat. שורת יומן זה מספקת מידע על התהליך שהופסק כמו שם התהליך ומזהה התהליך.

  1. ניתן לחפש ב-logcat את הטקסט שהוזכר לעיל על ידי הפעלת:
    $ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"
    

    לדוגמה, כאשר אפליקציית KitchenSink היא לקוח Watchdog רשום ואינה מגיבה לפינגים Watchdog, Watchdog רושם שורה כמו השורה למטה בעת סיום תהליך KitchenSink הרשום.

    05-01 09:50:19.683   578  5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
    
  2. כדי לזהות את סיבת השורש של חוסר ההיענות, השתמש ב-proces dump המאוחסן ב- /data/anr בדיוק כפי שהיית משתמש עבור מקרי ANR של פעילות. כדי לאחזר את קובץ ה-dump עבור התהליך שהופסק, השתמש בפקודות שלהלן.
    $ adb root
    $ adb shell grep -Hn "pid process_pid" /data/anr/*
    

    הפלט לדוגמה הבא הוא ספציפי לאפליקציית KitchenSink:

    $ adb shell su root grep -Hn "pid 5574" /data/anr/*.
    
    /data/anr/anr_2020-05-01-09-50-18-290:3:----- pid 5574 at 2020-05-01 09:50:18 -----
    /data/anr/anr_2020-05-01-09-50-18-290:285:----- Waiting Channels: pid 5574 at 2020-05-01 09:50:18 -----
    

    קובץ ה-dump עבור תהליך KitchenSink שהופסק נמצא בכתובת /data/anr/anr_2020-05-01-09-50-18-290 . התחל את הניתוח שלך באמצעות קובץ ה-ANR dump של התהליך שהופסק.