Glenn Kasten | c15d665 | 2012-05-30 14:52:57 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2012 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #define LOG_TAG "AudioWatchdog" |
| 18 | //#define LOG_NDEBUG 0 |
| 19 | |
Glenn Kasten | 153b9fe | 2013-07-15 11:23:36 -0700 | [diff] [blame] | 20 | #include "Configuration.h" |
Glenn Kasten | c15d665 | 2012-05-30 14:52:57 -0700 | [diff] [blame] | 21 | #include <utils/Log.h> |
| 22 | #include "AudioWatchdog.h" |
| 23 | |
Glenn Kasten | 153b9fe | 2013-07-15 11:23:36 -0700 | [diff] [blame] | 24 | #ifdef AUDIO_WATCHDOG |
| 25 | |
Glenn Kasten | c15d665 | 2012-05-30 14:52:57 -0700 | [diff] [blame] | 26 | namespace android { |
| 27 | |
| 28 | void AudioWatchdogDump::dump(int fd) |
| 29 | { |
| 30 | char buf[32]; |
| 31 | if (mMostRecent != 0) { |
| 32 | // includes NUL terminator |
| 33 | ctime_r(&mMostRecent, buf); |
| 34 | } else { |
| 35 | strcpy(buf, "N/A\n"); |
| 36 | } |
Elliott Hughes | 8b5f642 | 2014-05-22 01:22:06 -0700 | [diff] [blame] | 37 | dprintf(fd, "Watchdog: underruns=%u, logs=%u, most recent underrun log at %s", |
Glenn Kasten | c15d665 | 2012-05-30 14:52:57 -0700 | [diff] [blame] | 38 | mUnderruns, mLogs, buf); |
| 39 | } |
| 40 | |
| 41 | bool AudioWatchdog::threadLoop() |
| 42 | { |
| 43 | { |
| 44 | AutoMutex _l(mMyLock); |
| 45 | if (mPaused) { |
| 46 | mMyCond.wait(mMyLock); |
| 47 | // ignore previous timestamp after resume() |
| 48 | mOldTsValid = false; |
| 49 | // force an immediate log on first underrun after resume() |
| 50 | mLogTs.tv_sec = MIN_TIME_BETWEEN_LOGS_SEC; |
| 51 | mLogTs.tv_nsec = 0; |
| 52 | // caller will check for exitPending() |
| 53 | return true; |
| 54 | } |
| 55 | } |
| 56 | struct timespec newTs; |
| 57 | int rc = clock_gettime(CLOCK_MONOTONIC, &newTs); |
| 58 | if (rc != 0) { |
| 59 | pause(); |
| 60 | return false; |
| 61 | } |
| 62 | if (!mOldTsValid) { |
| 63 | mOldTs = newTs; |
| 64 | mOldTsValid = true; |
| 65 | return true; |
| 66 | } |
| 67 | time_t sec = newTs.tv_sec - mOldTs.tv_sec; |
| 68 | long nsec = newTs.tv_nsec - mOldTs.tv_nsec; |
| 69 | if (nsec < 0) { |
| 70 | --sec; |
| 71 | nsec += 1000000000; |
| 72 | } |
| 73 | mOldTs = newTs; |
| 74 | // cycleNs is same as sec*1e9 + nsec, but limited to about 4 seconds |
| 75 | uint32_t cycleNs = nsec; |
| 76 | if (sec > 0) { |
| 77 | if (sec < 4) { |
| 78 | cycleNs += sec * 1000000000; |
| 79 | } else { |
| 80 | cycleNs = 4000000000u; |
| 81 | } |
| 82 | } |
| 83 | mLogTs.tv_sec += sec; |
| 84 | if ((mLogTs.tv_nsec += nsec) >= 1000000000) { |
| 85 | mLogTs.tv_sec++; |
| 86 | mLogTs.tv_nsec -= 1000000000; |
| 87 | } |
| 88 | if (cycleNs > mMaxCycleNs) { |
| 89 | mDump->mUnderruns = ++mUnderruns; |
| 90 | if (mLogTs.tv_sec >= MIN_TIME_BETWEEN_LOGS_SEC) { |
| 91 | mDump->mLogs = ++mLogs; |
| 92 | mDump->mMostRecent = time(NULL); |
| 93 | ALOGW("Insufficient CPU for load: expected=%.1f actual=%.1f ms; underruns=%u logs=%u", |
| 94 | mPeriodNs * 1e-6, cycleNs * 1e-6, mUnderruns, mLogs); |
| 95 | mLogTs.tv_sec = 0; |
| 96 | mLogTs.tv_nsec = 0; |
| 97 | } |
| 98 | } |
| 99 | struct timespec req; |
| 100 | req.tv_sec = 0; |
| 101 | req.tv_nsec = mPeriodNs; |
| 102 | rc = nanosleep(&req, NULL); |
| 103 | if (!((rc == 0) || (rc == -1 && errno == EINTR))) { |
| 104 | pause(); |
| 105 | return false; |
| 106 | } |
| 107 | return true; |
| 108 | } |
| 109 | |
| 110 | void AudioWatchdog::requestExit() |
| 111 | { |
| 112 | // must be in this order to avoid a race condition |
| 113 | Thread::requestExit(); |
| 114 | resume(); |
| 115 | } |
| 116 | |
| 117 | void AudioWatchdog::pause() |
| 118 | { |
| 119 | AutoMutex _l(mMyLock); |
| 120 | mPaused = true; |
| 121 | } |
| 122 | |
| 123 | void AudioWatchdog::resume() |
| 124 | { |
| 125 | AutoMutex _l(mMyLock); |
| 126 | if (mPaused) { |
| 127 | mPaused = false; |
| 128 | mMyCond.signal(); |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | void AudioWatchdog::setDump(AudioWatchdogDump *dump) |
| 133 | { |
| 134 | mDump = dump != NULL ? dump : &mDummyDump; |
| 135 | } |
| 136 | |
| 137 | } // namespace android |
Glenn Kasten | 153b9fe | 2013-07-15 11:23:36 -0700 | [diff] [blame] | 138 | |
| 139 | #endif // AUDIO_WATCHDOG |