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