1 /* 2 * Copyright 2017 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 #ifndef ANDROID_AUDIO_ERROR_LOG_H 18 #define ANDROID_AUDIO_ERROR_LOG_H 19 20 #ifdef __cplusplus 21 22 #include <iomanip> 23 #include <mutex> 24 #include <sstream> 25 #include <unistd.h> 26 #include <vector> 27 28 #include <audio_utils/clock.h> 29 #include <utils/Errors.h> 30 31 namespace android { 32 33 /** 34 * ErrorLog captures audio errors codes, combining consecutive identical error codes 35 * (within a specified time) into a single entry (to reduce log spamming). 36 * 37 * The entry thus contains the number of consecutive error codes, 38 * together with the first time the error code occurs and the last time the error code occurs. 39 * 40 * The type T represents the error code type and is an int32_t for the C API. 41 */ 42 template <typename T> 43 class ErrorLog { 44 public: 45 /** 46 * \brief Creates an ErrorLog object 47 * 48 * \param entries the length of error history. 49 * \param aggregateNs the maximum time in nanoseconds between identical error codes 50 * to be aggregated into a single entry. 51 */ 52 explicit ErrorLog(size_t entries, int64_t aggregateNs = 1000000000 /* one second */) 53 : mErrors(0) 54 , mIdx(0) 55 , mAggregateNs(aggregateNs) 56 , mEntries(entries) 57 { 58 } 59 60 /** 61 * \brief Adds new error code to the error log. 62 * 63 * Consecutive errors with the same code will be aggregated 64 * if they occur within aggregateNs. 65 * 66 * \param code error code of type T. 67 * \param nowNs current time in nanoseconds. 68 */ log(const T & code,int64_t nowNs)69 void log(const T &code, int64_t nowNs) 70 { 71 std::lock_guard<std::mutex> guard(mLock); 72 73 ++mErrors; 74 75 // Within mAggregateNs (1 second by default), aggregate error codes together. 76 if (code == mEntries[mIdx].mCode 77 && nowNs - mEntries[mIdx].mLastTime < mAggregateNs) { 78 mEntries[mIdx].mCount++; 79 mEntries[mIdx].mLastTime = nowNs; 80 return; 81 } 82 83 // Add new error entry. 84 if (++mIdx >= mEntries.size()) { 85 mIdx = 0; 86 } 87 mEntries[mIdx].setFirstError(code, nowNs); 88 } 89 90 /** 91 * \brief Dumps the log to a std::string. 92 * \param prefix the prefix to use for each line 93 * (generally a null terminated string of spaces). 94 * \param lines maximum number of lines to output (0 disables). 95 * \param limitNs limit dump to data more recent than limitNs (0 disables). 96 * \return std::string of the dump. 97 */ 98 std::string dumpToString(const char *prefix = "", size_t lines = 0, int64_t limitNs = 0) const 99 { 100 std::lock_guard<std::mutex> guard(mLock); 101 102 std::stringstream ss; 103 const size_t numberOfEntries = mEntries.size(); 104 const size_t headerLines = 2; 105 106 if (lines == 0) { 107 lines = SIZE_MAX; 108 } 109 ss << prefix << "Errors: " << mErrors << "\n"; 110 111 if (mErrors == 0 || lines <= headerLines) { 112 return ss.str(); 113 } 114 115 lines = std::min(lines - headerLines, numberOfEntries); 116 // compute where to start dump log 117 ssize_t offset; 118 for (offset = 0; offset < (ssize_t)lines; ++offset) { 119 const auto &entry = 120 mEntries[(mIdx + numberOfEntries - offset) % numberOfEntries]; 121 if (entry.mCount == 0 || entry.mLastTime < limitNs) { 122 break; 123 } 124 } 125 if (offset > 0) { 126 offset--; 127 ss << prefix << " Code Freq First time Last time\n"; 128 for (; offset >= 0; --offset) { 129 const auto &entry = 130 mEntries[(mIdx + numberOfEntries - offset) % numberOfEntries]; 131 132 ss << prefix << std::setw(5) << entry.mCode 133 << " " << std::setw(5) << entry.mCount 134 << " " << audio_utils_time_string_from_ns(entry.mFirstTime).time 135 << " " << audio_utils_time_string_from_ns(entry.mLastTime).time << "\n"; 136 } 137 } 138 return ss.str(); 139 } 140 141 /** 142 * \brief Dumps the log to a raw file descriptor. 143 * \param fd file descriptor to use. 144 * \param prefix the prefix to use for each line 145 * (generally a null terminated string of spaces). 146 * \param lines maximum number of lines to output (0 disables). 147 * \param limitNs limit dump to data more recent than limitNs (0 disables). 148 * \return 149 * NO_ERROR on success or a negative number (-errno) on failure of write(). 150 */ 151 status_t dump(int fd, const char *prefix = "", size_t lines = 0, int64_t limitNs = 0) const 152 { 153 // thread safe but not necessarily serial with respect to concurrent dumps to the same fd. 154 const std::string s = dumpToString(prefix, lines, limitNs); 155 if (s.size() > 0 && write(fd, s.c_str(), s.size()) < 0) { 156 return -errno; 157 } 158 return NO_ERROR; 159 } 160 161 struct Entry { EntryEntry162 Entry() 163 : mCode(0) 164 , mCount(0) 165 , mFirstTime(0) 166 , mLastTime(0) 167 { 168 } 169 170 // Initialize entry with code as the first error at the given time. setFirstErrorEntry171 void setFirstError(T code, int64_t time) { 172 mCode = code; 173 mCount = 1; 174 mFirstTime = time; 175 mLastTime = time; 176 } 177 178 T mCode; // error code 179 uint32_t mCount; // number of consecutive errors of the same code. 180 int64_t mFirstTime; // first time of the error code. 181 int64_t mLastTime; // last time of the error code. 182 }; 183 184 private: 185 mutable std::mutex mLock; // monitor mutex 186 int64_t mErrors; // total number of errors registered 187 size_t mIdx; // current index into mEntries (active) 188 const int64_t mAggregateNs; // number of nanoseconds to aggregate consecutive error codes. 189 std::vector<Entry> mEntries; // circular buffer of error entries. 190 }; 191 192 } // namespace android 193 194 #endif // __cplusplus 195 196 // C API (see C++ API above for details) 197 198 /** \cond */ 199 __BEGIN_DECLS 200 /** \endcond */ 201 202 typedef struct error_log_t error_log_t; 203 204 /** 205 * \brief Creates an error log object 206 * 207 * \param entries the length of error history. 208 * \param aggregate_ns the maximum time in nanoseconds between identical error codes 209 * to be aggregated into a single entry. 210 * \return the error log object or NULL on failure. 211 */ 212 error_log_t *error_log_create(size_t entries, int64_t aggregate_ns); 213 214 /** 215 * \brief Adds new error code to the error log. 216 * 217 * Consecutive errors with the same code will be aggregated if 218 * they occur within aggregate_ns. 219 * 220 * \param error_log object returned by create, if NULL nothing happens. 221 * \param code error code of type T. 222 * \param now_ns current time in nanoseconds. 223 */ 224 void error_log_log(error_log_t *error_log, int32_t code, int64_t now_ns); 225 226 /** 227 * \brief Dumps the log to a raw file descriptor. 228 * \param error_log object returned by create, if NULL nothing happens. 229 * \param prefix the prefix to use for each line 230 * (generally a null terminated string of spaces). 231 * \param fd file descriptor to use. 232 * \param lines maximum number of lines to output (0 disables). 233 * \param limit_ns limit dump to data more recent than limit_ns (0 disables). 234 * \return 235 * NO_ERROR on success or a negative number (-errno) on failure of write(). 236 * if power_log is NULL, BAD_VALUE is returned. 237 */ 238 int error_log_dump( 239 error_log_t *error_log, int fd, const char *prefix, size_t lines, int64_t limit_ns); 240 241 /** 242 * \brief Destroys the error log object. 243 * 244 * \param error_log object returned by create, if NULL nothing happens. 245 */ 246 void error_log_destroy(error_log_t *error_log); 247 248 /** \cond */ 249 __END_DECLS 250 /** \endcond */ 251 252 #endif // !ANDROID_AUDIO_ERROR_LOG_H 253