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