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