blob: 1cf9c79daaf923c7ec6ec2234e3200e203131260 [file] [log] [blame]
// Copyright 2024 Google LLC
//
// 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 "log_collector_util.hpp"
#include "message_intf.hpp"
#include "message_util.hpp"
#include <fmt/core.h>
#include <chrono>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <regex>
#include <span>
#include <vector>
namespace google
{
namespace hoth
{
namespace internal
{
namespace fs = std::filesystem;
LogCollectorUtil::LogCollectorUtil(RateLimiter& rateLimiter,
int asyncWaitTime) :
rateLimiter(rateLimiter), asyncWaitTime(asyncWaitTime)
{}
std::vector<uint8_t> LogCollectorUtil::generateSnapshotRequest()
{
std::vector<uint8_t> static const requestBuffer(kTransactionBufferSize -
sizeof(struct ReqHeader));
struct ReqHeader requestHeader;
populateReqHeader(EC_CMD_CONSOLE_REQUEST, /*commandVersion*/ 0x00,
requestBuffer.data(), /*requestPayloadSize*/ 0,
&requestHeader);
auto *const requestHeaderPtr = reinterpret_cast<uint8_t *>(&requestHeader);
std::vector<uint8_t> populatedCommand(requestHeaderPtr,
requestHeaderPtr + sizeof(ReqHeader));
if constexpr (kDebug)
{
// snapshot doesn't return any buffer response
fmt::println(stderr, "Request size: ", populatedCommand.size());
}
return populatedCommand;
}
std::vector<uint8_t> LogCollectorUtil::generateGrabSnapshotRequest()
{
struct ec_params_console_read_v1 readSnapshotReq = {.subcmd =
CONSOLE_READ_NEXT};
struct ReqHeader consoleReadReqHdr;
populateReqHeader(EC_CMD_CONSOLE_READ, /*commandVersion*/ 0x00,
static_cast<void*>(&readSnapshotReq),
sizeof(readSnapshotReq), &consoleReadReqHdr);
auto* const consoleReadReqHdrPtr =
reinterpret_cast<uint8_t*>(&consoleReadReqHdr);
const auto* const consoleReadReqPtr =
reinterpret_cast<const uint8_t*>(&readSnapshotReq);
std::vector<uint8_t> populatedCommandConsoleRead(
consoleReadReqHdrPtr, consoleReadReqHdrPtr + sizeof(consoleReadReqHdr));
populatedCommandConsoleRead.insert(
populatedCommandConsoleRead.end(), consoleReadReqPtr,
consoleReadReqPtr + sizeof(readSnapshotReq));
return populatedCommandConsoleRead;
}
bool LogCollectorUtil::isResponseValid(std::vector<uint8_t> response)
{
std::span<const uint8_t> output = response;
const auto& responseHeader = stdplus::raw::extractRef<RspHeader>(output);
if constexpr (kDebug)
{
fmt::println(stderr, "Response Header result: {}",
static_cast<uint8_t>(responseHeader.result));
}
if (responseHeader.result != 0)
{
fmt::println(stderr,
"Log Collector command received a bad "
"response while getting logs. Unsuccessful response "
"code: {}",
static_cast<uint16_t>(responseHeader.result));
return false;
}
if constexpr (kDebug)
{
fmt::println(stderr, "checksum : {}",
static_cast<uint8_t>(responseHeader.checksum));
fmt::println(stderr, "result : {}",
static_cast<uint16_t>(responseHeader.result));
fmt::println(stderr, "data_len : {}",
static_cast<uint16_t>(responseHeader.data_len));
fmt::println(stderr, "reserved : {}",
static_cast<uint16_t>(responseHeader.reserved));
}
return true;
}
void LogCollectorUtil::writeToFile(std::string_view data, std::string_view name)
{
std::string filePath = fmt::format("{}{}.log", kSyslogFileNamePrefix, name);
if (data.empty())
{
fmt::println(stderr, "No data to publish to filePath: {}", filePath);
return;
}
std::string meta;
if (!fs::exists(filePath))
{
meta =
fmt::format("{} : Uart File does not exist: {}\n", name, filePath);
}
else
{
std::uintmax_t fileSize = fs::file_size(filePath);
if ((fileSize >= kMaxFileSizeInBytes) ||
((kMaxFileSizeInBytes - fileSize) <= data.size()))
{
meta = fmt::format(
"{} {} : Uart log file size exceeds the limit. Rotating...\n",
meta, name);
if (!fs::remove(filePath))
{
fmt::println(
stderr,
"{}\nError deleting the uart log file {}. "
"Not writing to Uart log file, since it can hog memory\n",
meta, filePath);
return;
}
meta = fmt::format(
"{} {} : Old file deleted! New Uart file will be created\n",
meta, name);
}
}
std::ofstream outputFile(filePath, std::ios::app);
if (!outputFile.is_open())
{
fmt::println(stderr, "Error opening the UART log file: {}", filePath);
return;
}
outputFile << meta << '\n';
outputFile << data << '\n';
outputFile.close();
if constexpr (kDebug)
{
fmt::println(stderr, "Written to file {}", filePath);
}
}
void LogCollectorUtil::publishSnapshot(
std::vector<uint8_t> response,
PublishType publishType = PublishType::Stderr,
std::string_view filePathSuffix, std::string_view meta)
{
switch (publishType)
{
case PublishType::Stderr:
{
std::string result(response.begin(), response.end());
fmt::println(stderr, "{} ", result);
fmt::println(stderr, "=Buffer size printed: {}=", response.size());
break;
}
case PublishType::File:
{
std::string suffix;
if (filePathSuffix.empty())
{
fmt::println(stderr,
"Failure to publish due to filepath empty");
return;
}
std::string result(response.begin(), response.end());
std::regex newlineRegex("\n");
std::string replacementString =
fmt::format("\n{} : ", filePathSuffix);
std::string taggedString =
std::regex_replace(result, newlineRegex, replacementString);
if (!meta.empty())
{
taggedString = fmt::format("{} {}\n{}", filePathSuffix, meta,
taggedString);
}
writeToFile(taggedString, filePathSuffix);
break;
}
default:
break; // no op
}
}
int LogCollectorUtil::generateRequestId()
{
requestCounter++;
if (requestCounter >= 1000)
{
requestCounter = 0;
}
return requestCounter;
}
std::vector<uint8_t> LogCollectorUtil::generateGetChannelWriteOffsetRequest(
const uint32_t &channelId)
{
if constexpr (kDebug) {
fmt::println(stderr, "Getting the hoth channel status for offset for {}...",
channelId);
}
struct ec_channel_status_request request;
request.channel_id = channelId;
struct ReqHeader requestHeader;
populateReqHeader(EC_CMD_BOARD_SPECIFIC_BASE + EC_PRV_CMD_HOTH_CHANNEL_STATUS,
/*commandVersion*/ 0x00, static_cast<void *>(&request),
sizeof(request), &requestHeader);
auto *const requestHeaderPtr = reinterpret_cast<uint8_t *>(&requestHeader);
const auto *const requestPtr = reinterpret_cast<const uint8_t *>(&request);
std::vector<uint8_t> populatedCommand(
requestHeaderPtr, requestHeaderPtr + sizeof(requestHeader));
populatedCommand.insert(populatedCommand.end(), requestPtr,
requestPtr + sizeof(request));
return populatedCommand;
}
std::vector<uint8_t>
LogCollectorUtil::generateCollectUartLogsRequest(const uint32_t channelId,
const uint32_t readOffset)
{
struct ec_channel_read_request request = {
.channel_id = channelId,
.offset = readOffset,
.size = kTransactionBufferSize,
.timeout_us = kTransactionTimeoutInMicroSec,
};
struct ReqHeader requestHeader;
populateReqHeader(EC_CMD_BOARD_SPECIFIC_BASE + EC_CMD_HOTH_CHANNEL_READ,
/*commandVersion*/ 0x00, static_cast<void *>(&request),
sizeof(request), &requestHeader);
auto *const requestHeaderPtr = reinterpret_cast<uint8_t *>(&requestHeader);
const auto *const requestPtr = reinterpret_cast<const uint8_t *>(&request);
std::vector<uint8_t> populatedCommand(
requestHeaderPtr, requestHeaderPtr + sizeof(requestHeader));
populatedCommand.insert(populatedCommand.end(), requestPtr,
requestPtr + sizeof(request));
return populatedCommand;
}
void LogCollectorUtil::heartbeat(std::string_view message)
{
fmt::println(stderr, "{}", message);
}
int LogCollectorUtil::getAsyncWaitTime() const
{
return this->asyncWaitTime;
}
} // namespace internal
} // namespace hoth
} // namespace google