blob: 28afefd83567e8fc4b74696d2f4854f86653e9b1 [file] [log] [blame]
// Copyright 2020 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.
use {
super::collector::InspectDataCollector,
crate::{
container::{ComponentIdentity, ReadSnapshot, SnapshotData},
events::types::InspectData,
},
diagnostics_data as schema,
diagnostics_hierarchy::InspectHierarchyMatcher,
fidl_fuchsia_io::DirectoryProxy,
fuchsia_async::{DurationExt, TimeoutExt},
fuchsia_inspect::reader::snapshot::{Snapshot, SnapshotTree},
fuchsia_zircon::{self as zx, DurationNum},
inspect_fidl_load as deprecated_inspect,
std::convert::TryFrom,
std::sync::Arc,
tracing::warn,
};
pub struct InspectArtifactsContainer {
/// DirectoryProxy for the out directory that this
/// data packet is configured for.
pub component_diagnostics_proxy: Arc<DirectoryProxy>,
/// The time when the DiagnosticsReady event that caused the creation of
/// the inspect artifact container was created.
pub event_timestamp: zx::Time,
}
/// PopulatedInspectDataContainer is the container that
/// holds the actual Inspect data for a given component,
/// along with all information needed to transform that data
/// to be returned to the client.
pub struct PopulatedInspectDataContainer {
pub identity: Arc<ComponentIdentity>,
/// Vector of all the snapshots of inspect hierarchies under
/// the diagnostics directory of the component identified by
/// relative_moniker, along with the metadata needed to populate
/// this snapshot's diagnostics schema.
pub snapshots: Vec<SnapshotData>,
/// Optional hierarchy matcher. If unset, the reader is running
/// in all-access mode, meaning no matching or filtering is required.
pub inspect_matcher: Option<InspectHierarchyMatcher>,
}
impl PopulatedInspectDataContainer {
async fn from(
unpopulated: Arc<UnpopulatedInspectDataContainer>,
) -> PopulatedInspectDataContainer {
let mut collector = InspectDataCollector::new();
match collector.populate_data_map(&unpopulated.component_diagnostics_proxy).await {
Ok(_) => {
let mut snapshots_data_opt = None;
if let Some(data_map) = Box::new(collector).take_data() {
let mut acc: Vec<SnapshotData> = vec![];
for (filename, data) in data_map {
match data {
InspectData::Tree(tree, _) => match SnapshotTree::try_from(&tree).await
{
Ok(snapshot_tree) => {
acc.push(SnapshotData::successful(
ReadSnapshot::Tree(snapshot_tree),
filename,
));
}
Err(e) => {
acc.push(SnapshotData::failed(
schema::Error { message: format!("{:?}", e) },
filename,
));
}
},
InspectData::DeprecatedFidl(inspect_proxy) => {
match deprecated_inspect::load_hierarchy(inspect_proxy).await {
Ok(hierarchy) => {
acc.push(SnapshotData::successful(
ReadSnapshot::Finished(hierarchy),
filename,
));
}
Err(e) => {
acc.push(SnapshotData::failed(
schema::Error { message: format!("{:?}", e) },
filename,
));
}
}
}
InspectData::Vmo(vmo) => match Snapshot::try_from(&vmo) {
Ok(snapshot) => {
acc.push(SnapshotData::successful(
ReadSnapshot::Single(snapshot),
filename,
));
}
Err(e) => {
acc.push(SnapshotData::failed(
schema::Error { message: format!("{:?}", e) },
filename,
));
}
},
InspectData::File(contents) => match Snapshot::try_from(contents) {
Ok(snapshot) => {
acc.push(SnapshotData::successful(
ReadSnapshot::Single(snapshot),
filename,
));
}
Err(e) => {
acc.push(SnapshotData::failed(
schema::Error { message: format!("{:?}", e) },
filename,
));
}
},
InspectData::Empty => {}
}
}
snapshots_data_opt = Some(acc);
}
match snapshots_data_opt {
Some(snapshots) => PopulatedInspectDataContainer {
identity: unpopulated.identity.clone(),
inspect_matcher: unpopulated.inspect_matcher.clone(),
snapshots,
},
None => {
let no_success_snapshot_data = vec![SnapshotData::failed(
schema::Error {
message: format!(
"Failed to extract any inspect data for {:?}",
unpopulated.identity.relative_moniker
),
},
"NO_FILE_SUCCEEDED".to_string(),
)];
PopulatedInspectDataContainer {
identity: unpopulated.identity.clone(),
snapshots: no_success_snapshot_data,
inspect_matcher: unpopulated.inspect_matcher.clone(),
}
}
}
}
Err(e) => {
let no_success_snapshot_data = vec![SnapshotData::failed(
schema::Error {
message: format!(
"Encountered error traversing diagnostics dir for {:?}: {:?}",
&unpopulated.identity.relative_moniker, e
),
},
"NO_FILE_SUCCEEDED".to_string(),
)];
PopulatedInspectDataContainer {
identity: unpopulated.identity.clone(),
snapshots: no_success_snapshot_data,
inspect_matcher: unpopulated.inspect_matcher.clone(),
}
}
}
}
}
/// UnpopulatedInspectDataContainer is the container that holds
/// all information needed to retrieve Inspect data
/// for a given component, when requested.
pub struct UnpopulatedInspectDataContainer {
pub identity: Arc<ComponentIdentity>,
/// DirectoryProxy for the out directory that this
/// data packet is configured for.
pub component_diagnostics_proxy: DirectoryProxy,
/// Optional hierarchy matcher. If unset, the reader is running
/// in all-access mode, meaning no matching or filtering is required.
pub inspect_matcher: Option<InspectHierarchyMatcher>,
}
impl UnpopulatedInspectDataContainer {
/// Populates this data container with a timeout. On the timeout firing returns a
/// container suitable to return to clients, but with timeout error information recorded.
pub async fn populate(
self,
timeout: i64,
on_timeout: impl FnOnce(),
) -> PopulatedInspectDataContainer {
let this = Arc::new(self);
PopulatedInspectDataContainer::from(this.clone())
.on_timeout(timeout.seconds().after_now(), move || {
on_timeout();
let error_string = format!(
"Exceeded per-component time limit for fetching diagnostics data: {:?}",
&this.identity.relative_moniker
);
warn!("{}", error_string);
PopulatedInspectDataContainer {
identity: this.identity.clone(),
inspect_matcher: this.inspect_matcher.clone(),
snapshots: vec![SnapshotData::failed(
schema::Error { message: error_string },
"NO_FILE_SUCCEEDED".to_string(),
)],
}
})
.await
}
}