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 { ERROR, WARN, INFO, DEBUG, VERBOSE };
45 
46 // Forward declaration for LogBufferCallbackInterface.
47 class LogBuffer;
48 
49 /**
50  * Callback objects that are implemented by the platform code and passed to the
51  * log buffer instances are notified of changes in the state of the buffer
52  * through this callback interface.
53  */
54 class LogBufferCallbackInterface {
55  public:
~LogBufferCallbackInterface()56   virtual ~LogBufferCallbackInterface() {}
57 
58   /**
59    * Notify the platform code that is using the buffer manager that it should
60    * call copyLogs because the buffer internal state has changed to suit the
61    * requirements for alerting the platform that logs are ready to be copied
62    * out of buffer.
63    */
64   virtual void onLogsReady() = 0;
65 };
66 
67 /**
68  * The class responsible for batching logs in memory until the notification
69  * callback is triggered and the platform copies log data out of the buffer.
70  */
71 class LogBuffer {
72  public:
73   //! The max size of a single log entry which must fit in a single byte.
74   static constexpr size_t kLogMaxSize = UINT8_MAX;
75 
76   /**
77    * @param callback The callback object that will receive notifications about
78    *                 the state of the log buffer or nullptr if it is not needed.
79    * @param buffer The buffer location that will store log data.
80    *                    message.
81    * @param bufferSize The number of bytes in the buffer. This value must be >
82    *                   kBufferMinSize
83    */
84   LogBuffer(LogBufferCallbackInterface *callback, void *buffer,
85             size_t bufferSize);
86 
87   /**
88    * Buffer this log and possibly call on logs ready callback depending on the
89    * notification setting in place.  The method is thread-safe and will ensure
90    * that logs are buffered in a FIFO ordering. If the buffer is full then drop
91    * the oldest log.
92    *
93    * @param logLevel The log level.
94    * @param timestampMs The timestamp that the log was collected as in
95    *                    milliseconds. Monotonically increasing and in
96    *                    milliseconds since boot.
97    * @param logFormat The ASCII log format that is buffered.
98    * @param ... The variable length set of parameters to print into the
99    *            logFormat string.
100    */
101   void handleLog(LogBufferLogLevel logLevel, uint32_t timestampMs,
102                  const char *logFormat, ...);
103 
104   /**
105    * Same as handleLog but with a va_list argument instead of a ... parameter.
106    *
107    * @param args The arguments in a va_list type.
108    */
109   void handleLogVa(LogBufferLogLevel logLevel, uint32_t timestampMs,
110                    const char *logFormat, va_list args);
111 
112   // TODO(b/179786399): Remove the copyLogs method when the LogBufferManager is
113   // refactored to no longer use it.
114   /**
115    * Copy out as many logs as will fit into destination buffer as they are
116    * formatted internally. The memory where the logs were stored will be freed.
117    * This method is thread-safe and will ensure that copyLogs will only copy
118    * out the logs in a FIFO ordering.
119    *
120    * @param destination Pointer to the destination memory address.
121    * @param size The max number of bytes to copy.
122    * @param numLogsDropped Non-null pointer which will be set to the number of
123    * logs dropped since CHRE started.
124    *
125    * @return The number of bytes copied from buffer to destination which may be
126    *         less than size because partial logs are not copied into
127    *         destination or the number of bytes left in the buffer is less than
128    *         size.
129    */
130   size_t copyLogs(void *destination, size_t size, size_t *numLogsDropped);
131 
132   /**
133    *
134    * @param logSize The size of the log text in bytes.
135    * @return true if log would cause an overflow of the buffer and would
136    * overwrite a log if it was pushed onto the buffer.
137    */
138   bool logWouldCauseOverflow(size_t logSize);
139 
140   /**
141    * Transfer all data from one log buffer to another. The destination log
142    * buffer must have equal or greater capacity than this buffer. The
143    * otherBuffer will be reset prior to this buffer's data being transferred to
144    * it and after the transfer this buffer will be reset. This method is
145    * thread-safe and will ensure that logs are kept in FIFO ordering during a
146    * transfer operation.
147    *
148    * @param otherBuffer The log buffer that is transferred to.
149    */
150   void transferTo(LogBuffer &otherBuffer);
151 
152   /**
153    * Update the current log buffer notification setting which will determine
154    * when the platform is notified to copy logs out of the buffer. Thread-safe.
155    *
156    * @param setting The new notification setting value.
157    * @param thresholdBytes If the nofification setting is THRESHOLD, then if
158    *                       the buffer allocates this many bytes the notification
159    *                       callback will be triggerd, otherwise this parameter
160    *                       is ignored.
161    */
162   void updateNotificationSetting(LogBufferNotificationSetting setting,
163                                  size_t thresholdBytes = 0);
164 
165   /**
166    * Thread safe.
167    *
168    * Empty out the log entries currently in the buffer and reset the number of
169    * logs dropped.
170    */
171   void reset();
172 
173   /**
174    * The data inside the buffer that is returned may be altered by
175    * another thread so it is up to the calling code to ensure that race
176    * conditions do not occur on writes to the data.
177    *
178    * @return The pointer to the underlying data buffer.
179    */
180   const uint8_t *getBufferData();
181 
182   /**
183    * Thread safe.
184    *
185    * @return The current buffer size.
186    */
187   size_t getBufferSize();
188 
189   /**
190    *
191    * Thread safe.
192    *
193    * @return The number of logs dropped since the object was last reset or
194    * instantiated.
195    */
196   size_t getNumLogsDropped();
197 
198  private:
199   /**
200    * Increment the value and take the modulus of the max size of the buffer.
201    *
202    * @param originalVal The original value to increment and mod.
203    * @param incrementBy The amount to increment by.
204    * @return The final value after incrementing and modulus.
205    */
206   size_t incrementAndModByBufferMaxSize(size_t originalVal,
207                                         size_t incrementBy) const;
208 
209   /**
210    * Copy from the source memory location to the buffer data ensuring that
211    * the copy wraps around the buffer data if needed.
212    *
213    * @param size The number of bytes to copy into the buffer.
214    * @param source The memory location to copy from.
215    */
216   void copyToBuffer(size_t size, const void *source);
217 
218   /**
219    * Copy from the buffer data to a destination memory location ensuring that
220    * the copy wraps around the buffer data if needed.
221    *
222    * @param size The number of bytes to copy into the buffer.
223    * @param destination The memory location to copy to.
224    */
225   void copyFromBuffer(size_t size, void *destination);
226 
227   /**
228    * Same as copyLogs method but requires that a lock already be held.
229    */
230   size_t copyLogsLocked(void *destination, size_t size, size_t *numLogsDropped);
231 
232   /**
233    * Same as reset method but requires that a lock already be held.
234    */
235   void resetLocked();
236 
237   /**
238    * Get next index indicating the start of a log entry from the starting
239    * index of a previous log entry.
240    *
241    * @param startingIndex The starting index given.
242    * @param logSize Non-null pointer that will be set to the size of the current
243    *        log message.
244    * @return The next starting log index.
245    */
246   size_t getNextLogIndex(size_t startingIndex, size_t *logSize);
247 
248   /**
249    * @param startingIndex The index to start from.
250    * @return The length of the data portion of a log along with the null
251    *         terminator. If a null terminator was not found at most
252    *         kLogMaxSize - kLogDataOffset bytes away from the startingIndex
253    *         then kLogMaxSize - kLogDataOffset + 1 is returned.
254    */
255   size_t getLogDataLength(size_t startingIndex);
256 
257   //! The number of bytes in a log entry of the buffer before the log data is
258   //! encountered.
259   static constexpr size_t kLogDataOffset = 5;
260 
261   /**
262    * The buffer data is stored in the format
263    *
264    * [ logLevel (1B) , timestamp (4B), data (dataLenB) , \0 (1B) ]
265    *
266    * This pattern is repeated as many times as there is log entries in the
267    * buffer.
268    *
269    * Since dataLength cannot be greater than uint8_t the max size of the data
270    * portion can be max 255.
271    */
272   uint8_t *const mBufferData;
273 
274   // TODO(b/170870354): Create a cirular buffer class to reuse this concept in
275   // other parts of CHRE
276   //! The buffer data head index
277   size_t mBufferDataHeadIndex = 0;
278   //! The buffer data tail index
279   size_t mBufferDataTailIndex = 0;
280   //! The current size of the data buffer
281   size_t mBufferDataSize = 0;
282   //! The buffer max size
283   size_t mBufferMaxSize;
284   //! The number of logs that have been dropped
285   size_t mNumLogsDropped = 0;
286   //! The buffer min size
287   // TODO(b/170870354): Setup a more appropriate min size
288   static constexpr size_t kBufferMinSize = 1024;  // 1KB
289 
290   //! The callback object
291   LogBufferCallbackInterface *mCallback;
292   //! The notification setting object
293   LogBufferNotificationSetting mNotificationSetting =
294       LogBufferNotificationSetting::ALWAYS;
295   //! The number of bytes that will trigger the threshold notification
296   size_t mNotificationThresholdBytes = 0;
297 
298   // TODO(srok): Optimize the locking scheme
299   //! The mutex guarding all thread safe operations.
300   Mutex mLock;
301 };
302 
303 }  // namespace chre
304 
305 #endif  // CHRE_PLATFORM_SHARED_LOG_BUFFER_H_
306