blob: 7542d445902635b77818d8dbc447368dc16f5cd7 [file] [log] [blame]
// Copyright 2019 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_data/attachments/inspect_ptr.h"
#include <lib/async/cpp/task.h>
#include <lib/fit/result.h>
#include <lib/fostr/fidl/fuchsia/diagnostics/formatting.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <memory>
#include <string>
#include <vector>
#include "src/developer/forensics/utils/errors.h"
#include "src/developer/forensics/utils/fit/promise.h"
#include "src/developer/forensics/utils/fit/timeout.h"
#include "src/lib/fsl/vmo/strings.h"
#include "src/lib/fxl/strings/join_strings.h"
namespace forensics {
namespace feedback_data {
::fit::promise<AttachmentValue> CollectInspectData(async_dispatcher_t* dispatcher,
std::shared_ptr<sys::ServiceDirectory> services,
fit::Timeout timeout) {
std::unique_ptr<Inspect> inspect = std::make_unique<Inspect>(dispatcher, services);
// We must store the promise in a variable due to the fact that the order of evaluation of
// function parameters is undefined.
auto inspect_data = inspect->Collect(std::move(timeout));
return fit::ExtendArgsLifetimeBeyondPromise(/*promise=*/std::move(inspect_data),
/*args=*/std::move(inspect));
}
Inspect::Inspect(async_dispatcher_t* dispatcher, std::shared_ptr<sys::ServiceDirectory> services)
: archive_(dispatcher, services, kArchiveAccessorName) {}
::fit::promise<AttachmentValue> Inspect::Collect(fit::Timeout timeout) {
// We set up the connection and all the error handlers.
SetUp();
// We start the Inspect data collection.
StreamInspectSnapshot();
// We wait on one way to finish the flow, joining whichever data has been collected.
return archive_.WaitForDone(std::move(timeout))
.then([this](::fit::result<void, Error>& result) -> ::fit::result<AttachmentValue> {
if (inspect_data_.empty()) {
FX_LOGS(WARNING) << "Empty Inspect data";
AttachmentValue value = (result.is_ok()) ? AttachmentValue(Error::kMissingValue)
: AttachmentValue(result.error());
return ::fit::ok(std::move(value));
}
std::string joined_data = "[\n";
joined_data += fxl::JoinStrings(inspect_data_, ",\n");
joined_data += "\n]";
AttachmentValue value = (result.is_ok())
? AttachmentValue(std::move(joined_data))
: AttachmentValue(std::move(joined_data), result.error());
return ::fit::ok(std::move(value));
});
}
void Inspect::SetUp() {
snapshot_iterator_.set_error_handler([this](zx_status_t status) {
if (archive_.IsAlreadyDone()) {
return;
}
FX_PLOGS(WARNING, status) << "Lost connection to fuchsia.diagnostics.BatchIterator";
archive_.CompleteError(Error::kConnectionError);
});
}
void Inspect::StreamInspectSnapshot() {
fuchsia::diagnostics::StreamParameters stream_parameters;
stream_parameters.set_data_type(fuchsia::diagnostics::DataType::INSPECT);
stream_parameters.set_stream_mode(fuchsia::diagnostics::StreamMode::SNAPSHOT);
stream_parameters.set_format(fuchsia::diagnostics::Format::JSON);
stream_parameters.set_client_selector_configuration(
fuchsia::diagnostics::ClientSelectorConfiguration::WithSelectAll(true));
archive_->StreamDiagnostics(std::move(stream_parameters), snapshot_iterator_.NewRequest());
AppendNextInspectBatch();
}
void Inspect::AppendNextInspectBatch() {
snapshot_iterator_->GetNext([this](auto result) {
if (archive_.IsAlreadyDone()) {
return;
}
if (result.is_err()) {
FX_LOGS(WARNING) << "Failed to retrieve next Inspect batch: " << result.err();
// TODO(fxbug.dev/51658): don't complete the flow on an error. The API says we should continue
// making calls instead.
archive_.CompleteError(Error::kBadValue);
return;
}
std::vector<fuchsia::diagnostics::FormattedContent> batch = std::move(result.response().batch);
if (batch.empty()) { // We have gotten all the Inspect data.
archive_.CompleteOk();
return;
}
for (const auto& chunk : batch) {
if (!chunk.is_json()) {
FX_LOGS(WARNING) << "Missing JSON Inspect chunk, skipping";
continue;
}
std::string json;
if (!fsl::StringFromVmo(chunk.json(), &json)) {
FX_LOGS(WARNING) << "Failed to convert Inspect data chunk to string, skipping";
continue;
}
inspect_data_.push_back(json);
}
AppendNextInspectBatch();
});
}
} // namespace feedback_data
} // namespace forensics