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