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