| // 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 |