xref: /aosp_15_r20/system/chre/host/common/include/chre_host/log_message_parser.h (revision 84e339476a462649f82315436d70fd732297a399)
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 #ifndef CHRE_LOG_MESSAGE_PARSER_H_
18 #define CHRE_LOG_MESSAGE_PARSER_H_
19 
20 #include <endian.h>
21 #include <cinttypes>
22 #include <memory>
23 #include <mutex>
24 #include <optional>
25 #include "chre/util/time.h"
26 #include "chre_host/bt_snoop_log_parser.h"
27 #include "chre_host/generated/host_messages_generated.h"
28 #include "chre_host/nanoapp_load_listener.h"
29 
30 #include <android-base/thread_annotations.h>
31 #include <android/log.h>
32 
33 #include "pw_tokenizer/detokenize.h"
34 
35 using chre::fbs::LogType;
36 using pw::tokenizer::DetokenizedString;
37 using pw::tokenizer::Detokenizer;
38 
39 namespace android {
40 namespace chre {
41 
42 class LogMessageParser : public INanoappLoadListener {
43  public:
44   LogMessageParser();
45 
46   /**
47    * Allow the user to enable verbose logging during instantiation.
48    */
LogMessageParser(bool enableVerboseLogging)49   LogMessageParser(bool enableVerboseLogging)
50       : mVerboseLoggingEnabled(enableVerboseLogging) {}
51 
52   /**
53    * Initializes the log message parser by reading the log token database,
54    * and instantiates a detokenizer to handle encoded log messages.
55    *
56    * @param nanoappImageHeaderSize Offset in bytes between the address of the
57    * nanoapp binary and the real start of the ELF header.
58    */
59   void init(size_t nanoappImageHeaderSize = 0);
60 
61   //! Logs from a log buffer containing one or more log messages (version 1)
62   void log(const uint8_t *logBuffer, size_t logBufferSize);
63 
64   //! Logs from a log buffer containing one or more log messages (version 2)
65   void logV2(const uint8_t *logBuffer, size_t logBufferSize,
66              uint32_t numLogsDropped);
67 
68   /**
69    * With verbose logging enabled (either during instantiation via a
70    * constructor argument, or during compilation via N_DEBUG being defined
71    * and set), dump a binary log buffer to a human-readable string.
72    *
73    * @param logBuffer buffer to be output as a string
74    * @param logBufferSize size of the buffer being output
75    */
76   void dump(const uint8_t *logBuffer, size_t logBufferSize);
77 
78   /**
79    * Stores a pigweed detokenizer for decoding logs from a given nanoapp.
80    *
81    * @param appId The app ID associated with the nanoapp.
82    * @param instanceId The instance ID assigned to this nanoapp by the CHRE
83    * event loop.
84    * @param databaseOffset The size offset of the token database from the start
85    * of the address of the ELF binary in bytes.
86    * @param databaseSize The size of the token database section in the ELF
87    * binary in bytes.
88    */
89   void addNanoappDetokenizer(uint64_t appId, uint16_t instanceId,
90                              uint64_t databaseOffset, size_t databaseSize);
91 
92   /**
93    * Remove an existing detokenizer associated with a nanoapp if it exists.
94    *
95    * @param appId The app ID associated with the nanoapp.
96    * @param removeBinary Remove the nanoapp binary associated with the app ID if
97    * true.
98    */
99   void removeNanoappDetokenizerAndBinary(uint64_t appId)
100       EXCLUDES(mNanoappMutex);
101 
102   /**
103    * Reset all nanoapp log detokenizers.
104    */
105   void resetNanoappDetokenizerState();
106 
107   // Functions from INanoappLoadListener.
108   void onNanoappLoadStarted(
109       uint64_t appId,
110       std::shared_ptr<const std::vector<uint8_t>> nanoappBinary) override;
111 
112   void onNanoappLoadFailed(uint64_t appId) override;
113 
114   void onNanoappUnloaded(uint64_t appId) override;
115 
116  private:
117   static constexpr char kHubLogFormatStr[] = "@ %3" PRIu32 ".%03" PRIu32 ": %s";
118 
119   // Constants used to extract the log type from log metadata.
120   static constexpr uint8_t kLogTypeMask = 0xF0;
121   static constexpr uint8_t kLogTypeBitOffset = 4;
122 
123   enum LogLevel : uint8_t {
124     ERROR = 1,
125     WARNING = 2,
126     INFO = 3,
127     DEBUG = 4,
128     VERBOSE = 5,
129   };
130 
131   //! See host_messages.fbs for the definition of this struct.
132   struct LogMessage {
133     enum LogLevel logLevel;
134     uint64_t timestampNanos;
135     char logMessage[];
136   } __attribute__((packed));
137 
138   //! See host_messages.fbs for the definition of this struct.
139   struct LogMessageV2 {
140     uint8_t metadata;
141     uint32_t timestampMillis;
142     char logMessage[];
143   } __attribute__((packed));
144 
145   /**
146    * Helper struct for readable decoding of a tokenized log message payload,
147    * essentially encapsulates the 'logMessage' field in LogMessageV2 for an
148    * encoded log.
149    */
150   struct EncodedLog {
151     uint8_t size;
152     char data[];
153   };
154 
155   /**
156    * Helper struct for readable decoding of a tokenized log message from a
157    * nanoapp.
158    */
159   struct NanoappTokenizedLog {
160     uint16_t instanceId;
161     uint8_t size;
162     char data[];
163   } __attribute__((packed));
164 
165   bool mVerboseLoggingEnabled;
166 
167   //! The number of logs dropped since CHRE start.
168   uint32_t mNumLogsDropped = 0;
169 
170   //! Log detokenizer used for CHRE system logs.
171   std::unique_ptr<Detokenizer> mSystemDetokenizer;
172 
173   /**
174    * Helper struct for keep track of nanoapp's log detokenizer with appIDs.
175    */
176   struct NanoappDetokenizer {
177     std::unique_ptr<Detokenizer> detokenizer;
178     uint64_t appId;
179   };
180 
181   //! Maps nanoapp instance IDs to the corresponding app ID and pigweed
182   //! detokenizer. Guarded by mNanoappMutex.
183   std::unordered_map<uint16_t /*instanceId*/, NanoappDetokenizer>
184       mNanoappDetokenizers GUARDED_BY(mNanoappMutex);
185 
186   //! This is used to find the binary associated with a nanoapp with its app ID.
187   //! Guarded by mNanoappMutex.
188   std::unordered_map<uint64_t /*appId*/,
189                      std::shared_ptr<const std::vector<uint8_t>>>
190       mNanoappAppIdToBinary GUARDED_BY(mNanoappMutex);
191 
192   //! The mutex used to guard operations of mNanoappAppIdtoBinary and
193   //! mNanoappDetokenizers.
194   std::mutex mNanoappMutex;
195 
196   static android_LogPriority chreLogLevelToAndroidLogPriority(uint8_t level);
197 
198   BtSnoopLogParser mBtLogParser;
199 
200   //! Offset in bytes between the address of the nanoapp binary and the real
201   //! start of the ELF header.
202   size_t mNanoappImageHeaderSize = 0;
203 
204   void updateAndPrintDroppedLogs(uint32_t numLogsDropped);
205 
206   //! Method for parsing unencoded (string) log messages.
207   std::optional<size_t> parseAndEmitStringLogMessageAndGetSize(
208       const LogMessageV2 *message, size_t maxLogMessageLen);
209 
210   /**
211    * Parses and emits an encoded log message while also returning the size of
212    * the parsed message for buffer index bookkeeping.
213    *
214    * @param message Buffer containing the log metadata and log payload.
215    * @param maxLogMessageLen The max size allowed for the log payload.
216    * @return Size of the encoded log message payload, std::nullopt if the
217    * message format is invalid. Note that the size includes the 1 byte header
218    * that we use for encoded log messages to track message size.
219    */
220   std::optional<size_t> parseAndEmitTokenizedLogMessageAndGetSize(
221       const LogMessageV2 *message, size_t maxLogMessageLen);
222 
223   /**
224    * Similar to parseAndEmitTokenizedLogMessageAndGetSize, but used for encoded
225    * log message from nanoapps.
226    *
227    * @param message Buffer containing the log metadata and log payload.
228    * @param maxLogMessageLen The max size allowed for the log payload.
229    * @return Size of the encoded log message payload, std::nullopt if the
230    * message format is invalid. Note that the size includes the 1 byte header
231    * that we use for encoded log messages to track message size, and the 2 byte
232    * instance ID that the host uses to find the correct detokenizer.
233    */
234   std::optional<size_t> parseAndEmitNanoappTokenizedLogMessageAndGetSize(
235       const LogMessageV2 *message, size_t maxLogMessageLen);
236 
237   void emitLogMessage(uint8_t level, uint32_t timestampMillis,
238                       const char *logMessage);
239 
240   /**
241    * Initialize the Log Detokenizer
242    *
243    * The log detokenizer reads a binary database file that contains key value
244    * pairs of hash-keys <--> Decoded log messages, and creates an instance
245    * of the Detokenizer.
246    *
247    * @return an instance of the Detokenizer
248    */
249   std::unique_ptr<Detokenizer> logDetokenizerInit();
250 
251   /**
252    * Helper function to get the logging level from the log message metadata.
253    *
254    * @param metadata A byte from the log message payload containing the
255    *        log level and encoding information.
256    *
257    * @return The log level of the current log message.
258    */
259   uint8_t getLogLevelFromMetadata(uint8_t metadata);
260 
261   /**
262    * Helper function to check the metadata whether the log message was encoded.
263    *
264    * @param metadata A byte from the log message payload containing the
265    *        log level and encoding information.
266    *
267    * @return true if an encoding was used on the log message payload.
268    */
269   bool isLogMessageEncoded(uint8_t metadata);
270 
271   /**
272    * Helper function to check the metadata whether the log message is a BT snoop
273    * log.
274    *
275    * @param metadata A byte from the log message payload containing the
276    *        log level and log type information.
277    *
278    * @return true if the log message type is BT Snoop log.
279    */
280   bool isBtSnoopLogMessage(uint8_t metadata);
281 
282   /**
283    * Helper function to check the metadata whether the log message is tokenized
284    * and sent from a nanoapp
285    *
286    * @param metadata A byte from the log message payload containing the
287    *        log level and log type information.
288    *
289    * @return true if the log message is tokenzied and sent from a nanoapp.
290    */
291   bool isNanoappTokenizedLogMessage(uint8_t metadata);
292 
293   /**
294    * Helper function to check nanoapp log token database for memory overflow and
295    * wraparound.
296    *
297    * @param databaseOffset The size offset of the token database from the start
298    * of the address of the ELF binary in bytes.
299    * @param databaseSize The size of the token database section in the ELF
300    * binary in bytes.
301    * @param binarySize. The size of the nanoapp binary in bytes.
302    *
303    * @return True if the token database passes memory bounds checks.
304    */
305   bool checkTokenDatabaseOverflow(uint32_t databaseOffset, size_t databaseSize,
306                                   size_t binarySize);
307 
308   /**
309    * Helper function that returns the log type of a log message.
310    */
extractLogType(const LogMessageV2 * message)311   LogType extractLogType(const LogMessageV2 *message) {
312     return static_cast<LogType>((message->metadata & kLogTypeMask) >>
313                                 kLogTypeBitOffset);
314   }
315 
316   /**
317    * Helper function that returns the nanoapp binary from its appId.
318    */
319   std::shared_ptr<const std::vector<uint8_t>> fetchNanoappBinary(uint64_t appId)
320       EXCLUDES(mNanoappMutex);
321 
322   /**
323    * Helper function that registers a nanoapp detokenizer with its appID and
324    * instanceID.
325    */
326   void registerDetokenizer(uint64_t appId, uint16_t instanceId,
327                            pw::Result<Detokenizer> nanoappDetokenizer)
328       EXCLUDES(mNanoappMutex);
329 };
330 
331 }  // namespace chre
332 }  // namespace android
333 
334 #endif  // CHRE_LOG_MESSAGE_PARSER_H_
335