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), ×tampMs);
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