Monitorowanie stanu systemu

Usługa Watchdog monitoruje stan usług dostawcy i usługi VHAL oraz zamyka wszystkie nieprawidłowo działające procesy. Gdy nieprawidłowy proces zostanie zakończony, Watchdog zapisze stan procesu w /data/anr, tak jak w przypadku innych zrzutów typu „Aplikacja nie odpowiada” (ANR). Ułatwi to proces debugowania.

Monitorowanie stanu usług dostawców

Usługi dostawców są monitorowane zarówno po stronie natywnej, jak i po stronie Javy. Aby usługa dostawcy była monitorowana, musi zarejestrować proces sprawdzania stanu w usłudze Watchdog, określając wstępnie zdefiniowany limit czasu. Usługa Watchdog monitoruje stan zarejestrowanego procesu sprawdzania stanu, wysyłając do niego pingi w interwale powiązanym z limitem czasu określonym podczas rejestracji. Jeśli proces, do którego wysłano ping, nie odpowie przed upływem czasu oczekiwania, zostanie uznany za nieprawidłowy.

Natywne monitorowanie stanu usługi

Określanie pliku makefile interfejsu AIDL watchdoga

  1. Uwzględnij carwatchdog_aidl_interface-ndk_platform w shared_libs.

    Android.bp

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

Dodawanie zasad SELinux

  1. Aby dodać zasadę SELinux, zezwól domenie usługi dostawcy na używanie bindera (makro binder_use) i dodaj domenę usługi dostawcy do domeny klienta carwatchdog (makro carwatchdog_client_domain). Poniżej znajdziesz kod dla sample_client.tefile_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

Zaimplementuj klasę klienta, dziedzicząc po klasie BnCarWatchdogClient.

  1. checkIfAlive przeprowadź kontrolę stanu. Jedną z opcji jest wysłanie postu do modułu obsługi pętli wątków. Jeśli jesteś zdrowy, zadzwoń pod numer ICarWatchdog::tellClientAlive. Poniżej znajdziesz kod dla SampleNativeClient.hSampleNativeClient.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);
    }

Rozpocznij wątek w aplikacji Binder i zarejestruj klienta

Nazwa interfejsu demona watchdoga w samochodzie to android.automotive.watchdog.ICarWatchdog/default.

  1. Wyszukaj demona o nazwie i wywołaj ICarWatchdog::registerClient. Poniżej znajdziesz kod dla main.cppSampleNativeClient.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);
    }

Monitorowanie stanu usług Java

Implementowanie klienta przez dziedziczenie CarWatchdogClientCallback

  1. Zmodyfikuj nowy plik w ten sposób:
    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() {}
    };

Rejestrowanie klienta

  1. Zadzwoń pod numer 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);
    }

Wyrejestrowanie klienta

  1. Zadzwoń pod numer CarWatchdogManager.unregisterClient(), gdy usługa zostanie wykonana:
    private void finishClient() {
        CarWatchdogManager manager =
            (CarWatchdogManager) car.getCarManager(
            Car.CAR_WATCHDOG_SERVICE);
        manager.unregisterClient(mClientCallback);
    }

Monitorowanie stanu VHAL

W przeciwieństwie do monitorowania stanu usług dostawcy Watchdog monitoruje stan usługi VHAL, subskrybując VHAL_HEARTBEAT właściwość pojazdu. Usługa Watchdog oczekuje, że wartość tej właściwości będzie aktualizowana co N sekund. Jeśli w tym czasie nie zostanie zaktualizowany sygnał, Watchdog zakończy działanie usługi VHAL.

Uwaga: usługa Watchdog monitoruje stan usługi VHAL tylko wtedy, gdy usługa VHAL obsługuje VHAL_HEARTBEAT właściwość pojazdu.

Wewnętrzna implementacja VHAL może się różnić w zależności od dostawcy. Skorzystaj z tych przykładowych kodów.

  1. Zarejestruj właściwość pojazdu VHAL_HEARTBEAT.

    Podczas uruchamiania usługi VHAL zarejestruj VHAL_HEARTBEAT właściwość pojazdu. W przykładzie poniżej użyto unordered_map, które mapuje identyfikator usługi na konfigurację, do przechowywania wszystkich obsługiwanych konfiguracji. Konfiguracja dla VHAL_HEARTBEAT jest dodawana do mapy, dzięki czemu po wysłaniu zapytania o VHAL_HEARTBEAT zwracana jest odpowiednia konfiguracja.

    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. Zaktualizuj właściwość pojazdu VHAL_HEARTBEAT.

    Na podstawie częstotliwości sprawdzania stanu VHAL (wyjaśnionej w sekcji Określanie częstotliwości sprawdzania stanu VHAL) aktualizuj VHAL_HEARTBEAT właściwość pojazdu co N sekund. Możesz to zrobić, używając RecurrentTimer, aby wywołać działanie, które sprawdza stan VHAL i aktualizuje właściwość VHAL_HEARTBEAT pojazdu w ramach limitu czasu.

    Poniżej znajdziesz przykładową implementację z użyciem 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. (Opcjonalnie) Określ częstotliwość kontroli stanu VHAL.

    Właściwość produktu ro.carwatchdog.vhal_healthcheck.intervaltylko do odczytu Watchdog określa częstotliwość kontroli stanu VHAL. Domyślna częstotliwość kontroli stanu (gdy ta właściwość nie jest zdefiniowana) wynosi 3 sekundy. Jeśli 3 sekundy to za mało, aby usługa VHAL zaktualizowała VHAL_HEARTBEATwłaściwość pojazduVHAL_HEARTBEAT, określ częstotliwość kontroli stanu VHAL w zależności od szybkości reakcji usługi.

Debugowanie procesów w złym stanie zakończonych przez Watchdoga

Usługa Watchdog zrzuca stan procesu i zamyka nieprawidłowe procesy. Podczas zamykania nieprawidłowego procesu Watchdog rejestruje w logcat tekst carwatchdog terminated <process name> (pid:<process id>). Ten wiersz logu zawiera informacje o zakończonym procesie, takie jak jego nazwa i identyfikator.

  1. Aby wyszukać w logcat wspomniany tekst, uruchom to polecenie:
    $ adb logcat -s CarServiceHelper | fgrep "carwatchdog killed"

    Jeśli na przykład aplikacja KitchenSink jest zarejestrowanym klientem usługi Watchdog i przestaje odpowiadać na pingi Watchdog, usługa Watchdog rejestruje w logach wiersz podobny do poniższego, gdy kończy zarejestrowany proces KitchenSink.

    05-01 09:50:19.683   578  5777 W CarServiceHelper: carwatchdog killed com.google.android.car.kitchensink (pid: 5574)
  2. Aby zidentyfikować główną przyczynę braku odpowiedzi, użyj zrzutu procesu przechowywanego w /data/anr, tak jak w przypadku błędów ANR związanych z aktywnością. Aby pobrać plik zrzutu dla zakończonego procesu, użyj tych poleceń.
    $ adb root
    $ adb shell grep -Hn "pid process_pid" /data/anr/*

    Poniższe przykładowe dane wyjściowe dotyczą aplikacji 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 -----

    Plik zrzutu zakończonego procesu KitchenSink znajduje się w lokalizacji /data/anr/anr_2020-05-01-09-50-18-290. Rozpocznij analizę, korzystając z pliku zrzutu ANR zakończonego procesu.