/* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_AUDIO_SIMPLE_LOG_H #define ANDROID_AUDIO_SIMPLE_LOG_H #include #include #include #include #include #include #include #include namespace android { /** * SimpleLog provides a private logcat-style logging to avoid cluttering * the device logcat. * * The public methods are internally protected by a mutex to be thread-safe. * Do not call from a sched_fifo thread as it can use a system time call * and obtains a local mutex. * * Formatted logs by log() and logv() will be truncated at kMaxStringLength - 1 * due to null termination. logs() does not have a string length limitation. */ class SimpleLog { public: /** * \brief Creates a SimpleLog object. * * \param maxLogLines the maximum number of log lines. */ explicit SimpleLog(size_t maxLogLines = kDefaultMaxLogLines) : mMaxLogLines(maxLogLines) { } /** * \brief Adds a formatted string into the log. * * Time is automatically associated with the string by audio_utils_get_real_time_ns(). * * \param format the format string, similar to printf(). * * and optional arguments. */ // using C++11 unified attribute syntax; index is offset by 1 for implicit "this". [[gnu::format(printf, 2 /* string-index */, 3 /* first-to-check */)]] void log(const char *format, ...) { va_list args; va_start(args, format); // use -1 to trigger the clock fetch within the mutex lock. logv(-1 /* nowNs */, format, args); va_end(args); } /** * \brief Adds a formatted string into the log with time. * * \param nowNs the time to use for logging. Assumed to be monotonically * increasing for sequential calls. If -1, then * audio_utils_get_real_time_ns() is called. * \param format the format string, similar to printf(). * * and optional arguments. */ // using C++11 unified attribute syntax; index is offset by 1 for implicit "this". [[gnu::format(printf, 3 /* string-index */, 4 /* first-to-check */)]] void log(int64_t nowNs, const char *format, ...) { va_list args; va_start(args, format); logv(nowNs, format, args); va_end(args); } /** * \brief Adds a formatted string by va_list with time. Not intended for typical use. * * \param nowNs the time to use for logging. Assumed to be monotonically * increasing for sequential calls. If -1, then * audio_utils_get_real_time_ns() is called. * \param format the format string, similar to printf(). * \param args va_list args. */ void logv(int64_t nowNs, const char *format, va_list args) { // format to buffer char buffer[kMaxStringLength]; int length = vsnprintf(buffer, sizeof(buffer), format, args); if (length < 0) { // encoding error logs(nowNs, "invalid format"); return; } else if (length >= (signed)sizeof(buffer)) { length = sizeof(buffer) - 1; } // strip out trailing newlines while (length > 0 && buffer[length - 1] == '\n') { buffer[--length] = '\0'; } logs(nowNs, buffer); } /** * \brief Logs a string to the buffer with time. * \param nowNs the time to use for logging. Assumed to be monotonically * increasing for sequential calls. If -1, then * audio_utils_get_real_time_ns() is called. * \param buffer contains a null terminated string, which may have * special characters such as % and \ that are * not interpreted. * This could be a char * or a std::string. */ template void logs(int64_t nowNs, U&& buffer) { // store in circular array std::lock_guard guard(mLock); if (nowNs == -1) { nowNs = audio_utils_get_real_time_ns(); } mLog.emplace_back(nowNs, std::forward(buffer)); if (mLog.size() > mMaxLogLines) { mLog.pop_front(); } } /** * \brief Dumps the log to a string. * * \param prefix the prefix to use for each line * (generally a null terminated string of spaces). * \param lines maximum number of lines to output (0 disables). * \param limitNs limit dump to data more recent than limitNs (0 disables). * \return a string object for the log. */ std::string dumpToString(const char *prefix = "", size_t lines = 0, int64_t limitNs = 0) const { if (lines == 0) { lines = mLog.size(); } std::stringstream ss; std::lock_guard guard(mLock); auto it = mLog.begin(); // Note: this restricts the lines before checking the time constraint. if (mLog.size() > lines) { it += (mLog.size() - lines); } for (; it != mLog.end(); ++it) { const int64_t time = it->first; if (time < limitNs) continue; // too old ss << prefix << audio_utils_time_string_from_ns(time).time << " " << it->second.c_str() << "\n"; } return ss.str(); } /** * \brief Dumps the log to a raw file descriptor. * * \param fd file descriptor to use. * \param prefix the prefix to use for each line * (generally a null terminated string of spaces). * \param lines maximum number of lines to output (0 disables). * \param limitNs limit dump to data more recent than limitNs (0 disables). * \return * NO_ERROR on success or a negative number (-errno) on failure of write(). */ status_t dump(int fd, const char *prefix = "", size_t lines = 0, int64_t limitNs = 0) const { // dumpToString() and write() are individually thread-safe, but concurrent threads // using dump() to the same file descriptor may write out of order. const std::string s = dumpToString(prefix, lines, limitNs); if (s.size() > 0 && write(fd, s.c_str(), s.size()) < 0) { return -errno; } return NO_ERROR; } private: mutable std::mutex mLock; static const size_t kMaxStringLength = 1024; // maximum formatted string length static const size_t kDefaultMaxLogLines = 80; // default maximum log history const size_t mMaxLogLines; // maximum log history std::deque> mLog; // circular buffer is backed by deque. }; } // namespace android #endif // !ANDROID_AUDIO_SIMPLE_LOG_H