| /* |
| * Copyright (C) 2012 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. |
| */ |
| |
| #define LOG_TAG "AudioWatchdog" |
| //#define LOG_NDEBUG 0 |
| |
| #include <utils/Log.h> |
| #include "AudioWatchdog.h" |
| |
| namespace android { |
| |
| void AudioWatchdogDump::dump(int fd) |
| { |
| char buf[32]; |
| if (mMostRecent != 0) { |
| // includes NUL terminator |
| ctime_r(&mMostRecent, buf); |
| } else { |
| strcpy(buf, "N/A\n"); |
| } |
| fdprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s", |
| mUnderruns, mLogs, buf); |
| } |
| |
| bool AudioWatchdog::threadLoop() |
| { |
| { |
| AutoMutex _l(mMyLock); |
| if (mPaused) { |
| mMyCond.wait(mMyLock); |
| // ignore previous timestamp after resume() |
| mOldTsValid = false; |
| // force an immediate log on first underrun after resume() |
| mLogTs.tv_sec = MIN_TIME_BETWEEN_LOGS_SEC; |
| mLogTs.tv_nsec = 0; |
| // caller will check for exitPending() |
| return true; |
| } |
| } |
| struct timespec newTs; |
| int rc = clock_gettime(CLOCK_MONOTONIC, &newTs); |
| if (rc != 0) { |
| pause(); |
| return false; |
| } |
| if (!mOldTsValid) { |
| mOldTs = newTs; |
| mOldTsValid = true; |
| return true; |
| } |
| time_t sec = newTs.tv_sec - mOldTs.tv_sec; |
| long nsec = newTs.tv_nsec - mOldTs.tv_nsec; |
| if (nsec < 0) { |
| --sec; |
| nsec += 1000000000; |
| } |
| mOldTs = newTs; |
| // cycleNs is same as sec*1e9 + nsec, but limited to about 4 seconds |
| uint32_t cycleNs = nsec; |
| if (sec > 0) { |
| if (sec < 4) { |
| cycleNs += sec * 1000000000; |
| } else { |
| cycleNs = 4000000000u; |
| } |
| } |
| mLogTs.tv_sec += sec; |
| if ((mLogTs.tv_nsec += nsec) >= 1000000000) { |
| mLogTs.tv_sec++; |
| mLogTs.tv_nsec -= 1000000000; |
| } |
| if (cycleNs > mMaxCycleNs) { |
| mDump->mUnderruns = ++mUnderruns; |
| if (mLogTs.tv_sec >= MIN_TIME_BETWEEN_LOGS_SEC) { |
| mDump->mLogs = ++mLogs; |
| mDump->mMostRecent = time(NULL); |
| ALOGW("Insufficient CPU for load: expected=%.1f actual=%.1f ms; underruns=%u logs=%u", |
| mPeriodNs * 1e-6, cycleNs * 1e-6, mUnderruns, mLogs); |
| mLogTs.tv_sec = 0; |
| mLogTs.tv_nsec = 0; |
| } |
| } |
| struct timespec req; |
| req.tv_sec = 0; |
| req.tv_nsec = mPeriodNs; |
| rc = nanosleep(&req, NULL); |
| if (!((rc == 0) || (rc == -1 && errno == EINTR))) { |
| pause(); |
| return false; |
| } |
| return true; |
| } |
| |
| void AudioWatchdog::requestExit() |
| { |
| // must be in this order to avoid a race condition |
| Thread::requestExit(); |
| resume(); |
| } |
| |
| void AudioWatchdog::pause() |
| { |
| AutoMutex _l(mMyLock); |
| mPaused = true; |
| } |
| |
| void AudioWatchdog::resume() |
| { |
| AutoMutex _l(mMyLock); |
| if (mPaused) { |
| mPaused = false; |
| mMyCond.signal(); |
| } |
| } |
| |
| void AudioWatchdog::setDump(AudioWatchdogDump *dump) |
| { |
| mDump = dump != NULL ? dump : &mDummyDump; |
| } |
| |
| } // namespace android |