blob: 20b93076e756e2f28d9a0fa9fc8fdfd686a3eb55 [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.
import 'dart:typed_data';
import 'package:fuchsia_services/services.dart';
import 'package:meta/meta.dart';
import 'package:zircon/zircon.dart';
import '../testing/util.dart' show FakeVmoHolder;
import '../vmo/vmo_holder.dart';
import '../vmo/vmo_writer.dart';
import 'internal/_inspect_impl.dart';
part 'node.dart';
part 'property.dart';
// ignore_for_file: public_member_api_docs
typedef OnDemandRootFn = Function(Node);
/// Unless reconfigured, the VMO will be this size.
/// @nodoc
@visibleForTesting
const int defaultVmoSizeBytes = 256 * 1024;
int _nextSuffix = 0;
/// Utility function to Generate unique names for nodes and properties.
String uniqueName(String prefix) {
final suffix = _nextSuffix++;
return '$prefix$suffix';
}
/// Thrown when the programmer misuses Inspect.
class InspectStateError extends StateError {
/// Constructor
InspectStateError(String message) : super(message);
}
/// [Inspect] exposes a structured tree of internal component state.
///
/// The [Inspect] object maintains a hierarchy of [Node] objects whose data are
/// exposed for reading by specialized tools such as iquery.
///
/// The classes exposed by this library do not support reading.
abstract class Inspect {
/// Size of the VMO that was / will be created.
/// @nodoc
static int vmoSize = defaultVmoSizeBytes;
static InspectImpl? _singleton;
/// Maps an inspect instance name to the number of instantiations
/// of that inspector. Used to deduplicate requests for
/// similarly named inspectors.
static Map<String, int> nameToInstanceCount = <String, int>{};
/// For use in testing only. There's probably no way to put @visibleForTesting
/// because this needs to be used by the Validator Puppet, outside the current
/// library.
/// @nodoc
Handle? get vmoHandleForExportTestOnly;
/// Returns a singleton [Inspect] instance at root.inspect
factory Inspect() {
return _singleton ??= InspectImpl(VmoWriter.withSize(vmoSize));
}
/// Returns a new [Inspect] object at root.inspect backed by a fake VMO
/// intended for unit testing inspect integrations, so that they can run as
/// host tests.
factory Inspect.forTesting(FakeVmoHolder vmo) {
// Legacy dart analyzer might warn about this.
// ignore: unnecessary_cast
return InspectImpl(VmoWriter.withVmo(vmo as VmoHolder));
}
/// Mounts an [Inspect] file at <name>.inspect whose contents are
/// dynamically created by rootNodeCallback on each read.
///
/// If methods on this class are called multiple times with the same
/// name, a unique number will be appended to the name.
void onDemand(String name, OnDemandRootFn rootNodeCallback);
/// Attaches the inspect to the outgoing diagnostics/debug dir.
void serve(Outgoing outgoing);
static String nextInstanceWithName(String name) {
final _name = name.endsWith('.inspect') ? name : '$name.inspect';
if (nameToInstanceCount.containsKey(_name)) {
int val = nameToInstanceCount[_name]! + 1;
nameToInstanceCount[_name] = val;
return '${_name}_$val';
} else {
nameToInstanceCount[_name] = 1;
return '$_name';
}
}
/// Optionally configure global settings for inspection.
///
/// This may not be called after the first call to Inspect().
///
/// [vmoSizeBytes]: Sets the maximum size of the virtual memory object (VMO)
/// used to store inspection data for this program.
/// Must be at least 64 bytes.
///
/// Throws [InspectStateError] if called after Inspect(), or [ArgumentError]
/// if called with an invalid vmoSizeBytes.
static void configure({int? vmoSizeBytes}) {
if (_singleton != null) {
throw InspectStateError(
'configureInspect cannot be called after factory runs');
}
if (vmoSizeBytes != null) {
if (vmoSizeBytes < 64) {
throw ArgumentError('VMO size must be at least 64 bytes.');
}
vmoSize = vmoSizeBytes;
}
}
/// The root [Node] of this Inspect tree.
///
/// This node can't be deleted; trying to delete it is a NOP.
Node? get root => _singleton!.root;
/// The health [Node] of this Inspect tree.
///
/// This node can't be deleted once created; but its creation is on demand.
HealthNode get health => _singleton!.health;
}