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