/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "chre_host/log_message_parser.h" #include #include "chre/util/time.h" #include "chre_host/daemon_base.h" #include "chre_host/log.h" #include "include/chre_host/log_message_parser.h" using chre::kOneMillisecondInNanoseconds; using chre::kOneSecondInMilliseconds; namespace android { namespace chre { namespace { #if defined(LOG_NDEBUG) || LOG_NDEBUG != 0 constexpr bool kVerboseLoggingEnabled = true; #else constexpr bool kVerboseLoggingEnabled = false; #endif } // anonymous namespace LogMessageParser::LogMessageParser() : mVerboseLoggingEnabled(kVerboseLoggingEnabled) {} std::unique_ptr LogMessageParser::logDetokenizerInit() { #ifdef CHRE_TOKENIZED_LOGGING_ENABLED constexpr const char kLogDatabaseFilePath[] = "/vendor/etc/chre/libchre_log_database.bin"; std::vector tokenData; if (ChreDaemonBase::readFileContents(kLogDatabaseFilePath, &tokenData)) { pw::tokenizer::TokenDatabase database = pw::tokenizer::TokenDatabase::Create(tokenData); if (database.ok()) { LOGD("Log database initialized, creating detokenizer"); return std::make_unique(database); } else { LOGE("CHRE Token database creation not OK"); } } else { LOGE("Failed to read CHRE Token database file"); } #endif return std::unique_ptr(nullptr); } void LogMessageParser::init() { mDetokenizer = logDetokenizerInit(); } void LogMessageParser::dump(const uint8_t *buffer, size_t size) { if (mVerboseLoggingEnabled) { char line[32]; char lineChars[32]; int offset = 0; int offsetChars = 0; size_t orig_size = size; if (size > 128) { size = 128; LOGV("Dumping first 128 bytes of buffer of size %zu", orig_size); } else { LOGV("Dumping buffer of size %zu bytes", size); } for (size_t i = 1; i <= size; ++i) { offset += snprintf(&line[offset], sizeof(line) - offset, "%02x ", buffer[i - 1]); offsetChars += snprintf(&lineChars[offsetChars], sizeof(lineChars) - offsetChars, "%c", (isprint(buffer[i - 1])) ? buffer[i - 1] : '.'); if ((i % 8) == 0) { LOGV(" %s\t%s", line, lineChars); offset = 0; offsetChars = 0; } else if ((i % 4) == 0) { offset += snprintf(&line[offset], sizeof(line) - offset, " "); } } if (offset > 0) { char tabs[8]; char *pos = tabs; while (offset < 28) { *pos++ = '\t'; offset += 8; } *pos = '\0'; LOGV(" %s%s%s", line, tabs, lineChars); } } } android_LogPriority LogMessageParser::chreLogLevelToAndroidLogPriority( uint8_t level) { switch (level) { case LogLevel::ERROR: return ANDROID_LOG_ERROR; case LogLevel::WARNING: return ANDROID_LOG_WARN; case LogLevel::INFO: return ANDROID_LOG_INFO; case LogLevel::DEBUG: return ANDROID_LOG_DEBUG; default: return ANDROID_LOG_SILENT; } } uint8_t LogMessageParser::getLogLevelFromMetadata(uint8_t metadata) { // The lower nibble of the metadata denotes the loglevel, as indicated // by the schema in host_messages.fbs. return (metadata & 0xf); } bool LogMessageParser::isLogMessageEncoded(uint8_t metadata) { // The upper nibble of the metadata denotes the encoding, as indicated // by the schema in host_messages.fbs. return (((metadata >> 4) & 0xf) != 0); } void LogMessageParser::log(const uint8_t *logBuffer, size_t logBufferSize) { size_t bufferIndex = 0; while (bufferIndex < logBufferSize) { const LogMessage *message = reinterpret_cast(&logBuffer[bufferIndex]); uint64_t timeNs = le64toh(message->timestampNanos); emitLogMessage(message->logLevel, timeNs / kOneMillisecondInNanoseconds, message->logMessage); bufferIndex += sizeof(LogMessage) + strnlen(message->logMessage, logBufferSize - bufferIndex) + 1; } } size_t LogMessageParser::parseAndEmitTokenizedLogMessageAndGetSize( const LogMessageV2 *message) { size_t logMessageSize = 0; auto detokenizer = mDetokenizer.get(); if (detokenizer != nullptr) { auto *encodedLog = const_cast( reinterpret_cast(message->logMessage)); DetokenizedString detokenizedString = detokenizer->Detokenize(encodedLog->data, encodedLog->size); std::string decodedString = detokenizedString.BestStringWithErrors(); emitLogMessage(getLogLevelFromMetadata(message->metadata), le32toh(message->timestampMillis), decodedString.c_str()); logMessageSize = encodedLog->size + sizeof(struct EncodedLog); } else { LOGE("Null detokenizer! Cannot decode log message"); } return logMessageSize; } void LogMessageParser::parseAndEmitLogMessage(const LogMessageV2 *message) { emitLogMessage(getLogLevelFromMetadata(message->metadata), le32toh(message->timestampMillis), message->logMessage); } void LogMessageParser::updateAndPrintDroppedLogs(uint32_t numLogsDropped) { if (numLogsDropped < mNumLogsDropped) { LOGE( "The numLogsDropped value received from CHRE is less than the last " "value received. Received: %" PRIu32 " Last value: %" PRIu32, numLogsDropped, mNumLogsDropped); } // Log the number of logs dropped once before logging remaining logs from CHRE uint32_t diffLogsDropped = numLogsDropped - mNumLogsDropped; mNumLogsDropped = numLogsDropped; if (diffLogsDropped > 0) { LOGI("# logs dropped: %" PRIu32, diffLogsDropped); } } void LogMessageParser::emitLogMessage(uint8_t level, uint32_t timestampMillis, const char *logMessage) { constexpr const char kLogTag[] = "CHRE"; uint32_t timeSec = timestampMillis / kOneSecondInMilliseconds; uint32_t timeMsRemainder = timestampMillis % kOneSecondInMilliseconds; android_LogPriority priority = chreLogLevelToAndroidLogPriority(level); LOG_PRI(priority, kLogTag, kHubLogFormatStr, timeSec, timeMsRemainder, logMessage); } void LogMessageParser::logV2(const uint8_t *logBuffer, size_t logBufferSize, uint32_t numLogsDropped) { updateAndPrintDroppedLogs(numLogsDropped); size_t bufferIndex = 0; while (bufferIndex < logBufferSize) { const LogMessageV2 *message = reinterpret_cast(&logBuffer[bufferIndex]); size_t bufferIndexDelta = logBufferSize - bufferIndex; size_t logMessageSize; if (isLogMessageEncoded(message->metadata)) { logMessageSize = parseAndEmitTokenizedLogMessageAndGetSize(message); } else { parseAndEmitLogMessage(message); logMessageSize = strlen(message->logMessage) + 1; } bufferIndex += sizeof(LogMessageV2) + std::min(logMessageSize, bufferIndexDelta); } } } // namespace chre } // namespace android