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