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