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

Watchdog سلامت سرویس‌های فروشنده و سرویس VHAL را رصد می‌کند و هرگونه فرآیند ناسالم را خاتمه می‌دهد. هنگامی که یک فرآیند ناسالم خاتمه می‌یابد، Watchdog وضعیت فرآیند را مانند سایر dumpهای 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 اضافه کنید (ماکرو 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);
    }

یک رشته اتصال‌دهنده را شروع کنید و کلاینت را ثبت کنید

نام رابط دیمن 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);
    }

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

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