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