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