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