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) mMaxLogLines(maxLogLines)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 */)]] log(const char * format,...)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 */)]] log(int64_t nowNs,const char * format,...)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 */ logv(int64_t nowNs,const char * format,va_list args)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 * This could be a char * or a std::string. 133 */ 134 template <typename U> logs(int64_t nowNs,U && buffer)135 void logs(int64_t nowNs, U&& buffer) 136 { 137 // store in circular array 138 std::lock_guard<std::mutex> guard(mLock); 139 if (nowNs == -1) { 140 nowNs = audio_utils_get_real_time_ns(); 141 } 142 mLog.emplace_back(nowNs, std::forward<U>(buffer)); 143 if (mLog.size() > mMaxLogLines) { 144 mLog.pop_front(); 145 } 146 } 147 148 /** 149 * \brief Dumps the log to a string. 150 * 151 * \param prefix the prefix to use for each line 152 * (generally a null terminated string of spaces). 153 * \param lines maximum number of lines to output (0 disables). 154 * \param limitNs limit dump to data more recent than limitNs (0 disables). 155 * \return a string object for the log. 156 */ 157 std::string dumpToString(const char *prefix = "", size_t lines = 0, int64_t limitNs = 0) const 158 { 159 if (lines == 0) { 160 lines = mLog.size(); 161 } 162 163 std::stringstream ss; 164 std::lock_guard<std::mutex> guard(mLock); 165 auto it = mLog.begin(); 166 167 // Note: this restricts the lines before checking the time constraint. 168 if (mLog.size() > lines) { 169 it += (mLog.size() - lines); 170 } 171 for (; it != mLog.end(); ++it) { 172 const int64_t time = it->first; 173 if (time < limitNs) continue; // too old 174 ss << prefix << audio_utils_time_string_from_ns(time).time 175 << " " << it->second.c_str() << "\n"; 176 } 177 return ss.str(); 178 } 179 180 /** 181 * \brief Dumps the log to a raw file descriptor. 182 * 183 * \param fd file descriptor to use. 184 * \param prefix the prefix to use for each line 185 * (generally a null terminated string of spaces). 186 * \param lines maximum number of lines to output (0 disables). 187 * \param limitNs limit dump to data more recent than limitNs (0 disables). 188 * \return 189 * NO_ERROR on success or a negative number (-errno) on failure of write(). 190 */ 191 status_t dump(int fd, const char *prefix = "", size_t lines = 0, int64_t limitNs = 0) const 192 { 193 // dumpToString() and write() are individually thread-safe, but concurrent threads 194 // using dump() to the same file descriptor may write out of order. 195 const std::string s = dumpToString(prefix, lines, limitNs); 196 if (s.size() > 0 && write(fd, s.c_str(), s.size()) < 0) { 197 return -errno; 198 } 199 return NO_ERROR; 200 } 201 202 private: 203 mutable std::mutex mLock; 204 static const size_t kMaxStringLength = 1024; // maximum formatted string length 205 static const size_t kDefaultMaxLogLines = 80; // default maximum log history 206 207 const size_t mMaxLogLines; // maximum log history 208 std::deque<std::pair<int64_t, std::string>> mLog; // circular buffer is backed by deque. 209 }; 210 211 } // namespace android 212 213 #endif // !ANDROID_AUDIO_SIMPLE_LOG_H 214