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