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