Home | History | Annotate | Download | only in audio_utils
      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      */
     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 {
    162         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.
    171         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