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
21 #include <cstdarg>
22 #include <cstdio>
23
24 namespace chre {
25
LogBuffer(LogBufferCallbackInterface * callback,void * buffer,size_t bufferSize)26 LogBuffer::LogBuffer(LogBufferCallbackInterface *callback, void *buffer,
27 size_t bufferSize)
28 : mBufferData(static_cast<uint8_t *>(buffer)),
29 mBufferMaxSize(bufferSize),
30 mCallback(callback) {
31 CHRE_ASSERT(bufferSize >= kBufferMinSize);
32 }
33
handleLog(LogBufferLogLevel logLevel,uint32_t timestampMs,const char * logFormat,...)34 void LogBuffer::handleLog(LogBufferLogLevel logLevel, uint32_t timestampMs,
35 const char *logFormat, ...) {
36 va_list args;
37 va_start(args, logFormat);
38 handleLogVa(logLevel, timestampMs, logFormat, args);
39 va_end(args);
40 }
41
handleLogVa(LogBufferLogLevel logLevel,uint32_t timestampMs,const char * logFormat,va_list args)42 void LogBuffer::handleLogVa(LogBufferLogLevel logLevel, uint32_t timestampMs,
43 const char *logFormat, va_list args) {
44 constexpr size_t maxLogLen = kLogMaxSize - kLogDataOffset;
45 char tempBuffer[maxLogLen];
46 int logLenSigned = vsnprintf(tempBuffer, maxLogLen, logFormat, args);
47 if (logLenSigned > 0) {
48 size_t logLen = static_cast<size_t>(logLenSigned);
49 if (logLen >= maxLogLen) {
50 // Leave space for nullptr to be copied on end
51 logLen = maxLogLen - 1;
52 }
53 size_t totalLogSize = kLogDataOffset + logLen;
54 {
55 LockGuard<Mutex> lockGuard(mLock);
56 // Invalidate memory allocated for log at head while the buffer is greater
57 // than max size
58 while (mBufferDataSize + totalLogSize > mBufferMaxSize) {
59 mNumLogsDropped++;
60 size_t logSize;
61 mBufferDataHeadIndex = getNextLogIndex(mBufferDataHeadIndex, &logSize);
62 mBufferDataSize -= logSize;
63 }
64 // The final log level as parsed by the daemon requires that the log level
65 // be incremented.
66 uint8_t logLevelAdjusted = static_cast<uint8_t>(logLevel) + 1;
67 copyToBuffer(sizeof(logLevelAdjusted), &logLevelAdjusted);
68 copyToBuffer(sizeof(timestampMs), ×tampMs);
69 copyToBuffer(logLen, tempBuffer);
70 copyToBuffer(1, reinterpret_cast<const void *>("\0"));
71 }
72 if (mCallback != nullptr) {
73 switch (mNotificationSetting) {
74 case LogBufferNotificationSetting::ALWAYS: {
75 mCallback->onLogsReady();
76 break;
77 }
78 case LogBufferNotificationSetting::NEVER: {
79 break;
80 }
81 case LogBufferNotificationSetting::THRESHOLD: {
82 if (mBufferDataSize > mNotificationThresholdBytes) {
83 mCallback->onLogsReady();
84 }
85 break;
86 }
87 }
88 }
89 }
90 }
91
copyLogs(void * destination,size_t size,size_t * numLogsDropped)92 size_t LogBuffer::copyLogs(void *destination, size_t size,
93 size_t *numLogsDropped) {
94 LockGuard<Mutex> lock(mLock);
95 return copyLogsLocked(destination, size, numLogsDropped);
96 }
97
logWouldCauseOverflow(size_t logSize)98 bool LogBuffer::logWouldCauseOverflow(size_t logSize) {
99 LockGuard<Mutex> lock(mLock);
100 return (mBufferDataSize + logSize + kLogDataOffset + 1 /* nullptr */ >
101 mBufferMaxSize);
102 }
103
transferTo(LogBuffer & buffer)104 void LogBuffer::transferTo(LogBuffer &buffer) {
105 LockGuard<Mutex> lockGuardOther(buffer.mLock);
106 size_t numLogsDropped;
107 size_t bytesCopied;
108 {
109 LockGuard<Mutex> lockGuardThis(mLock);
110 // The buffer being transferred to should be as big or bigger.
111 CHRE_ASSERT(buffer.mBufferMaxSize >= mBufferMaxSize);
112
113 buffer.resetLocked();
114
115 bytesCopied = copyLogsLocked(buffer.mBufferData, buffer.mBufferMaxSize,
116 &numLogsDropped);
117
118 resetLocked();
119 }
120 buffer.mBufferDataTailIndex = bytesCopied % buffer.mBufferMaxSize;
121 buffer.mBufferDataSize = bytesCopied;
122 buffer.mNumLogsDropped = numLogsDropped;
123 }
124
updateNotificationSetting(LogBufferNotificationSetting setting,size_t thresholdBytes)125 void LogBuffer::updateNotificationSetting(LogBufferNotificationSetting setting,
126 size_t thresholdBytes) {
127 LockGuard<Mutex> lock(mLock);
128
129 mNotificationSetting = setting;
130 mNotificationThresholdBytes = thresholdBytes;
131 }
132
reset()133 void LogBuffer::reset() {
134 LockGuard<Mutex> lock(mLock);
135 resetLocked();
136 }
137
getBufferData()138 const uint8_t *LogBuffer::getBufferData() {
139 return mBufferData;
140 }
141
getBufferSize()142 size_t LogBuffer::getBufferSize() {
143 LockGuard<Mutex> lockGuard(mLock);
144 return mBufferDataSize;
145 }
146
getNumLogsDropped()147 size_t LogBuffer::getNumLogsDropped() {
148 LockGuard<Mutex> lockGuard(mLock);
149 return mNumLogsDropped;
150 }
151
incrementAndModByBufferMaxSize(size_t originalVal,size_t incrementBy) const152 size_t LogBuffer::incrementAndModByBufferMaxSize(size_t originalVal,
153 size_t incrementBy) const {
154 return (originalVal + incrementBy) % mBufferMaxSize;
155 }
156
copyToBuffer(size_t size,const void * source)157 void LogBuffer::copyToBuffer(size_t size, const void *source) {
158 const uint8_t *sourceBytes = static_cast<const uint8_t *>(source);
159 if (mBufferDataTailIndex + size > mBufferMaxSize) {
160 size_t firstSize = mBufferMaxSize - mBufferDataTailIndex;
161 size_t secondSize = size - firstSize;
162 memcpy(&mBufferData[mBufferDataTailIndex], sourceBytes, firstSize);
163 memcpy(mBufferData, &sourceBytes[firstSize], secondSize);
164 } else {
165 memcpy(&mBufferData[mBufferDataTailIndex], sourceBytes, size);
166 }
167 mBufferDataSize += size;
168 mBufferDataTailIndex =
169 incrementAndModByBufferMaxSize(mBufferDataTailIndex, size);
170 }
171
copyFromBuffer(size_t size,void * destination)172 void LogBuffer::copyFromBuffer(size_t size, void *destination) {
173 uint8_t *destinationBytes = static_cast<uint8_t *>(destination);
174 if (mBufferDataHeadIndex + size > mBufferMaxSize) {
175 size_t firstSize = mBufferMaxSize - mBufferDataHeadIndex;
176 size_t secondSize = size - firstSize;
177 memcpy(destinationBytes, &mBufferData[mBufferDataHeadIndex], firstSize);
178 memcpy(&destinationBytes[firstSize], mBufferData, secondSize);
179 } else {
180 memcpy(destinationBytes, &mBufferData[mBufferDataHeadIndex], size);
181 }
182 mBufferDataSize -= size;
183 mBufferDataHeadIndex =
184 incrementAndModByBufferMaxSize(mBufferDataHeadIndex, size);
185 }
186
copyLogsLocked(void * destination,size_t size,size_t * numLogsDropped)187 size_t LogBuffer::copyLogsLocked(void *destination, size_t size,
188 size_t *numLogsDropped) {
189 size_t copySize = 0;
190
191 if (size != 0 && destination != nullptr && mBufferDataSize != 0) {
192 if (size >= mBufferDataSize) {
193 copySize = mBufferDataSize;
194 } else {
195 size_t logSize;
196 // There is guaranteed to be a null terminator within the max log length
197 // number of bytes so logStartIndex will not be maxBytes + 1
198 size_t logStartIndex = getNextLogIndex(mBufferDataHeadIndex, &logSize);
199 while (copySize + logSize <= size &&
200 copySize + logSize <= mBufferDataSize) {
201 copySize += logSize;
202 logStartIndex = getNextLogIndex(logStartIndex, &logSize);
203 }
204 }
205 copyFromBuffer(copySize, destination);
206 }
207
208 *numLogsDropped = mNumLogsDropped;
209
210 return copySize;
211 }
212
resetLocked()213 void LogBuffer::resetLocked() {
214 mBufferDataHeadIndex = 0;
215 mBufferDataTailIndex = 0;
216 mBufferDataSize = 0;
217 mNumLogsDropped = 0;
218 }
219
getNextLogIndex(size_t startingIndex,size_t * logSize)220 size_t LogBuffer::getNextLogIndex(size_t startingIndex, size_t *logSize) {
221 size_t logDataStartIndex =
222 incrementAndModByBufferMaxSize(startingIndex, kLogDataOffset);
223
224 size_t logDataSize = getLogDataLength(logDataStartIndex);
225 *logSize = kLogDataOffset + logDataSize;
226 return incrementAndModByBufferMaxSize(startingIndex, *logSize);
227 }
228
getLogDataLength(size_t startingIndex)229 size_t LogBuffer::getLogDataLength(size_t startingIndex) {
230 size_t currentIndex = startingIndex;
231 constexpr size_t maxBytes = kLogMaxSize - kLogDataOffset;
232 size_t numBytes = maxBytes + 1;
233
234 for (size_t i = 0; i < maxBytes; i++) {
235 if (mBufferData[currentIndex] == '\0') {
236 // +1 to include the null terminator
237 numBytes = i + 1;
238 break;
239 }
240 currentIndex = incrementAndModByBufferMaxSize(currentIndex, 1);
241 }
242 return numBytes;
243 }
244
245 } // namespace chre
246