1838414545d9731775fa7d6049f48000f26db88f3Andy Hung/*
2838414545d9731775fa7d6049f48000f26db88f3Andy Hung * Copyright 2017 The Android Open Source Project
3838414545d9731775fa7d6049f48000f26db88f3Andy Hung *
4838414545d9731775fa7d6049f48000f26db88f3Andy Hung * Licensed under the Apache License, Version 2.0 (the "License");
5838414545d9731775fa7d6049f48000f26db88f3Andy Hung * you may not use this file except in compliance with the License.
6838414545d9731775fa7d6049f48000f26db88f3Andy Hung * You may obtain a copy of the License at
7838414545d9731775fa7d6049f48000f26db88f3Andy Hung *
8838414545d9731775fa7d6049f48000f26db88f3Andy Hung *      http://www.apache.org/licenses/LICENSE-2.0
9838414545d9731775fa7d6049f48000f26db88f3Andy Hung *
10838414545d9731775fa7d6049f48000f26db88f3Andy Hung * Unless required by applicable law or agreed to in writing, software
11838414545d9731775fa7d6049f48000f26db88f3Andy Hung * distributed under the License is distributed on an "AS IS" BASIS,
12838414545d9731775fa7d6049f48000f26db88f3Andy Hung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13838414545d9731775fa7d6049f48000f26db88f3Andy Hung * See the License for the specific language governing permissions and
14838414545d9731775fa7d6049f48000f26db88f3Andy Hung * limitations under the License.
15838414545d9731775fa7d6049f48000f26db88f3Andy Hung */
16838414545d9731775fa7d6049f48000f26db88f3Andy Hung
17838414545d9731775fa7d6049f48000f26db88f3Andy Hung#ifndef ANDROID_AUDIO_SIMPLE_LOG_H
18838414545d9731775fa7d6049f48000f26db88f3Andy Hung#define ANDROID_AUDIO_SIMPLE_LOG_H
19838414545d9731775fa7d6049f48000f26db88f3Andy Hung
20838414545d9731775fa7d6049f48000f26db88f3Andy Hung#include <deque>
21838414545d9731775fa7d6049f48000f26db88f3Andy Hung#include <mutex>
22838414545d9731775fa7d6049f48000f26db88f3Andy Hung#include <sstream>
23838414545d9731775fa7d6049f48000f26db88f3Andy Hung#include <stdint.h>
24838414545d9731775fa7d6049f48000f26db88f3Andy Hung#include <string>
25838414545d9731775fa7d6049f48000f26db88f3Andy Hung#include <unistd.h>
26838414545d9731775fa7d6049f48000f26db88f3Andy Hung#include <utils/Errors.h>
27838414545d9731775fa7d6049f48000f26db88f3Andy Hung
28838414545d9731775fa7d6049f48000f26db88f3Andy Hung#include <audio_utils/clock.h>
29838414545d9731775fa7d6049f48000f26db88f3Andy Hung
30838414545d9731775fa7d6049f48000f26db88f3Andy Hungnamespace android {
31838414545d9731775fa7d6049f48000f26db88f3Andy Hung
32838414545d9731775fa7d6049f48000f26db88f3Andy Hung/**
33838414545d9731775fa7d6049f48000f26db88f3Andy Hung * SimpleLog provides a private logcat-style logging to avoid cluttering
34838414545d9731775fa7d6049f48000f26db88f3Andy Hung * the device logcat.
35838414545d9731775fa7d6049f48000f26db88f3Andy Hung *
36838414545d9731775fa7d6049f48000f26db88f3Andy Hung * The public methods are internally protected by a mutex to be thread-safe.
37838414545d9731775fa7d6049f48000f26db88f3Andy Hung * Do not call from a sched_fifo thread as it can use a system time call
38838414545d9731775fa7d6049f48000f26db88f3Andy Hung * and obtains a local mutex.
39838414545d9731775fa7d6049f48000f26db88f3Andy Hung *
40838414545d9731775fa7d6049f48000f26db88f3Andy Hung * Formatted logs by log() and logv() will be truncated at kMaxStringLength - 1
41838414545d9731775fa7d6049f48000f26db88f3Andy Hung * due to null termination. logs() does not have a string length limitation.
42838414545d9731775fa7d6049f48000f26db88f3Andy Hung */
43838414545d9731775fa7d6049f48000f26db88f3Andy Hung
44838414545d9731775fa7d6049f48000f26db88f3Andy Hungclass SimpleLog {
45838414545d9731775fa7d6049f48000f26db88f3Andy Hungpublic:
46838414545d9731775fa7d6049f48000f26db88f3Andy Hung    /**
47838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \brief Creates a SimpleLog object.
48838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *
49838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param maxLogLines the maximum number of log lines.
50838414545d9731775fa7d6049f48000f26db88f3Andy Hung     */
51838414545d9731775fa7d6049f48000f26db88f3Andy Hung    explicit SimpleLog(size_t maxLogLines = kDefaultMaxLogLines)
52838414545d9731775fa7d6049f48000f26db88f3Andy Hung        : mMaxLogLines(maxLogLines)
53838414545d9731775fa7d6049f48000f26db88f3Andy Hung    {
54838414545d9731775fa7d6049f48000f26db88f3Andy Hung    }
55838414545d9731775fa7d6049f48000f26db88f3Andy Hung
56838414545d9731775fa7d6049f48000f26db88f3Andy Hung    /**
57838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \brief Adds a formatted string into the log.
58838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *
59838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * Time is automatically associated with the string by audio_utils_get_real_time_ns().
60838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *
61838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param format            the format string, similar to printf().
62838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *
63838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * and optional arguments.
64838414545d9731775fa7d6049f48000f26db88f3Andy Hung     */
65838414545d9731775fa7d6049f48000f26db88f3Andy Hung    // using C++11 unified attribute syntax; index is offset by 1 for implicit "this".
66838414545d9731775fa7d6049f48000f26db88f3Andy Hung    [[gnu::format(printf, 2 /* string-index */, 3 /* first-to-check */)]]
67838414545d9731775fa7d6049f48000f26db88f3Andy Hung    void log(const char *format, ...)
68838414545d9731775fa7d6049f48000f26db88f3Andy Hung    {
69838414545d9731775fa7d6049f48000f26db88f3Andy Hung        va_list args;
70838414545d9731775fa7d6049f48000f26db88f3Andy Hung        va_start(args, format);
71838414545d9731775fa7d6049f48000f26db88f3Andy Hung        // use -1 to trigger the clock fetch within the mutex lock.
72838414545d9731775fa7d6049f48000f26db88f3Andy Hung        logv(-1 /* nowNs */, format, args);
73838414545d9731775fa7d6049f48000f26db88f3Andy Hung        va_end(args);
74838414545d9731775fa7d6049f48000f26db88f3Andy Hung    }
75838414545d9731775fa7d6049f48000f26db88f3Andy Hung
76838414545d9731775fa7d6049f48000f26db88f3Andy Hung    /**
77838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \brief Adds a formatted string into the log with time.
78838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *
79838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param nowNs             the time to use for logging. Assumed to be monotonically
80838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *                          increasing for sequential calls.  If -1, then
81838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *                          audio_utils_get_real_time_ns() is called.
82838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param format            the format string, similar to printf().
83838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *
84838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * and optional arguments.
85838414545d9731775fa7d6049f48000f26db88f3Andy Hung     */
86838414545d9731775fa7d6049f48000f26db88f3Andy Hung    // using C++11 unified attribute syntax; index is offset by 1 for implicit "this".
87838414545d9731775fa7d6049f48000f26db88f3Andy Hung    [[gnu::format(printf, 3 /* string-index */, 4 /* first-to-check */)]]
88838414545d9731775fa7d6049f48000f26db88f3Andy Hung    void log(int64_t nowNs, const char *format, ...)
89838414545d9731775fa7d6049f48000f26db88f3Andy Hung    {
90838414545d9731775fa7d6049f48000f26db88f3Andy Hung        va_list args;
91838414545d9731775fa7d6049f48000f26db88f3Andy Hung        va_start(args, format);
92838414545d9731775fa7d6049f48000f26db88f3Andy Hung        logv(nowNs, format, args);
93838414545d9731775fa7d6049f48000f26db88f3Andy Hung        va_end(args);
94838414545d9731775fa7d6049f48000f26db88f3Andy Hung    }
95838414545d9731775fa7d6049f48000f26db88f3Andy Hung
96838414545d9731775fa7d6049f48000f26db88f3Andy Hung    /**
97838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \brief Adds a formatted string by va_list with time.  Not intended for typical use.
98838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *
99838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param nowNs             the time to use for logging. Assumed to be monotonically
100838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *                          increasing for sequential calls.  If -1, then
101838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *                          audio_utils_get_real_time_ns() is called.
102838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param format            the format string, similar to printf().
103838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param args              va_list args.
104838414545d9731775fa7d6049f48000f26db88f3Andy Hung     */
105838414545d9731775fa7d6049f48000f26db88f3Andy Hung    void logv(int64_t nowNs, const char *format, va_list args)
106838414545d9731775fa7d6049f48000f26db88f3Andy Hung    {
107838414545d9731775fa7d6049f48000f26db88f3Andy Hung        // format to buffer
108838414545d9731775fa7d6049f48000f26db88f3Andy Hung        char buffer[kMaxStringLength];
109838414545d9731775fa7d6049f48000f26db88f3Andy Hung        int length = vsnprintf(buffer, sizeof(buffer), format, args);
110838414545d9731775fa7d6049f48000f26db88f3Andy Hung        if (length < 0) { // encoding error
111838414545d9731775fa7d6049f48000f26db88f3Andy Hung            logs(nowNs, "invalid format");
112838414545d9731775fa7d6049f48000f26db88f3Andy Hung            return;
113838414545d9731775fa7d6049f48000f26db88f3Andy Hung        } else if (length >= (signed)sizeof(buffer)) {
114838414545d9731775fa7d6049f48000f26db88f3Andy Hung            length = sizeof(buffer) - 1;
115838414545d9731775fa7d6049f48000f26db88f3Andy Hung        }
116838414545d9731775fa7d6049f48000f26db88f3Andy Hung
117838414545d9731775fa7d6049f48000f26db88f3Andy Hung        // strip out trailing newlines
118838414545d9731775fa7d6049f48000f26db88f3Andy Hung        while (length > 0 && buffer[length - 1] == '\n') {
119838414545d9731775fa7d6049f48000f26db88f3Andy Hung            buffer[--length] = '\0';
120838414545d9731775fa7d6049f48000f26db88f3Andy Hung        }
121838414545d9731775fa7d6049f48000f26db88f3Andy Hung        logs(nowNs, buffer);
122838414545d9731775fa7d6049f48000f26db88f3Andy Hung    }
123838414545d9731775fa7d6049f48000f26db88f3Andy Hung
124838414545d9731775fa7d6049f48000f26db88f3Andy Hung    /**
125838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \brief Logs a string to the buffer with time.
126838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param nowNs             the time to use for logging. Assumed to be monotonically
127838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *                          increasing for sequential calls.  If -1, then
128838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *                          audio_utils_get_real_time_ns() is called.
129838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param buffer            contains a null terminated string, which may have
130838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *                          special characters such as % and \ that are
131838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *                          not interpreted.
132838414545d9731775fa7d6049f48000f26db88f3Andy Hung     */
133838414545d9731775fa7d6049f48000f26db88f3Andy Hung    void logs(int64_t nowNs, const char *buffer)
134838414545d9731775fa7d6049f48000f26db88f3Andy Hung    {
135838414545d9731775fa7d6049f48000f26db88f3Andy Hung        // store in circular array
136838414545d9731775fa7d6049f48000f26db88f3Andy Hung        std::lock_guard<std::mutex> guard(mLock);
137838414545d9731775fa7d6049f48000f26db88f3Andy Hung        if (nowNs == -1) {
138838414545d9731775fa7d6049f48000f26db88f3Andy Hung            nowNs = audio_utils_get_real_time_ns();
139838414545d9731775fa7d6049f48000f26db88f3Andy Hung        }
140838414545d9731775fa7d6049f48000f26db88f3Andy Hung        mLog.emplace_back(nowNs, std::string(buffer));
141838414545d9731775fa7d6049f48000f26db88f3Andy Hung        if (mLog.size() > mMaxLogLines) {
142838414545d9731775fa7d6049f48000f26db88f3Andy Hung            mLog.pop_front();
143838414545d9731775fa7d6049f48000f26db88f3Andy Hung        }
144838414545d9731775fa7d6049f48000f26db88f3Andy Hung    }
145838414545d9731775fa7d6049f48000f26db88f3Andy Hung
146838414545d9731775fa7d6049f48000f26db88f3Andy Hung    /**
147838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \brief Dumps the log to a string.
148838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *
149838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param prefix            the prefix to use for each line
150838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *                          (generally a null terminated string of spaces).
151838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param lines             maximum number of lines to output (0 disables).
152838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param limitNs           limit dump to data more recent than limitNs (0 disables).
153838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \return a string object for the log.
154838414545d9731775fa7d6049f48000f26db88f3Andy Hung     */
155838414545d9731775fa7d6049f48000f26db88f3Andy Hung    std::string dumpToString(const char *prefix = "", size_t lines = 0, int64_t limitNs = 0) const
156838414545d9731775fa7d6049f48000f26db88f3Andy Hung    {
157838414545d9731775fa7d6049f48000f26db88f3Andy Hung        if (lines == 0) {
158838414545d9731775fa7d6049f48000f26db88f3Andy Hung            lines = mLog.size();
159838414545d9731775fa7d6049f48000f26db88f3Andy Hung        }
160838414545d9731775fa7d6049f48000f26db88f3Andy Hung
161838414545d9731775fa7d6049f48000f26db88f3Andy Hung        std::stringstream ss;
162838414545d9731775fa7d6049f48000f26db88f3Andy Hung        std::lock_guard<std::mutex> guard(mLock);
163838414545d9731775fa7d6049f48000f26db88f3Andy Hung        auto it = mLog.begin();
164838414545d9731775fa7d6049f48000f26db88f3Andy Hung
165838414545d9731775fa7d6049f48000f26db88f3Andy Hung        // Note: this restricts the lines before checking the time constraint.
166838414545d9731775fa7d6049f48000f26db88f3Andy Hung        if (mLog.size() > lines) {
167838414545d9731775fa7d6049f48000f26db88f3Andy Hung            it += (mLog.size() - lines);
168838414545d9731775fa7d6049f48000f26db88f3Andy Hung        }
169838414545d9731775fa7d6049f48000f26db88f3Andy Hung        for (; it != mLog.end(); ++it) {
170838414545d9731775fa7d6049f48000f26db88f3Andy Hung            const int64_t time = it->first;
171838414545d9731775fa7d6049f48000f26db88f3Andy Hung            if (time < limitNs) continue;  // too old
172c1eb186fc1ab8837e95e01be8a87a77e952fc81fAndy Hung            ss << prefix << audio_utils_time_string_from_ns(time).time
173c1eb186fc1ab8837e95e01be8a87a77e952fc81fAndy Hung                    << " " << it->second.c_str() << "\n";
174838414545d9731775fa7d6049f48000f26db88f3Andy Hung        }
175838414545d9731775fa7d6049f48000f26db88f3Andy Hung        return ss.str();
176838414545d9731775fa7d6049f48000f26db88f3Andy Hung    }
177838414545d9731775fa7d6049f48000f26db88f3Andy Hung
178838414545d9731775fa7d6049f48000f26db88f3Andy Hung    /**
179838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \brief Dumps the log to a raw file descriptor.
180838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *
181838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param fd                file descriptor to use.
182838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param prefix            the prefix to use for each line
183838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *                          (generally a null terminated string of spaces).
184838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param lines             maximum number of lines to output (0 disables).
185838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \param limitNs           limit dump to data more recent than limitNs (0 disables).
186838414545d9731775fa7d6049f48000f26db88f3Andy Hung     * \return
187838414545d9731775fa7d6049f48000f26db88f3Andy Hung     *   NO_ERROR on success or a negative number (-errno) on failure of write().
188838414545d9731775fa7d6049f48000f26db88f3Andy Hung     */
189838414545d9731775fa7d6049f48000f26db88f3Andy Hung    status_t dump(int fd, const char *prefix = "", size_t lines = 0, int64_t limitNs = 0) const
190838414545d9731775fa7d6049f48000f26db88f3Andy Hung    {
191838414545d9731775fa7d6049f48000f26db88f3Andy Hung        // dumpToString() and write() are individually thread-safe, but concurrent threads
192838414545d9731775fa7d6049f48000f26db88f3Andy Hung        // using dump() to the same file descriptor may write out of order.
193838414545d9731775fa7d6049f48000f26db88f3Andy Hung        const std::string s = dumpToString(prefix, lines, limitNs);
194838414545d9731775fa7d6049f48000f26db88f3Andy Hung        if (s.size() > 0 && write(fd, s.c_str(), s.size()) < 0) {
195838414545d9731775fa7d6049f48000f26db88f3Andy Hung            return -errno;
196838414545d9731775fa7d6049f48000f26db88f3Andy Hung        }
197838414545d9731775fa7d6049f48000f26db88f3Andy Hung        return NO_ERROR;
198838414545d9731775fa7d6049f48000f26db88f3Andy Hung    }
199838414545d9731775fa7d6049f48000f26db88f3Andy Hung
200838414545d9731775fa7d6049f48000f26db88f3Andy Hungprivate:
201838414545d9731775fa7d6049f48000f26db88f3Andy Hung    mutable std::mutex mLock;
202838414545d9731775fa7d6049f48000f26db88f3Andy Hung    static const size_t kMaxStringLength = 1024;  // maximum formatted string length
203838414545d9731775fa7d6049f48000f26db88f3Andy Hung    static const size_t kDefaultMaxLogLines = 80; // default maximum log history
204838414545d9731775fa7d6049f48000f26db88f3Andy Hung
205838414545d9731775fa7d6049f48000f26db88f3Andy Hung    const size_t mMaxLogLines;                    // maximum log history
206838414545d9731775fa7d6049f48000f26db88f3Andy Hung    std::deque<std::pair<int64_t, std::string>> mLog; // circular buffer is backed by deque.
207838414545d9731775fa7d6049f48000f26db88f3Andy Hung};
208838414545d9731775fa7d6049f48000f26db88f3Andy Hung
209838414545d9731775fa7d6049f48000f26db88f3Andy Hung} // namespace android
210838414545d9731775fa7d6049f48000f26db88f3Andy Hung
211838414545d9731775fa7d6049f48000f26db88f3Andy Hung#endif // !ANDROID_AUDIO_SIMPLE_LOG_H
212