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