Мониторинг состояния системы

Сторожевой таймер отслеживает работоспособность служб поставщика и службы VHAL и завершает любой неработоспособный процесс. При завершении неработоспособного процесса сторожевой таймер сохраняет состояние процесса в /data/anr как и другие дампы, связанные с состоянием приложения, не отвечающего (ANR). Это облегчает процесс отладки.

Мониторинг работоспособности услуг поставщика

Мониторинг сервисов Vendor осуществляется как на стороне нативного кода, так и на стороне Java. Для мониторинга сервиса Vendor он должен зарегистрировать процесс проверки работоспособности в Watchdog, указав предопределённый тайм-аут. Watchdog отслеживает работоспособность зарегистрированного процесса проверки работоспособности, отправляя ему пинги с интервалом, зависящим от тайм-аута, указанного при регистрации. Если пингуемый процесс не отвечает в течение этого тайм-аута, он считается неработоспособным.

Мониторинг работоспособности собственной службы

Укажите make-файл 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_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);
    }

Начните ветку обсуждения и зарегистрируйте клиента

Имя интерфейса демона автомобильного сторожевого таймера — 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.

Примечание: сторожевой таймер отслеживает работоспособность службы VHAL только в том случае, если свойство транспортного средства VHAL_HEARTBEAT поддерживается службой VHAL.

Внутренняя реализация VHAL может различаться у разных поставщиков. Используйте следующие примеры кода в качестве справочных материалов.

  1. Зарегистрируйте свойство транспортного средства VHAL_HEARTBEAT .

    При запуске службы VHAL зарегистрируйте свойство транспортного средства VHAL_HEARTBEAT . В приведённом ниже примере unordered_map , сопоставляющий идентификатор свойства с конфигурацией, используется для хранения всех поддерживаемых конфигураций. Конфигурация для 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 , доступное только для чтения, определяет частоту проверки работоспособности VHAL. Частота проверки работоспособности по умолчанию (если это свойство не определено) составляет три секунды. Если трёх секунд недостаточно для обновления службой VHAL свойства транспортного средства VHAL_HEARTBEAT , определите частоту проверки работоспособности VHAL в зависимости от скорости отклика службы.

Отладка нездоровых процессов, завершенных сторожевым таймером

Сторожевой таймер выводит состояние процесса и завершает неработоспособные процессы. При завершении неработоспособного процесса сторожевой таймер записывает в logcat текст carwatchdog terminated <process name> (pid:<process id>) . Эта строка журнала содержит информацию о завершённом процессе, такую ​​как имя и идентификатор процесса.

  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. Чтобы определить первопричину отсутствия ответа, используйте дамп процесса, хранящийся в каталоге /data/anr так же, как и в случаях ошибок ANR. Чтобы получить файл дампа для завершённого процесса, выполните следующие команды.
    $ 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 -----

    Файл дампа для завершённого процесса KitchenSink находится по адресу /data/anr/anr_2020-05-01-09-50-18-290 . Начните анализ, используя файл дампа ANR завершённого процесса.