نظارت بر سلامت سیستم

Watchdog بر سلامت سرویس های فروشنده و سرویس VHAL نظارت می کند و هر فرآیند ناسالم را خاتمه می دهد. هنگامی که یک فرآیند ناسالم خاتمه می‌یابد، Watchdog وضعیت فرآیند را مانند سایر برنامه‌های تخلیه‌شده Application Not Responding (ANR) به /data/anr می‌ریزد. انجام این کار فرآیند اشکال زدایی را تسهیل می کند.

نظارت بر سلامت خدمات فروشنده

خدمات فروشنده در هر دو سمت بومی و جاوا نظارت می شود. برای اینکه یک سرویس فروشنده نظارت شود، این سرویس باید با تعیین یک بازه زمانی از پیش تعریف شده، یک فرآیند بررسی سلامت را در 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 (macro 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);
    }

یک thread بایندر راه اندازی کنید و مشتری را ثبت کنید

نام واسط شبح نگهبان خودرو 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);
    }

نظارت بر سلامت سرویس جاوا

با به ارث بردن 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_HEARTBEAT سلامت سرویس VHAL را نظارت می کند. Watchdog انتظار دارد که ارزش این ویژگی هر N ثانیه یک بار به روز شود. هنگامی که ضربان قلب در این بازه زمانی به روز نمی شود، Watchdog سرویس VHAL را خاتمه می دهد.

توجه: Watchdog سلامت سرویس 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 Watchdog فرکانس بررسی سلامت VHAL را تعریف می کند. فرکانس پیش فرض بررسی سلامت (زمانی که این ویژگی تعریف نشده باشد) سه ثانیه است. اگر سه ثانیه برای سرویس VHAL برای به‌روزرسانی ویژگی خودروی VHAL_HEARTBEAT کافی نیست، فرکانس بررسی سلامت VHAL را بسته به پاسخگویی سرویس تعریف کنید.

اشکال زدایی فرآیندهای ناسالم پایان یافته توسط Watchdog

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. برای شناسایی علت اصلی عدم پاسخگویی، از فرآیند تخلیه ذخیره شده در /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 قرار دارد. تجزیه و تحلیل خود را با استفاده از فایل dump ANR فرآیند خاتمه یافته شروع کنید.