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_manager.h"
18
19 #include "chre/core/event_loop_manager.h"
20 #include "chre/platform/assert.h"
21 #include "chre/platform/shared/bt_snoop_log.h"
22 #include "chre/platform/shared/generated/host_messages_generated.h"
23 #include "chre/util/lock_guard.h"
24
25 #ifdef CHRE_TOKENIZED_LOGGING_ENABLED
26 #include "chre/platform/log.h"
27 #include "pw_log_tokenized/config.h"
28 #include "pw_tokenizer/encode_args.h"
29 #include "pw_tokenizer/tokenize.h"
30 #endif // CHRE_TOKENIZED_LOGGING_ENABLED
31
chrePlatformLogToBuffer(chreLogLevel chreLogLevel,const char * format,...)32 void chrePlatformLogToBuffer(chreLogLevel chreLogLevel, const char *format,
33 ...) {
34 va_list args;
35 va_start(args, format);
36 if (chre::LogBufferManagerSingleton::isInitialized()) {
37 chre::LogBufferManagerSingleton::get()->logVa(chreLogLevel, format, args);
38 }
39 va_end(args);
40 }
41
chrePlatformEncodedLogToBuffer(chreLogLevel level,const uint8_t * msg,size_t msgSize)42 void chrePlatformEncodedLogToBuffer(chreLogLevel level, const uint8_t *msg,
43 size_t msgSize) {
44 if (chre::LogBufferManagerSingleton::isInitialized()) {
45 chre::LogBufferManagerSingleton::get()->logEncoded(level, msg, msgSize);
46 }
47 }
48
chrePlatformBtSnoopLog(BtSnoopDirection direction,const uint8_t * buffer,size_t size)49 void chrePlatformBtSnoopLog(BtSnoopDirection direction, const uint8_t *buffer,
50 size_t size) {
51 chre::LogBufferManagerSingleton::get()->logBtSnoop(direction, buffer, size);
52 }
53
54 #ifdef CHRE_TOKENIZED_LOGGING_ENABLED
55 // The callback function that must be defined to handle an encoded
56 // tokenizer message.
EncodeTokenizedMessage(uint32_t level,pw_tokenizer_Token token,pw_tokenizer_ArgTypes types,...)57 void EncodeTokenizedMessage(uint32_t level, pw_tokenizer_Token token,
58 pw_tokenizer_ArgTypes types, ...) {
59 va_list args;
60 va_start(args, types);
61 pw::tokenizer::EncodedMessage<pw::log_tokenized::kEncodingBufferSizeBytes>
62 encodedMessage(token, types, args);
63 va_end(args);
64
65 chrePlatformEncodedLogToBuffer(static_cast<chreLogLevel>(level),
66 encodedMessage.data_as_uint8(),
67 encodedMessage.size());
68 }
69 #endif // CHRE_TOKENIZED_LOGGING_ENABLED
70
71 namespace chre {
72
73 using LogType = fbs::LogType;
74
onLogsReady()75 void LogBufferManager::onLogsReady() {
76 LockGuard<Mutex> lockGuard(mFlushLogsMutex);
77 if (!mLogFlushToHostPending) {
78 if (EventLoopManagerSingleton::isInitialized() &&
79 EventLoopManagerSingleton::get()
80 ->getEventLoop()
81 .getPowerControlManager()
82 .hostIsAwake()) {
83 mLogFlushToHostPending = true;
84 mSendLogsToHostCondition.notify_one();
85 }
86 } else {
87 mLogsBecameReadyWhileFlushPending = true;
88 }
89 }
90
flushLogs()91 void LogBufferManager::flushLogs() {
92 onLogsReady();
93 }
94
onLogsSentToHost(bool success)95 void LogBufferManager::onLogsSentToHost(bool success) {
96 LockGuard<Mutex> lockGuard(mFlushLogsMutex);
97 onLogsSentToHostLocked(success);
98 }
99
startSendLogsToHostLoop()100 void LogBufferManager::startSendLogsToHostLoop() {
101 LockGuard<Mutex> lockGuard(mFlushLogsMutex);
102 // TODO(b/181871430): Allow this loop to exit for certain platforms
103 while (true) {
104 while (!mLogFlushToHostPending) {
105 mSendLogsToHostCondition.wait(mFlushLogsMutex);
106 }
107 bool logWasSent = false;
108 if (EventLoopManagerSingleton::get()
109 ->getEventLoop()
110 .getPowerControlManager()
111 .hostIsAwake()) {
112 auto &hostCommsMgr =
113 EventLoopManagerSingleton::get()->getHostCommsManager();
114 preSecondaryBufferUse();
115 if (mSecondaryLogBuffer.getBufferSize() == 0) {
116 // TODO (b/184178045): Transfer logs into the secondary buffer from
117 // primary if there is room.
118 mPrimaryLogBuffer.transferTo(mSecondaryLogBuffer);
119 }
120 // If the primary buffer was not flushed to the secondary buffer then set
121 // the flag that will cause sendLogsToHost to be run again after
122 // onLogsSentToHost has been called and the secondary buffer has been
123 // cleared out.
124 if (mPrimaryLogBuffer.getBufferSize() > 0) {
125 mLogsBecameReadyWhileFlushPending = true;
126 }
127 if (mSecondaryLogBuffer.getBufferSize() > 0) {
128 mNumLogsDroppedTotal += mSecondaryLogBuffer.getNumLogsDropped();
129 mFlushLogsMutex.unlock();
130 hostCommsMgr.sendLogMessageV2(mSecondaryLogBuffer.getBufferData(),
131 mSecondaryLogBuffer.getBufferSize(),
132 mNumLogsDroppedTotal);
133 logWasSent = true;
134 mFlushLogsMutex.lock();
135 }
136 }
137 if (!logWasSent) {
138 onLogsSentToHostLocked(false);
139 }
140 }
141 }
142
log(chreLogLevel logLevel,const char * formatStr,...)143 void LogBufferManager::log(chreLogLevel logLevel, const char *formatStr, ...) {
144 va_list args;
145 va_start(args, formatStr);
146 logVa(logLevel, formatStr, args);
147 va_end(args);
148 }
149
getTimestampMs()150 uint32_t LogBufferManager::getTimestampMs() {
151 uint64_t timeNs = SystemTime::getMonotonicTime().toRawNanoseconds();
152 return static_cast<uint32_t>(timeNs / kOneMillisecondInNanoseconds);
153 }
154
bufferOverflowGuard(size_t logSize,LogType type)155 void LogBufferManager::bufferOverflowGuard(size_t logSize, LogType type) {
156 switch (type) {
157 case LogType::STRING:
158 logSize += LogBuffer::kStringLogOverhead;
159 break;
160 case LogType::TOKENIZED:
161 logSize += LogBuffer::kTokenizedLogOffset;
162 break;
163 case LogType::BLUETOOTH:
164 logSize += LogBuffer::kBtSnoopLogOffset;
165 break;
166 case LogType::NANOAPP_TOKENIZED:
167 logSize += LogBuffer::kNanoappTokenizedLogOffset;
168 break;
169 default:
170 CHRE_ASSERT_LOG(false, "Received unexpected log message type");
171 break;
172 }
173 if (mPrimaryLogBuffer.logWouldCauseOverflow(logSize)) {
174 LockGuard<Mutex> lockGuard(mFlushLogsMutex);
175 if (!mLogFlushToHostPending) {
176 preSecondaryBufferUse();
177 mPrimaryLogBuffer.transferTo(mSecondaryLogBuffer);
178 }
179 }
180 }
181
logVa(chreLogLevel logLevel,const char * formatStr,va_list args)182 void LogBufferManager::logVa(chreLogLevel logLevel, const char *formatStr,
183 va_list args) {
184 // Copy the va_list before getting size from vsnprintf so that the next
185 // argument that will be accessed in buffer.handleLogVa is the starting one.
186 va_list getSizeArgs;
187 va_copy(getSizeArgs, args);
188 size_t logSize = vsnprintf(nullptr, 0, formatStr, getSizeArgs);
189 va_end(getSizeArgs);
190 bufferOverflowGuard(logSize, LogType::STRING);
191 mPrimaryLogBuffer.handleLogVa(chreToLogBufferLogLevel(logLevel),
192 getTimestampMs(), formatStr, args);
193 }
194
logBtSnoop(BtSnoopDirection direction,const uint8_t * buffer,size_t size)195 void LogBufferManager::logBtSnoop(BtSnoopDirection direction,
196 const uint8_t *buffer, size_t size) {
197 #ifdef CHRE_BLE_SUPPORT_ENABLED
198 bufferOverflowGuard(size, LogType::BLUETOOTH);
199 mPrimaryLogBuffer.handleBtLog(direction, getTimestampMs(), buffer, size);
200 #else
201 UNUSED_VAR(direction);
202 UNUSED_VAR(buffer);
203 UNUSED_VAR(size);
204 #endif // CHRE_BLE_SUPPORT_ENABLED
205 }
206
logEncoded(chreLogLevel logLevel,const uint8_t * encodedLog,size_t encodedLogSize)207 void LogBufferManager::logEncoded(chreLogLevel logLevel,
208 const uint8_t *encodedLog,
209 size_t encodedLogSize) {
210 bufferOverflowGuard(encodedLogSize, LogType::TOKENIZED);
211 mPrimaryLogBuffer.handleEncodedLog(chreToLogBufferLogLevel(logLevel),
212 getTimestampMs(), encodedLog,
213 encodedLogSize);
214 }
215
logNanoappTokenized(chreLogLevel logLevel,uint16_t instanceId,const uint8_t * msg,size_t msgSize)216 void LogBufferManager::logNanoappTokenized(chreLogLevel logLevel,
217 uint16_t instanceId,
218 const uint8_t *msg, size_t msgSize) {
219 bufferOverflowGuard(msgSize, LogType::NANOAPP_TOKENIZED);
220 mPrimaryLogBuffer.handleNanoappTokenizedLog(chreToLogBufferLogLevel(logLevel),
221 getTimestampMs(), instanceId, msg,
222 msgSize);
223 }
224
chreToLogBufferLogLevel(chreLogLevel chreLogLevel)225 LogBufferLogLevel LogBufferManager::chreToLogBufferLogLevel(
226 chreLogLevel chreLogLevel) {
227 switch (chreLogLevel) {
228 case CHRE_LOG_ERROR:
229 return LogBufferLogLevel::ERROR;
230 case CHRE_LOG_WARN:
231 return LogBufferLogLevel::WARN;
232 case CHRE_LOG_INFO:
233 return LogBufferLogLevel::INFO;
234 default: // CHRE_LOG_DEBUG
235 return LogBufferLogLevel::DEBUG;
236 }
237 }
238
onLogsSentToHostLocked(bool success)239 void LogBufferManager::onLogsSentToHostLocked(bool success) {
240 if (success) {
241 mSecondaryLogBuffer.reset();
242 }
243 // If there is a failure to send a log through do not try to send another
244 // one to avoid an infinite loop occurring
245 mLogFlushToHostPending = mLogsBecameReadyWhileFlushPending && success;
246 mLogsBecameReadyWhileFlushPending = false;
247 if (mLogFlushToHostPending) {
248 mSendLogsToHostCondition.notify_one();
249 }
250 }
251
252 //! Explicitly instantiate the EventLoopManagerSingleton to reduce codesize.
253 template class Singleton<LogBufferManager>;
254
255 } // namespace chre
256