blob: 91d15db6ae9e969099a32ee7675078ec6cc11a7c [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/forensics/feedback/attachments/kernel_log.h"
#include <inttypes.h>
#include <lib/async/cpp/task.h>
#include <lib/fpromise/bridge.h>
#include <lib/fpromise/promise.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/syscalls/log.h>
#include <variant>
#include "src/developer/forensics/utils/errors.h"
#include "src/lib/fxl/strings/join_strings.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace forensics::feedback {
KernelLog::KernelLog(async_dispatcher_t* dispatcher,
std::shared_ptr<sys::ServiceDirectory> services,
std::unique_ptr<backoff::Backoff> backoff, RedactorBase* redactor)
: dispatcher_(dispatcher),
services_(std::move(services)),
backoff_(std::move(backoff)),
redactor_(redactor) {
read_only_log_.set_error_handler([this](const zx_status_t status) {
FX_PLOGS(WARNING, status) << "Lost connection to fuchsia.boot.ReadOnlyLog";
for (auto& waiting : waiting_) {
if (waiting != nullptr) {
waiting(Error::kConnectionError);
}
}
auto self = ptr_factory_.GetWeakPtr();
async::PostDelayedTask(
dispatcher_,
[self] {
if (self) {
self->services_->Connect(self->read_only_log_.NewRequest(self->dispatcher_));
}
},
backoff_->GetNext());
});
services_->Connect(read_only_log_.NewRequest(dispatcher_));
}
::fpromise::promise<AttachmentValue> KernelLog::Get(const zx::duration timeout) {
if (!read_only_log_.is_bound()) {
return ::fpromise::make_ok_promise(AttachmentValue(Error::kConnectionError));
}
::fpromise::bridge<zx::debuglog, Error> bridge;
// Construct a promise and an object that can be used to complete the promise with a value at a
// later point in time.
auto consume = bridge.consumer.promise_or(::fpromise::error(Error::kLogicError));
::fit::callback<void(std::variant<zx::debuglog, Error>)> complete =
[completer = std::move(bridge.completer)](std::variant<zx::debuglog, Error> result) mutable {
if (std::holds_alternative<zx::debuglog>(result)) {
completer.complete_ok(std::move(std::get<zx::debuglog>(result)));
} else {
completer.complete_error(std::get<Error>(result));
}
};
auto self = ptr_factory_.GetWeakPtr();
async::PostDelayedTask(
dispatcher_,
[self, complete = complete.share()]() mutable {
if (complete != nullptr) {
complete(Error::kTimeout);
}
},
timeout);
read_only_log_->Get([complete = complete.share()](zx::debuglog debuglog) mutable {
if (complete != nullptr) {
complete(std::move(debuglog));
}
});
return consume
.and_then([this](zx::debuglog& debuglog) {
waiting_.erase(std::remove(waiting_.begin(), waiting_.end(), nullptr), waiting_.end());
std::vector<std::string> messages;
std::array<char, ZX_LOG_RECORD_MAX + 1> buf;
auto* record = reinterpret_cast<zx_log_record_t*>(buf.data());
while (debuglog.read(/*options=*/0, /*buffer=*/record,
/*buffer_size=*/ZX_LOG_RECORD_MAX) > 0) {
if (auto& len = record->datalen; len != 0 && record->data[len - 1] == '\n') {
--len;
}
record->data[record->datalen] = 0;
std::string data(record->data);
data = redactor_->Redact(data);
messages.push_back(
fxl::StringPrintf("[%05d.%03d] %05" PRIu64 ".%05" PRIu64 "> %s\n",
static_cast<int>(record->timestamp / 1000000000ULL),
static_cast<int>((record->timestamp / 1000000ULL) % 1000ULL),
record->pid, record->tid, data.c_str()));
}
if (messages.empty()) {
FX_LOGS(ERROR) << "Empty kernel log";
return ::fpromise::ok(AttachmentValue(Error::kMissingValue));
}
return ::fpromise::ok(AttachmentValue(fxl::JoinStrings(messages)));
})
.or_else([](const Error& error) { return ::fpromise::ok(AttachmentValue(error)); });
}
} // namespace forensics::feedback