222 lines
7.4 KiB
C++
222 lines
7.4 KiB
C++
/*
|
|
* 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 <endian.h>
|
|
#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<Detokenizer> LogMessageParser::logDetokenizerInit() {
|
|
#ifdef CHRE_TOKENIZED_LOGGING_ENABLED
|
|
constexpr const char kLogDatabaseFilePath[] =
|
|
"/vendor/etc/chre/libchre_log_database.bin";
|
|
std::vector<uint8_t> 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<Detokenizer>(database);
|
|
} else {
|
|
LOGE("CHRE Token database creation not OK");
|
|
}
|
|
} else {
|
|
LOGE("Failed to read CHRE Token database file");
|
|
}
|
|
#endif
|
|
return std::unique_ptr<Detokenizer>(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<const LogMessage *>(&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<EncodedLog *>(
|
|
reinterpret_cast<const EncodedLog *>(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<const LogMessageV2 *>(&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
|