1 /*
2  * Copyright (C) 2022 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 <android-base/file.h>
18 #include <log/log.h>
19 #include <mutex>
20 
21 namespace android {
22 namespace hardware {
23 namespace contexthub {
24 
25 /**
26  * Class to help request and synchronize dumping CHRE debug information.
27  */
28 class DebugDumpHelper {
29  public:
~DebugDumpHelper()30   virtual ~DebugDumpHelper() {}
31 
32   /**
33    * Implementation specific method to send a debug dump request.
34    *
35    * @return true on a successful request, false otherwise.
36    */
37   virtual bool requestDebugDump() = 0;
38 
39   /**
40    * Implementation specific method to write a string to a debug file.
41    * Note that this function must only be called after a debug dump request
42    * has been initiated via 'startDebugDump()'.
43    *
44    * @param str String to be written to the debug dump file. MUST be NULL
45    *        terminated.
46    */
47   virtual void writeToDebugFile(const char *str) = 0;
48 
49   /**
50    * Optional implementation specific method to write any debug info private to
51    * said implementation.
52    * Note that this function must only be called after a debug dump request
53    * has been initiated via 'startDebugDump()'.
54    */
debugDumpFinish()55   virtual void debugDumpFinish() {}
56 
checkDebugFd()57   bool checkDebugFd() {
58     return mDebugFd != kInvalidFd;
59   }
60 
getDebugFd()61   int getDebugFd() const {
62     return mDebugFd;
63   }
64 
invalidateDebugFd()65   void invalidateDebugFd() {
66     mDebugFd = kInvalidFd;
67   }
68 
69   /**
70    * Initiate a debug dump request.
71    *
72    * @param fd Posix file descriptor to write debug information into.
73    */
debugDumpStart(int fd)74   void debugDumpStart(int fd) {
75     // Timeout inside CHRE is typically 5 seconds, grant 500ms extra here to let
76     // the data reach us
77     constexpr auto kDebugDumpTimeout = std::chrono::milliseconds(5500);
78     mDebugFd = fd;
79     if (mDebugFd < 0) {
80       ALOGW("Can't dump debug info to invalid fd %d", mDebugFd);
81     } else {
82       writeToDebugFile("-- Dumping CHRE debug info --\n");
83 
84       ALOGV("Sending debug dump request");
85       std::unique_lock<std::mutex> lock(mDebugDumpMutex);
86       mDebugDumpPending = true;
87       if (!requestDebugDump()) {
88         ALOGW("Couldn't send debug dump request");
89       } else {
90         mDebugDumpCond.wait_for(lock, kDebugDumpTimeout,
91                                 [this]() { return !mDebugDumpPending; });
92         if (mDebugDumpPending) {
93           ALOGE("Timed out waiting on debug dump data");
94           mDebugDumpPending = false;
95         }
96       }
97     }
98   }
99 
100   /**
101    * Append to a debug dump file asynchronously. Note that a call to this
102    * function will only go through if a debug dump request was already
103    * initiated via debugDumpStart().
104    *
105    * @param str Debug string to append to the file.
106    */
debugDumpAppend(std::string & str)107   void debugDumpAppend(std::string &str) {
108     if (mDebugFd == kInvalidFd) {
109       ALOGW("Got unexpected debug dump data message");
110     } else {
111       writeToDebugFile(str.c_str());
112     }
113   }
114 
115   /**
116    * Called at the end of a debug dump request.
117    */
debugDumpComplete()118   void debugDumpComplete() {
119     std::lock_guard<std::mutex> lock(mDebugDumpMutex);
120     if (!mDebugDumpPending) {
121       ALOGI("Ignoring duplicate/unsolicited debug dump response");
122     } else {
123       mDebugDumpPending = false;
124       mDebugDumpCond.notify_all();
125       invalidateDebugFd();
126     }
127   }
128 
129  private:
130   static constexpr int kInvalidFd = -1;
131   int mDebugFd = kInvalidFd;
132   bool mDebugDumpPending = false;
133   std::mutex mDebugDumpMutex;
134   std::condition_variable mDebugDumpCond;
135 };
136 
137 }  // namespace contexthub
138 }  // namespace hardware
139 }  // namespace android