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 #include "chre/platform/shared/log_buffer.h"
18 #include "chre/platform/assert.h"
19 #include "chre/platform/shared/generated/host_messages_generated.h"
20 #include "chre/util/lock_guard.h"
21
22 #include <cstdarg>
23 #include <cstdio>
24
25 namespace chre {
26
27 using LogType = fbs::LogType;
28
LogBuffer(LogBufferCallbackInterface * callback,void * buffer,size_t bufferSize)29 LogBuffer::LogBuffer(LogBufferCallbackInterface *callback, void *buffer,
30 size_t bufferSize)
31 : mBufferData(static_cast<uint8_t *>(buffer)),
32 mBufferMaxSize(bufferSize),
33 mCallback(callback) {
34 CHRE_ASSERT(bufferSize >= kBufferMinSize);
35 }
36
handleLog(LogBufferLogLevel logLevel,uint32_t timestampMs,const char * logFormat,...)37 void LogBuffer::handleLog(LogBufferLogLevel logLevel, uint32_t timestampMs,
38 const char *logFormat, ...) {
39 va_list args;
40 va_start(args, logFormat);
41 handleLogVa(logLevel, timestampMs, logFormat, args);
42 va_end(args);
43 }
44
handleLogVa(LogBufferLogLevel logLevel,uint32_t timestampMs,const char * logFormat,va_list args)45 void LogBuffer::handleLogVa(LogBufferLogLevel logLevel, uint32_t timestampMs,
46 const char *logFormat, va_list args) {
47 constexpr size_t maxLogLen = kLogMaxSize - kLogDataOffset;
48 char tempBuffer[maxLogLen];
49 int logLenSigned = vsnprintf(tempBuffer, maxLogLen, logFormat, args);
50 processLog(logLevel, timestampMs, tempBuffer, logLenSigned,
51 false /* encoded */);
52 }
53
54 #ifdef CHRE_BLE_SUPPORT_ENABLED
handleBtLog(BtSnoopDirection direction,uint32_t timestampMs,const uint8_t * buffer,size_t size)55 void LogBuffer::handleBtLog(BtSnoopDirection direction, uint32_t timestampMs,
56 const uint8_t *buffer, size_t size) {
57 if (size > 0) {
58 auto logLen = static_cast<uint8_t>(size);
59
60 if (size < kLogMaxSize) {
61 LockGuard<Mutex> lockGuard(mLock);
62
63 // No additional terminator towards the end.
64 discardExcessOldLogsLocked(false, logLen);
65
66 uint8_t logType = static_cast<uint8_t>(LogType::BLUETOOTH);
67 uint8_t snoopLogDirection = static_cast<uint8_t>(direction);
68
69 // Set all BT logs to the CHRE_LOG_LEVEL_INFO.
70 uint8_t metadata =
71 (static_cast<uint8_t>(logType) << 4) | CHRE_LOG_LEVEL_INFO;
72 copyToBuffer(sizeof(metadata), &metadata);
73
74 copyToBuffer(sizeof(timestampMs), ×tampMs);
75 copyToBuffer(sizeof(direction), &snoopLogDirection);
76 copyToBuffer(sizeof(logLen), &logLen);
77
78 copyToBuffer(logLen, buffer);
79 } else {
80 // Cannot truncate a BT event. Log a failure message instead.
81 constexpr char kBtSnoopLogGenericErrorMsg[] =
82 "Bt Snoop log message too large";
83 static_assert(
84 sizeof(kBtSnoopLogGenericErrorMsg) <= kLogMaxSize,
85 "Error meessage size needs to be smaller than max log length");
86 logLen = static_cast<uint8_t>(sizeof(kBtSnoopLogGenericErrorMsg));
87 copyLogToBuffer(LogBufferLogLevel::INFO, timestampMs,
88 kBtSnoopLogGenericErrorMsg, logLen, false /* encoded */);
89 }
90 dispatch();
91 }
92 }
93 #endif // CHRE_BLE_SUPPORT_ENABLED
94
handleEncodedLog(LogBufferLogLevel logLevel,uint32_t timestampMs,const uint8_t * log,size_t logSize)95 void LogBuffer::handleEncodedLog(LogBufferLogLevel logLevel,
96 uint32_t timestampMs, const uint8_t *log,
97 size_t logSize) {
98 processLog(logLevel, timestampMs, log, logSize);
99 }
100
copyLogs(void * destination,size_t size,size_t * numLogsDropped)101 size_t LogBuffer::copyLogs(void *destination, size_t size,
102 size_t *numLogsDropped) {
103 LockGuard<Mutex> lock(mLock);
104 return copyLogsLocked(destination, size, numLogsDropped);
105 }
106
logWouldCauseOverflow(size_t logSize)107 bool LogBuffer::logWouldCauseOverflow(size_t logSize) {
108 LockGuard<Mutex> lock(mLock);
109 return (mBufferDataSize + logSize + kLogDataOffset + 1 /* nullptr */ >
110 mBufferMaxSize);
111 }
112
transferTo(LogBuffer & buffer)113 void LogBuffer::transferTo(LogBuffer &buffer) {
114 LockGuard<Mutex> lockGuardOther(buffer.mLock);
115 size_t numLogsDropped;
116 size_t bytesCopied;
117 {
118 LockGuard<Mutex> lockGuardThis(mLock);
119 // The buffer being transferred to should be as big or bigger.
120 CHRE_ASSERT(buffer.mBufferMaxSize >= mBufferMaxSize);
121
122 buffer.resetLocked();
123
124 bytesCopied = copyLogsLocked(buffer.mBufferData, buffer.mBufferMaxSize,
125 &numLogsDropped);
126
127 resetLocked();
128 }
129 buffer.mBufferDataTailIndex = bytesCopied % buffer.mBufferMaxSize;
130 buffer.mBufferDataSize = bytesCopied;
131 buffer.mNumLogsDropped = numLogsDropped;
132 }
133
updateNotificationSetting(LogBufferNotificationSetting setting,size_t thresholdBytes)134 void LogBuffer::updateNotificationSetting(LogBufferNotificationSetting setting,
135 size_t thresholdBytes) {
136 LockGuard<Mutex> lock(mLock);
137
138 mNotificationSetting = setting;
139 mNotificationThresholdBytes = thresholdBytes;
140 }
141
reset()142 void LogBuffer::reset() {
143 LockGuard<Mutex> lock(mLock);
144 resetLocked();
145 }
146
getBufferData()147 const uint8_t *LogBuffer::getBufferData() {
148 return mBufferData;
149 }
150
getBufferSize()151 size_t LogBuffer::getBufferSize() {
152 LockGuard<Mutex> lockGuard(mLock);
153 return mBufferDataSize;
154 }
155
getNumLogsDropped()156 size_t LogBuffer::getNumLogsDropped() {
157 LockGuard<Mutex> lockGuard(mLock);
158 return mNumLogsDropped;
159 }
160
incrementAndModByBufferMaxSize(size_t originalVal,size_t incrementBy) const161 size_t LogBuffer::incrementAndModByBufferMaxSize(size_t originalVal,
162 size_t incrementBy) const {
163 return (originalVal + incrementBy) % mBufferMaxSize;
164 }
165
copyToBuffer(size_t size,const void * source)166 void LogBuffer::copyToBuffer(size_t size, const void *source) {
167 const uint8_t *sourceBytes = static_cast<const uint8_t *>(source);
168 if (mBufferDataTailIndex + size > mBufferMaxSize) {
169 size_t firstSize = mBufferMaxSize - mBufferDataTailIndex;
170 size_t secondSize = size - firstSize;
171 memcpy(&mBufferData[mBufferDataTailIndex], sourceBytes, firstSize);
172 memcpy(mBufferData, &sourceBytes[firstSize], secondSize);
173 } else {
174 memcpy(&mBufferData[mBufferDataTailIndex], sourceBytes, size);
175 }
176 mBufferDataSize += size;
177 mBufferDataTailIndex =
178 incrementAndModByBufferMaxSize(mBufferDataTailIndex, size);
179 }
180
copyFromBuffer(size_t size,void * destination)181 void LogBuffer::copyFromBuffer(size_t size, void *destination) {
182 uint8_t *destinationBytes = static_cast<uint8_t *>(destination);
183 if (mBufferDataHeadIndex + size > mBufferMaxSize) {
184 size_t firstSize = mBufferMaxSize - mBufferDataHeadIndex;
185 size_t secondSize = size - firstSize;
186 memcpy(destinationBytes, &mBufferData[mBufferDataHeadIndex], firstSize);
187 memcpy(&destinationBytes[firstSize], mBufferData, secondSize);
188 } else {
189 memcpy(destinationBytes, &mBufferData[mBufferDataHeadIndex], size);
190 }
191 mBufferDataSize -= size;
192 mBufferDataHeadIndex =
193 incrementAndModByBufferMaxSize(mBufferDataHeadIndex, size);
194 }
195
copyLogsLocked(void * destination,size_t size,size_t * numLogsDropped)196 size_t LogBuffer::copyLogsLocked(void *destination, size_t size,
197 size_t *numLogsDropped) {
198 size_t copySize = 0;
199
200 if (size != 0 && destination != nullptr && mBufferDataSize != 0) {
201 if (size >= mBufferDataSize) {
202 copySize = mBufferDataSize;
203 } else {
204 size_t logSize;
205 // There is guaranteed to be a null terminator within the max log length
206 // number of bytes so logStartIndex will not be maxBytes + 1
207 size_t logStartIndex = getNextLogIndex(mBufferDataHeadIndex, &logSize);
208 while (copySize + logSize <= size &&
209 copySize + logSize <= mBufferDataSize) {
210 copySize += logSize;
211 logStartIndex = getNextLogIndex(logStartIndex, &logSize);
212 }
213 }
214 copyFromBuffer(copySize, destination);
215 }
216
217 *numLogsDropped = mNumLogsDropped;
218
219 return copySize;
220 }
221
resetLocked()222 void LogBuffer::resetLocked() {
223 mBufferDataHeadIndex = 0;
224 mBufferDataTailIndex = 0;
225 mBufferDataSize = 0;
226 mNumLogsDropped = 0;
227 }
228
getNextLogIndex(size_t startingIndex,size_t * logSize)229 size_t LogBuffer::getNextLogIndex(size_t startingIndex, size_t *logSize) {
230 size_t logDataStartIndex =
231 incrementAndModByBufferMaxSize(startingIndex, kLogDataOffset);
232
233 size_t logDataSize = getLogDataLength(logDataStartIndex);
234 *logSize = kLogDataOffset + logDataSize;
235 return incrementAndModByBufferMaxSize(startingIndex, *logSize);
236 }
237
getLogDataLength(size_t startingIndex)238 size_t LogBuffer::getLogDataLength(size_t startingIndex) {
239 size_t currentIndex = startingIndex;
240 constexpr size_t maxBytes = kLogMaxSize - kLogDataOffset;
241 size_t numBytes = maxBytes + 1;
242
243 for (size_t i = 0; i < maxBytes; i++) {
244 if (mBufferData[currentIndex] == '\0') {
245 // +1 to include the null terminator
246 numBytes = i + 1;
247 break;
248 }
249 currentIndex = incrementAndModByBufferMaxSize(currentIndex, 1);
250 }
251 return numBytes;
252 }
253
processLog(LogBufferLogLevel logLevel,uint32_t timestampMs,const void * logBuffer,size_t size,bool encoded)254 void LogBuffer::processLog(LogBufferLogLevel logLevel, uint32_t timestampMs,
255 const void *logBuffer, size_t size, bool encoded) {
256 if (size > 0) {
257 auto logLen = static_cast<uint8_t>(size);
258 if (size >= kLogMaxSize) {
259 if (!encoded) {
260 // Leave space for null terminator to be copied on end
261 logLen = static_cast<uint8_t>(kLogMaxSize - 1);
262 } else {
263 // There is no way of decoding an encoded message if we truncate it, so
264 // we do the next best thing and try to log a generic failure message
265 // reusing the logbuffer for as much as we can. Note that we also need
266 // flip the encoding flag for proper decoding by the host log message
267 // parser.
268 constexpr char kTokenizedLogGenericErrorMsg[] =
269 "Tokenized log message too large";
270 static_assert(
271 sizeof(kTokenizedLogGenericErrorMsg) <= kLogMaxSize,
272 "Error meessage size needs to be smaller than max log length");
273 logBuffer = kTokenizedLogGenericErrorMsg;
274 logLen = static_cast<uint8_t>(sizeof(kTokenizedLogGenericErrorMsg));
275 encoded = false;
276 }
277 }
278 copyLogToBuffer(logLevel, timestampMs, logBuffer, logLen, encoded);
279 dispatch();
280 }
281 }
282
copyLogToBuffer(LogBufferLogLevel level,uint32_t timestampMs,const void * logBuffer,uint8_t logLen,bool encoded)283 void LogBuffer::copyLogToBuffer(LogBufferLogLevel level, uint32_t timestampMs,
284 const void *logBuffer, uint8_t logLen,
285 bool encoded) {
286 LockGuard<Mutex> lockGuard(mLock);
287 discardExcessOldLogsLocked(encoded, logLen);
288 encodeAndCopyLogLocked(level, timestampMs, logBuffer, logLen, encoded);
289 }
290
discardExcessOldLogsLocked(bool encoded,uint8_t currentLogLen)291 void LogBuffer::discardExcessOldLogsLocked(bool encoded,
292 uint8_t currentLogLen) {
293 size_t totalLogSize =
294 kLogDataOffset + (encoded ? currentLogLen : currentLogLen + 1);
295 while (mBufferDataSize + totalLogSize > mBufferMaxSize) {
296 mNumLogsDropped++;
297 size_t logSize;
298 mBufferDataHeadIndex = getNextLogIndex(mBufferDataHeadIndex, &logSize);
299 mBufferDataSize -= logSize;
300 }
301 }
302
encodeAndCopyLogLocked(LogBufferLogLevel level,uint32_t timestampMs,const void * logBuffer,uint8_t logLen,bool encoded)303 void LogBuffer::encodeAndCopyLogLocked(LogBufferLogLevel level,
304 uint32_t timestampMs,
305 const void *logBuffer, uint8_t logLen,
306 bool encoded) {
307 uint8_t metadata =
308 (static_cast<uint8_t>(encoded) << 4) | static_cast<uint8_t>(level);
309
310 copyToBuffer(sizeof(uint8_t), &metadata);
311 copyToBuffer(sizeof(timestampMs), ×tampMs);
312
313 if (encoded) {
314 copyToBuffer(sizeof(uint8_t), &logLen);
315 }
316 copyToBuffer(logLen, logBuffer);
317 if (!encoded) {
318 copyToBuffer(1, reinterpret_cast<const void *>("\0"));
319 }
320 }
321
dispatch()322 void LogBuffer::dispatch() {
323 if (mCallback != nullptr) {
324 switch (mNotificationSetting) {
325 case LogBufferNotificationSetting::ALWAYS: {
326 mCallback->onLogsReady();
327 break;
328 }
329 case LogBufferNotificationSetting::NEVER: {
330 break;
331 }
332 case LogBufferNotificationSetting::THRESHOLD: {
333 if (mBufferDataSize > mNotificationThresholdBytes) {
334 mCallback->onLogsReady();
335 }
336 break;
337 }
338 }
339 }
340 }
341
342 } // namespace chre
343