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), &timestampMs);
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