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_SIMPLE_LOG_H
     18 #define ANDROID_AUDIO_SIMPLE_LOG_H
     19 
     20 #include <deque>
     21 #include <mutex>
     22 #include <sstream>
     23 #include <stdint.h>
     24 #include <string>
     25 #include <unistd.h>
     26 #include <utils/Errors.h>
     27 
     28 #include <audio_utils/clock.h>
     29 
     30 namespace android {
     31 
     32 /**
     33  * SimpleLog provides a private logcat-style logging to avoid cluttering
     34  * the device logcat.
     35  *
     36  * The public methods are internally protected by a mutex to be thread-safe.
     37  * Do not call from a sched_fifo thread as it can use a system time call
     38  * and obtains a local mutex.
     39  *
     40  * Formatted logs by log() and logv() will be truncated at kMaxStringLength - 1
     41  * due to null termination. logs() does not have a string length limitation.
     42  */
     43 
     44 class SimpleLog {
     45 public:
     46     /**
     47      * \brief Creates a SimpleLog object.
     48      *
     49      * \param maxLogLines the maximum number of log lines.
     50      */
     51     explicit SimpleLog(size_t maxLogLines = kDefaultMaxLogLines)
     52         : mMaxLogLines(maxLogLines)
     53     {
     54     }
     55 
     56     /**
     57      * \brief Adds a formatted string into the log.
     58      *
     59      * Time is automatically associated with the string by audio_utils_get_real_time_ns().
     60      *
     61      * \param format            the format string, similar to printf().
     62      *
     63      * and optional arguments.
     64      */
     65     // using C++11 unified attribute syntax; index is offset by 1 for implicit "this".
     66     [[gnu::format(printf, 2 /* string-index */, 3 /* first-to-check */)]]
     67     void log(const char *format, ...)
     68     {
     69         va_list args;
     70         va_start(args, format);
     71         // use -1 to trigger the clock fetch within the mutex lock.
     72         logv(-1 /* nowNs */, format, args);
     73         va_end(args);
     74     }
     75 
     76     /**
     77      * \brief Adds a formatted string into the log with time.
     78      *
     79      * \param nowNs             the time to use for logging. Assumed to be monotonically
     80      *                          increasing for sequential calls.  If -1, then
     81      *                          audio_utils_get_real_time_ns() is called.
     82      * \param format            the format string, similar to printf().
     83      *
     84      * and optional arguments.
     85      */
     86     // using C++11 unified attribute syntax; index is offset by 1 for implicit "this".
     87     [[gnu::format(printf, 3 /* string-index */, 4 /* first-to-check */)]]
     88     void log(int64_t nowNs, const char *format, ...)
     89     {
     90         va_list args;
     91         va_start(args, format);
     92         logv(nowNs, format, args);
     93         va_end(args);
     94     }
     95 
     96     /**
     97      * \brief Adds a formatted string by va_list with time.  Not intended for typical use.
     98      *
     99      * \param nowNs             the time to use for logging. Assumed to be monotonically
    100      *                          increasing for sequential calls.  If -1, then
    101      *                          audio_utils_get_real_time_ns() is called.
    102      * \param format            the format string, similar to printf().
    103      * \param args              va_list args.
    104      */
    105     void logv(int64_t nowNs, const char *format, va_list args)
    106     {
    107         // format to buffer
    108         char buffer[kMaxStringLength];
    109         int length = vsnprintf(buffer, sizeof(buffer), format, args);
    110         if (length < 0) { // encoding error
    111             logs(nowNs, "invalid format");
    112             return;
    113         } else if (length >= (signed)sizeof(buffer)) {
    114             length = sizeof(buffer) - 1;
    115         }
    116 
    117         // strip out trailing newlines
    118         while (length > 0 && buffer[length - 1] == '\n') {
    119             buffer[--length] = '\0';
    120         }
    121         logs(nowNs, buffer);
    122     }
    123 
    124     /**
    125      * \brief Logs a string to the buffer with time.
    126      * \param nowNs             the time to use for logging. Assumed to be monotonically
    127      *                          increasing for sequential calls.  If -1, then
    128      *                          audio_utils_get_real_time_ns() is called.
    129      * \param buffer            contains a null terminated string, which may have
    130      *                          special characters such as % and \ that are
    131      *                          not interpreted.
    132      */
    133     void logs(int64_t nowNs, const char *buffer)
    134     {
    135         // store in circular array
    136         std::lock_guard<std::mutex> guard(mLock);
    137         if (nowNs == -1) {
    138             nowNs = audio_utils_get_real_time_ns();
    139         }
    140         mLog.emplace_back(nowNs, std::string(buffer));
    141         if (mLog.size() > mMaxLogLines) {
    142             mLog.pop_front();
    143         }
    144     }
    145 
    146     /**
    147      * \brief Dumps the log to a string.
    148      *
    149      * \param prefix            the prefix to use for each line
    150      *                          (generally a null terminated string of spaces).
    151      * \param lines             maximum number of lines to output (0 disables).
    152      * \param limitNs           limit dump to data more recent than limitNs (0 disables).
    153      * \return a string object for the log.
    154      */
    155     std::string dumpToString(const char *prefix = "", size_t lines = 0, int64_t limitNs = 0) const
    156     {
    157         if (lines == 0) {
    158             lines = mLog.size();
    159         }
    160 
    161         std::stringstream ss;
    162         std::lock_guard<std::mutex> guard(mLock);
    163         auto it = mLog.begin();
    164 
    165         // Note: this restricts the lines before checking the time constraint.
    166         if (mLog.size() > lines) {
    167             it += (mLog.size() - lines);
    168         }
    169         for (; it != mLog.end(); ++it) {
    170             const int64_t time = it->first;
    171             if (time < limitNs) continue;  // too old
    172             ss << prefix << audio_utils_time_string_from_ns(time).time
    173                     << " " << it->second.c_str() << "\n";
    174         }
    175         return ss.str();
    176     }
    177 
    178     /**
    179      * \brief Dumps the log to a raw file descriptor.
    180      *
    181      * \param fd                file descriptor to use.
    182      * \param prefix            the prefix to use for each line
    183      *                          (generally a null terminated string of spaces).
    184      * \param lines             maximum number of lines to output (0 disables).
    185      * \param limitNs           limit dump to data more recent than limitNs (0 disables).
    186      * \return
    187      *   NO_ERROR on success or a negative number (-errno) on failure of write().
    188      */
    189     status_t dump(int fd, const char *prefix = "", size_t lines = 0, int64_t limitNs = 0) const
    190     {
    191         // dumpToString() and write() are individually thread-safe, but concurrent threads
    192         // using dump() to the same file descriptor may write out of order.
    193         const std::string s = dumpToString(prefix, lines, limitNs);
    194         if (s.size() > 0 && write(fd, s.c_str(), s.size()) < 0) {
    195             return -errno;
    196         }
    197         return NO_ERROR;
    198     }
    199 
    200 private:
    201     mutable std::mutex mLock;
    202     static const size_t kMaxStringLength = 1024;  // maximum formatted string length
    203     static const size_t kDefaultMaxLogLines = 80; // default maximum log history
    204 
    205     const size_t mMaxLogLines;                    // maximum log history
    206     std::deque<std::pair<int64_t, std::string>> mLog; // circular buffer is backed by deque.
    207 };
    208 
    209 } // namespace android
    210 
    211 #endif // !ANDROID_AUDIO_SIMPLE_LOG_H
    212