blob: d15f1289fb751c995fa2476bab80da6a90b9554b [file] [log] [blame]
// Copyright 2021 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.
import 'dart:convert';
import 'package:fidl_fuchsia_diagnostics/fidl_async.dart';
import 'package:fidl_fuchsia_mem/fidl_async.dart';
import 'package:fuchsia_services/services.dart';
import 'package:zircon/zircon.dart';
import 'diagnostic_config.dart';
import 'diagnostic_data.dart';
/// Selector for specifying data to retrieve from [ArchiveReader].
class Selector {
String _rawSelector;
/// Creates [Selector] from raw selector string.
///
/// See https://fuchsia.dev/reference/fidl/fuchsia.diagnostics#SelectorArgument
/// for raw selector format.
factory Selector.fromRawSelector(String rawSelector) {
return Selector._internal(rawSelector: rawSelector);
}
Selector._internal({required String rawSelector})
: _rawSelector = rawSelector;
SelectorArgument get _selectorArgument =>
SelectorArgument.withRawSelector(_rawSelector);
}
/// Wrapper for the [ArchiveAccessor](https://fuchsia.dev/reference/fidl/fuchsia.diagnostics#ArchiveAccessor)
/// service.
///
/// Basic use of this class is as follows:
///
/// ```dart
/// final reader =
/// ArchiveReader.forInspect();
/// final snapshot = reader.snapshot;
/// ```
///
/// TODO(fxbug.dev/71014): Implement lifecycle and log reading.
class ArchiveReader<METADATA> {
List<Selector>? selectors;
DiagnosticsConfiguration<METADATA> diagnosticsConfiguration;
/// Create an [ArchiveReader] for reading the data specified by
/// a [InspectConfiguration], using the given [selectors].
///
/// If no selector is specified the entire inspect tree will be returned.
static ArchiveReader<InspectMetadata> forInspect(
{List<Selector>? selectors}) =>
ArchiveReader._internal(
selectors: selectors,
diagnosticsConfiguration: InspectConfiguration());
ArchiveReader._internal(
{required this.diagnosticsConfiguration, this.selectors});
/// Obtain a snapshot of the current inspect data as constrained by
/// [selector].
///
/// The return value is the json object returned by [FormattedContent.json] as
/// parsed by [jsonDecode].
///
/// If [acceptSnapshot] is not null, this method returns a snapshot once
/// [acceptSnapshot] returns true. If [maxAttempts] is not null, at most
/// [maxAttempts] are made to obtain a snapshot. [delay] controls the delay
/// between snapshot attempts.
Future<List<DiagnosticsData<METADATA>>> snapshot(
{bool acceptSnapshot(List<DiagnosticsData<METADATA>> snapshot)?,
Duration attemptDelay = const Duration(milliseconds: 100),
int? maxAttempts = 200}) async {
for (var attempts = 0;
maxAttempts == null || attempts < maxAttempts;
attempts++) {
final snapshot = await _snapshotAttempt;
if (acceptSnapshot == null || acceptSnapshot(snapshot)) {
return snapshot;
}
await Future.delayed(attemptDelay);
}
throw Exception('snapshot failed');
}
Future<List<DiagnosticsData<METADATA>>> get _snapshotAttempt async {
final archiveAccessor = ArchiveAccessorProxy();
final incoming = Incoming.fromSvcPath()..connectToService(archiveAccessor);
final iterator = BatchIteratorProxy();
late ClientSelectorConfiguration clientSelectorConfiguration;
if (selectors != null) {
clientSelectorConfiguration = ClientSelectorConfiguration.withSelectors(
selectors!.map((selector) => selector._selectorArgument).toList());
} else {
clientSelectorConfiguration =
ClientSelectorConfiguration.withSelectAll(true);
}
await archiveAccessor.streamDiagnostics(
StreamParameters(
dataType: diagnosticsConfiguration.dataType,
streamMode: StreamMode.snapshot,
format: Format.json,
clientSelectorConfiguration: clientSelectorConfiguration),
iterator.ctrl.request());
final List<DiagnosticsData<METADATA>> results = [];
for (var content = await iterator.getNext();
content.isNotEmpty;
content = await iterator.getNext()) {
for (final entry in content) {
Buffer? entryJson = entry.json;
if (entryJson != null) {
final jsonString = _readBuffer(entryJson);
final jsonData = jsonDecode(jsonString);
results.add(DiagnosticsData(
metadata:
diagnosticsConfiguration.buildMetadata(jsonData['metadata']),
dataType: diagnosticsConfiguration.dataType,
moniker: jsonData['moniker'] ?? '',
payload: jsonData['payload'] ?? <String, dynamic>{},
version: jsonData['version'] ?? 0));
}
}
}
iterator.ctrl.close();
await incoming.close();
return results;
}
}
String _readBuffer(Buffer buffer) =>
utf8.decode(SizedVmo(buffer.vmo.handle, buffer.size)
.read(buffer.size)
.bytesAsUint8List());