blob: 9f258656d1992c983ac28e6dd11bae329f706e83 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* The CameraService watchdog is used to help detect bad states in the
* Camera HAL. The threadloop uses cycle counters, assigned to each calling
* thread, to monitor the elapsing time and kills the process when the
* expected duration has exceeded.
* Notes on multi-threaded behaviors:
* - The threadloop is blocked/paused when there are no calls being
* monitored (when the TID cycle to counter map is empty).
* - The start and stop functions handle simultaneous call monitoring
* and single call monitoring differently. See function documentation for
* more details.
* To disable/enable:
* - adb shell cmd media.camera set-cameraservice-watchdog [0/1]
*/
#pragma once
#include <chrono>
#include <thread>
#include <time.h>
#include <utils/Thread.h>
#include <utils/Log.h>
#include <unordered_map>
#include "utils/CameraServiceProxyWrapper.h"
// Used to wrap the call of interest in start and stop calls
#define WATCH(toMonitor) watchThread([&]() { return toMonitor;}, gettid(), __FUNCTION__)
#define WATCH_CUSTOM_TIMER(toMonitor, cycles, cycleLength) \
watchThread([&]() { return toMonitor;}, gettid(), __FUNCTION__, cycles, cycleLength);
// Default cycles and cycle length values used to calculate permitted elapsed time
const static size_t kMaxCycles = 100;
const static uint32_t kCycleLengthMs = 100;
namespace android {
class CameraServiceWatchdog : public Thread {
struct MonitoredFunction {
uint32_t cycles;
std::string functionName;
};
public:
explicit CameraServiceWatchdog(const std::string &cameraId,
std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper) :
mCameraId(cameraId), mPause(true), mMaxCycles(kMaxCycles),
mCycleLengthMs(kCycleLengthMs), mEnabled(true),
mCameraServiceProxyWrapper(cameraServiceProxyWrapper) {};
explicit CameraServiceWatchdog (const std::string &cameraId, size_t maxCycles,
uint32_t cycleLengthMs, bool enabled,
std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper) :
mCameraId(cameraId), mPause(true), mMaxCycles(maxCycles),
mCycleLengthMs(cycleLengthMs), mEnabled(enabled),
mCameraServiceProxyWrapper(cameraServiceProxyWrapper) {};
virtual ~CameraServiceWatchdog() {};
virtual void requestExit();
/** Enables/disables the watchdog */
void setEnabled(bool enable);
/** Used to wrap monitored calls in start and stop functions using custom timer values */
template<typename T>
auto watchThread(T func, uint32_t tid, const char* functionName, uint32_t cycles,
uint32_t cycleLength) {
decltype(func()) res;
if (cycles != mMaxCycles || cycleLength != mCycleLengthMs) {
// Create another instance of the watchdog to prevent disruption
// of timer for current monitored calls
// Lock for mEnabled
mEnabledLock.lock();
sp<CameraServiceWatchdog> tempWatchdog = new CameraServiceWatchdog(
mCameraId, cycles, cycleLength, mEnabled, mCameraServiceProxyWrapper);
mEnabledLock.unlock();
status_t status = tempWatchdog->run("CameraServiceWatchdog");
if (status != OK) {
ALOGE("Unable to watch thread: %s (%d)", strerror(-status), status);
res = watchThread(func, tid, functionName);
return res;
}
res = tempWatchdog->watchThread(func, tid, functionName);
tempWatchdog->requestExit();
tempWatchdog.clear();
} else {
// If custom timer values are equivalent to set class timer values, use
// current thread
res = watchThread(func, tid, functionName);
}
return res;
}
/** Used to wrap monitored calls in start and stop functions using class timer values */
template<typename T>
auto watchThread(T func, uint32_t tid, const char* functionName) {
decltype(func()) res;
AutoMutex _l(mEnabledLock);
if (mEnabled) {
start(tid, functionName);
res = func();
stop(tid);
} else {
res = func();
}
return res;
}
private:
/**
* Start adds a cycle counter for the calling thread. When threadloop is blocked/paused,
* start() unblocks and starts the watchdog
*/
void start(uint32_t tid, const char* functionName);
/**
* If there are no calls left to be monitored, stop blocks/pauses threadloop
* otherwise stop() erases the cycle counter to end watchdog for the calling thread
*/
void stop(uint32_t tid);
std::string getAbortMessage(const std::string& functionName);
virtual bool threadLoop();
Mutex mWatchdogLock; // Lock for condition variable
Mutex mEnabledLock; // Lock for enabled status
Condition mWatchdogCondition; // Condition variable for stop/start
std::string mCameraId; // Camera Id the watchdog belongs to
bool mPause; // True if tid map is empty
uint32_t mMaxCycles; // Max cycles
uint32_t mCycleLengthMs; // Length of time elapsed per cycle
bool mEnabled; // True if watchdog is enabled
std::shared_ptr<CameraServiceProxyWrapper> mCameraServiceProxyWrapper;
std::unordered_map<uint32_t, MonitoredFunction> mTidMap; // Thread Id to MonitoredFunction type
// which retrieves the num of cycles
// and name of the function
};
} // namespace android