• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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), &timestampMs);
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), &timestampMs);
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