• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 CHRE_PLATFORM_SHARED_LOG_BUFFER_H_
18 #define CHRE_PLATFORM_SHARED_LOG_BUFFER_H_
19 
20 #include <cinttypes>
21 #include <cstdarg>
22 #include <cstring>
23 
24 #include "chre/platform/mutex.h"
25 
26 namespace chre {
27 
28 /**
29  * Values that represent a preferred setting for when the LogBuffer should
30  * notify the platform that logs are ready to be copied.
31  *
32  * ALWAYS - The LogBuffer should immediately notify the platform when a new log
33  *          is received.
34  * NEVER -  The LogBuffer should never alert the platform that logs have been
35  *          received. It is up to the platform to decide when to copy logs out.
36  * THRESHOLD - The LogBuffer should notify the platform when a certain thresold
37  *             of memory has been allocated for logs in the buffer.
38  */
39 enum class LogBufferNotificationSetting : uint8_t { ALWAYS, NEVER, THRESHOLD };
40 
41 /**
42  * The log level options for logs stored in a log buffer.
43  */
44 enum class LogBufferLogLevel : uint8_t {
45   UNKNOWN,
46   ERROR,
47   WARN,
48   INFO,
49   DEBUG,
50   VERBOSE
51 };
52 
53 // Forward declaration for LogBufferCallbackInterface.
54 class LogBuffer;
55 
56 /**
57  * Callback objects that are implemented by the platform code and passed to the
58  * log buffer instances are notified of changes in the state of the buffer
59  * through this callback interface.
60  */
61 class LogBufferCallbackInterface {
62  public:
~LogBufferCallbackInterface()63   virtual ~LogBufferCallbackInterface() {}
64 
65   /**
66    * Notify the platform code that is using the buffer manager that it should
67    * call copyLogs because the buffer internal state has changed to suit the
68    * requirements for alerting the platform that logs are ready to be copied
69    * out of buffer.
70    */
71   virtual void onLogsReady() = 0;
72 };
73 
74 /**
75  * The class responsible for batching logs in memory until the notification
76  * callback is triggered and the platform copies log data out of the buffer.
77  */
78 class LogBuffer {
79  public:
80   //! The max size of a single log entry which must fit in a single byte.
81   static constexpr size_t kLogMaxSize = UINT8_MAX;
82 
83   //! The number of bytes in a log entry of the buffer before the log data is
84   //! encountered. This is determined by the size of the 'header' in the log
85   //! message.
86   static constexpr size_t kLogDataOffset = 5;
87 
88   /**
89    * @param callback The callback object that will receive notifications about
90    *                 the state of the log buffer or nullptr if it is not needed.
91    * @param buffer The buffer location that will store log data.
92    *                    message.
93    * @param bufferSize The number of bytes in the buffer. This value must be >
94    *                   kBufferMinSize
95    */
96   LogBuffer(LogBufferCallbackInterface *callback, void *buffer,
97             size_t bufferSize);
98 
99   /**
100    * Buffer this log and possibly call on logs ready callback depending on the
101    * notification setting in place.  The method is thread-safe and will ensure
102    * that logs are buffered in a FIFO ordering. If the buffer is full then drop
103    * the oldest log.
104    *
105    * @param logLevel The log level.
106    * @param timestampMs The timestamp that the log was collected as in
107    *                    milliseconds. Monotonically increasing and in
108    *                    milliseconds since boot.
109    * @param logFormat The ASCII log format that is buffered.
110    * @param ... The variable length set of parameters to print into the
111    *            logFormat string.
112    */
113   void handleLog(LogBufferLogLevel logLevel, uint32_t timestampMs,
114                  const char *logFormat, ...);
115 
116   /**
117    * Same as handleLog but with a va_list argument instead of a ... parameter.
118    *
119    * @param args The arguments in a va_list type.
120    */
121   void handleLogVa(LogBufferLogLevel logLevel, uint32_t timestampMs,
122                    const char *logFormat, va_list args);
123 
124   void handleEncodedLog(LogBufferLogLevel logLevel, uint32_t timestampMs,
125                         const uint8_t *log, size_t logSize);
126 
127   // TODO(b/179786399): Remove the copyLogs method when the LogBufferManager is
128   // refactored to no longer use it.
129   /**
130    * Copy out as many logs as will fit into destination buffer as they are
131    * formatted internally. The memory where the logs were stored will be freed.
132    * This method is thread-safe and will ensure that copyLogs will only copy
133    * out the logs in a FIFO ordering.
134    *
135    * @param destination Pointer to the destination memory address.
136    * @param size The max number of bytes to copy.
137    * @param numLogsDropped Non-null pointer which will be set to the number of
138    * logs dropped since CHRE started.
139    *
140    * @return The number of bytes copied from buffer to destination which may be
141    *         less than size because partial logs are not copied into
142    *         destination or the number of bytes left in the buffer is less than
143    *         size.
144    */
145   size_t copyLogs(void *destination, size_t size, size_t *numLogsDropped);
146 
147   /**
148    *
149    * @param logSize The size of the log text in bytes.
150    * @return true if log would cause an overflow of the buffer and would
151    * overwrite a log if it was pushed onto the buffer.
152    */
153   bool logWouldCauseOverflow(size_t logSize);
154 
155   /**
156    * Transfer all data from one log buffer to another. The destination log
157    * buffer must have equal or greater capacity than this buffer. The
158    * otherBuffer will be reset prior to this buffer's data being transferred to
159    * it and after the transfer this buffer will be reset. This method is
160    * thread-safe and will ensure that logs are kept in FIFO ordering during a
161    * transfer operation.
162    *
163    * @param otherBuffer The log buffer that is transferred to.
164    */
165   void transferTo(LogBuffer &otherBuffer);
166 
167   /**
168    * Update the current log buffer notification setting which will determine
169    * when the platform is notified to copy logs out of the buffer. Thread-safe.
170    *
171    * @param setting The new notification setting value.
172    * @param thresholdBytes If the nofification setting is THRESHOLD, then if
173    *                       the buffer allocates this many bytes the notification
174    *                       callback will be triggerd, otherwise this parameter
175    *                       is ignored.
176    */
177   void updateNotificationSetting(LogBufferNotificationSetting setting,
178                                  size_t thresholdBytes = 0);
179 
180   /**
181    * Thread safe.
182    *
183    * Empty out the log entries currently in the buffer and reset the number of
184    * logs dropped.
185    */
186   void reset();
187 
188   /**
189    * The data inside the buffer that is returned may be altered by
190    * another thread so it is up to the calling code to ensure that race
191    * conditions do not occur on writes to the data.
192    *
193    * @return The pointer to the underlying data buffer.
194    */
195   const uint8_t *getBufferData();
196 
197   /**
198    * Thread safe.
199    *
200    * @return The current buffer size.
201    */
202   size_t getBufferSize();
203 
204   /**
205    *
206    * Thread safe.
207    *
208    * @return The number of logs dropped since the object was last reset or
209    * instantiated.
210    */
211   size_t getNumLogsDropped();
212 
213  private:
214   /**
215    * Increment the value and take the modulus of the max size of the buffer.
216    *
217    * @param originalVal The original value to increment and mod.
218    * @param incrementBy The amount to increment by.
219    * @return The final value after incrementing and modulus.
220    */
221   size_t incrementAndModByBufferMaxSize(size_t originalVal,
222                                         size_t incrementBy) const;
223 
224   /**
225    * Copy from the source memory location to the buffer data ensuring that
226    * the copy wraps around the buffer data if needed.
227    *
228    * @param size The number of bytes to copy into the buffer.
229    * @param source The memory location to copy from.
230    */
231   void copyToBuffer(size_t size, const void *source);
232 
233   /**
234    * Copy from the buffer data to a destination memory location ensuring that
235    * the copy wraps around the buffer data if needed.
236    *
237    * @param size The number of bytes to copy into the buffer.
238    * @param destination The memory location to copy to.
239    */
240   void copyFromBuffer(size_t size, void *destination);
241 
242   /**
243    * Same as copyLogs method but requires that a lock already be held.
244    */
245   size_t copyLogsLocked(void *destination, size_t size, size_t *numLogsDropped);
246 
247   /**
248    * Same as reset method but requires that a lock already be held.
249    */
250   void resetLocked();
251 
252   /**
253    * Get next index indicating the start of a log entry from the starting
254    * index of a previous log entry.
255    *
256    * @param startingIndex The starting index given.
257    * @param logSize Non-null pointer that will be set to the size of the current
258    *        log message.
259    * @return The next starting log index.
260    */
261   size_t getNextLogIndex(size_t startingIndex, size_t *logSize);
262 
263   /**
264    * @param startingIndex The index to start from.
265    * @return The length of the data portion of a log along with the null
266    *         terminator. If a null terminator was not found at most
267    *         kLogMaxSize - kLogDataOffset bytes away from the startingIndex
268    *         then kLogMaxSize - kLogDataOffset + 1 is returned.
269    */
270   size_t getLogDataLength(size_t startingIndex);
271 
272   /**
273    * Encode the received log message (if tokenization or similar encoding
274    * is used) and dispatch it.
275    */
276   void processLog(LogBufferLogLevel logLevel, uint32_t timestampMs,
277                   const void *log, size_t logSize, bool encoded = true);
278 
279   /**
280    * First ensure that there's enough space for the log by discarding older
281    * logs, then encode and copy this log into the internal log buffer.
282    */
283   void copyLogToBuffer(LogBufferLogLevel level, uint32_t timestampMs,
284                        const void *logBuffer, uint8_t logLen, bool encoded);
285 
286   /**
287    * Invalidate memory allocated for log at head while the buffer is greater
288    * than max size. This function must only be called with the log buffer mutex
289    * locked.
290    */
291   void discardExcessOldLogsLocked(bool encoded, uint8_t currentLogLen);
292 
293   /**
294    * Add an encoding header to the log message if the encoding param is true.
295    * This function must only be called with the log buffer mutex locked.
296    */
297   void encodeAndCopyLogLocked(LogBufferLogLevel level, uint32_t timestampMs,
298                               const void *logBuffer, uint8_t logLen,
299                               bool encoded);
300 
301   /**
302    * Send ready to dispatch logs over, based on the current log notification
303    * setting
304    */
305   void dispatch();
306 
307   /**
308    * The buffer data is stored in the format
309    *
310    * [ metadata (1B) , timestamp (4B), data (dataLenB) ]
311    *
312    * The upper nibble of the metadata indicates if an encoding scheme was used,
313    * while the lower nibble indicates the severity level of this log.
314    *
315    * The data buffer is encoded as follows:
316    *  - In the case of encoded logs, the first byte indicates the number of
317    *    actual log data bytes that follow. These are typically used as
318    *    information for the decoder, which decodes the log data from a 1 byte
319    *    offset.
320    *  - When logs are unencoded, the data buffer can be interpreted as a
321    *    NULL terminated C-style string (pass to string manipulation functions,
322    *    get size from strlen(), etc.).
323    *
324    * This pattern is repeated as many times as there is log entries in the
325    * buffer.
326    *
327    * Since dataLength cannot be greater than uint8_t the max size of the data
328    * portion can be max 255.
329    */
330   uint8_t *const mBufferData;
331 
332   // TODO(b/170870354): Create a cirular buffer class to reuse this concept in
333   // other parts of CHRE
334   //! The buffer data head index
335   size_t mBufferDataHeadIndex = 0;
336   //! The buffer data tail index
337   size_t mBufferDataTailIndex = 0;
338   //! The current size of the data buffer
339   size_t mBufferDataSize = 0;
340   //! The buffer max size
341   size_t mBufferMaxSize;
342   //! The number of logs that have been dropped
343   size_t mNumLogsDropped = 0;
344   //! The buffer min size
345   // TODO(b/170870354): Setup a more appropriate min size
346   static constexpr size_t kBufferMinSize = 1024;  // 1KB
347 
348   //! The callback object
349   LogBufferCallbackInterface *mCallback;
350   //! The notification setting object
351   LogBufferNotificationSetting mNotificationSetting =
352       LogBufferNotificationSetting::ALWAYS;
353   //! The number of bytes that will trigger the threshold notification
354   size_t mNotificationThresholdBytes = 0;
355 
356   // TODO(srok): Optimize the locking scheme
357   //! The mutex guarding all thread safe operations.
358   Mutex mLock;
359 };
360 
361 }  // namespace chre
362 
363 #endif  // CHRE_PLATFORM_SHARED_LOG_BUFFER_H_
364