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