• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_ERROR_LOG_H
18 #define ANDROID_AUDIO_ERROR_LOG_H
19 
20 #ifdef __cplusplus
21 
22 #include <iomanip>
23 #include <mutex>
24 #include <sstream>
25 #include <unistd.h>
26 #include <vector>
27 
28 #include <sys/cdefs.h>
29 
30 #include <audio_utils/clock.h>
31 #include <utils/Errors.h>
32 
33 namespace android {
34 
35 /**
36  * ErrorLog captures audio errors codes, combining consecutive identical error codes
37  * (within a specified time) into a single entry (to reduce log spamming).
38  *
39  * The entry thus contains the number of consecutive error codes,
40  * together with the first time the error code occurs and the last time the error code occurs.
41  *
42  * The type T represents the error code type and is an int32_t for the C API.
43  */
44 template <typename T>
45 class ErrorLog {
46 public:
47     /**
48      * \brief Creates an ErrorLog object
49      *
50      * \param entries           the length of error history.
51      * \param aggregateNs       the maximum time in nanoseconds between identical error codes
52      *                          to be aggregated into a single entry.
53      */
54     explicit ErrorLog(size_t entries, int64_t aggregateNs = 1000000000 /* one second */)
55         : mErrors(0)
56         , mIdx(0)
57         , mAggregateNs(aggregateNs)
58         , mEntries(entries)
59     {
60     }
61 
62     /**
63      * \brief Adds new error code to the error log.
64      *
65      * Consecutive errors with the same code will be aggregated
66      * if they occur within aggregateNs.
67      *
68      * \param code              error code of type T.
69      * \param nowNs             current time in nanoseconds.
70      */
log(const T & code,int64_t nowNs)71     void log(const T &code, int64_t nowNs)
72     {
73         std::lock_guard<std::mutex> guard(mLock);
74 
75         ++mErrors;
76 
77         // Within mAggregateNs (1 second by default), aggregate error codes together.
78         if (code == mEntries[mIdx].mCode
79                 && nowNs - mEntries[mIdx].mLastTime < mAggregateNs) {
80             mEntries[mIdx].mCount++;
81             mEntries[mIdx].mLastTime = nowNs;
82             return;
83         }
84 
85         // Add new error entry.
86         if (++mIdx >= mEntries.size()) {
87             mIdx = 0;
88         }
89         mEntries[mIdx].setFirstError(code, nowNs);
90     }
91 
92     /**
93      * \brief Dumps the log to a std::string.
94      * \param prefix            the prefix to use for each line
95      *                          (generally a null terminated string of spaces).
96      * \param lines             maximum number of lines to output (0 disables).
97      * \param limitNs           limit dump to data more recent than limitNs (0 disables).
98      * \return std::string of the dump.
99      */
100     std::string dumpToString(const char *prefix = "", size_t lines = 0, int64_t limitNs = 0) const
101     {
102         std::lock_guard<std::mutex> guard(mLock);
103 
104         std::stringstream ss;
105         const size_t numberOfEntries = mEntries.size();
106         const size_t headerLines = 2;
107 
108         if (lines == 0) {
109             lines = SIZE_MAX;
110         }
111         ss << prefix << "Errors: " << mErrors << "\n";
112 
113         if (mErrors == 0 || lines <= headerLines) {
114             return ss.str();
115         }
116 
117         lines = std::min(lines - headerLines, numberOfEntries);
118         // compute where to start dump log
119         ssize_t offset;
120         for (offset = 0; offset < (ssize_t)lines; ++offset) {
121             const auto &entry =
122                     mEntries[(mIdx + numberOfEntries - offset) % numberOfEntries];
123             if (entry.mCount == 0 || entry.mLastTime < limitNs) {
124                 break;
125             }
126         }
127         if (offset > 0) {
128             offset--;
129             ss << prefix << " Code  Freq          First time           Last time\n";
130             for (; offset >= 0; --offset) {
131                 const auto &entry =
132                         mEntries[(mIdx + numberOfEntries - offset) % numberOfEntries];
133 
134                 ss << prefix << std::setw(5) <<  entry.mCode
135                         << " " << std::setw(5) << entry.mCount
136                         << "  " << audio_utils_time_string_from_ns(entry.mFirstTime).time
137                         << "  " << audio_utils_time_string_from_ns(entry.mLastTime).time << "\n";
138             }
139         }
140         return ss.str();
141     }
142 
143     /**
144      * \brief Dumps the log to a raw file descriptor.
145      * \param fd                file descriptor to use.
146      * \param prefix            the prefix to use for each line
147      *                          (generally a null terminated string of spaces).
148      * \param lines             maximum number of lines to output (0 disables).
149      * \param limitNs           limit dump to data more recent than limitNs (0 disables).
150      * \return
151      *   NO_ERROR on success or a negative number (-errno) on failure of write().
152      */
153     status_t dump(int fd, const char *prefix = "", size_t lines = 0, int64_t limitNs = 0) const
154     {
155         // thread safe but not necessarily serial with respect to concurrent dumps to the same fd.
156         const std::string s = dumpToString(prefix, lines, limitNs);
157         if (s.size() > 0 && write(fd, s.c_str(), s.size()) < 0) {
158             return -errno;
159         }
160         return NO_ERROR;
161     }
162 
163     struct Entry {
EntryEntry164         Entry()
165             : mCode(0)
166             , mCount(0)
167             , mFirstTime(0)
168             , mLastTime(0)
169         {
170         }
171 
172         // Initialize entry with code as the first error at the given time.
setFirstErrorEntry173         void setFirstError(T code, int64_t time) {
174             mCode = code;
175             mCount = 1;
176             mFirstTime = time;
177             mLastTime = time;
178         }
179 
180         T mCode;            // error code
181         uint32_t mCount;    // number of consecutive errors of the same code.
182         int64_t mFirstTime; // first time of the error code.
183         int64_t mLastTime;  // last time of the error code.
184     };
185 
186 private:
187     mutable std::mutex mLock;     // monitor mutex
188     int64_t mErrors;              // total number of errors registered
189     size_t mIdx;                  // current index into mEntries (active)
190     const int64_t mAggregateNs;   // number of nanoseconds to aggregate consecutive error codes.
191     std::vector<Entry> mEntries;  // circular buffer of error entries.
192 };
193 
194 } // namespace android
195 
196 #endif // __cplusplus
197 
198 // C API (see C++ API above for details)
199 
200 /** \cond */
201 __BEGIN_DECLS
202 /** \endcond */
203 
204 typedef struct error_log_t error_log_t;
205 
206 /**
207  * \brief Creates an error log object
208  *
209  * \param entries           the length of error history.
210  * \param aggregate_ns      the maximum time in nanoseconds between identical error codes
211  *                          to be aggregated into a single entry.
212  * \return the error log object or NULL on failure.
213  */
214 error_log_t *error_log_create(size_t entries, int64_t aggregate_ns);
215 
216 /**
217  * \brief Adds new error code to the error log.
218  *
219  * Consecutive errors with the same code will be aggregated if
220  * they occur within aggregate_ns.
221  *
222  * \param error_log         object returned by create, if NULL nothing happens.
223  * \param code              error code of type T.
224  * \param now_ns            current time in nanoseconds.
225  */
226 void error_log_log(error_log_t *error_log, int32_t code, int64_t now_ns);
227 
228 /**
229  * \brief Dumps the log to a raw file descriptor.
230  * \param error_log         object returned by create, if NULL nothing happens.
231  * \param prefix            the prefix to use for each line
232  *                          (generally a null terminated string of spaces).
233  * \param fd                file descriptor to use.
234  * \param lines             maximum number of lines to output (0 disables).
235  * \param limit_ns          limit dump to data more recent than limit_ns (0 disables).
236  * \return
237  *   NO_ERROR on success or a negative number (-errno) on failure of write().
238  *   if power_log is NULL, BAD_VALUE is returned.
239  */
240 int error_log_dump(
241         error_log_t *error_log, int fd, const char *prefix, size_t lines, int64_t limit_ns);
242 
243 /**
244  * \brief Destroys the error log object.
245  *
246  * \param error_log         object returned by create, if NULL nothing happens.
247  */
248 void error_log_destroy(error_log_t *error_log);
249 
250 /** \cond */
251 __END_DECLS
252 /** \endcond */
253 
254 #endif // !ANDROID_AUDIO_ERROR_LOG_H
255