| // 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. |
| |
| library topaz.public.dart.fuchsia_inspect.inspect.inspect; |
| |
| import 'dart:typed_data'; |
| |
| import 'package:fuchsia_services/services.dart'; |
| import 'package:fuchsia_vfs/vfs.dart'; |
| import 'package:meta/meta.dart'; |
| import 'package:zircon/zircon.dart'; |
| |
| import '../vmo/vmo_writer.dart'; |
| import 'internal/_inspect_impl.dart'; |
| |
| part 'node.dart'; |
| part 'property.dart'; |
| |
| typedef OnDemandRootFn = Function(Node); |
| |
| /// Unless reconfigured, the VMO will be this size. |
| /// @nodoc |
| @visibleForTesting |
| const int defaultVmoSizeBytes = 256 * 1024; |
| |
| /// 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; |
| |
| /// 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() { |
| if (_singleton == null) { |
| var context = StartupContext.fromStartupInfo(); |
| var writer = VmoWriter.withSize(vmoSize); |
| _singleton = InspectImpl( |
| context.outgoing.diagnosticsDir(), 'root.inspect', writer); |
| } |
| return _singleton; |
| } |
| |
| /// Returns a new [Inspect] object at <name>.inspect |
| /// If called multiple times with the same name within a process, a unique |
| /// number will be appended, though any existing file will be overwritten. |
| /// |
| /// Example: |
| /// Inspect.named('test'); |
| /// Inspect.named('test'); |
| /// Results in "test.inspect" and "test_2.inspect" |
| factory Inspect.named(String name) { |
| var context = StartupContext.fromStartupInfo(); |
| var writer = VmoWriter.withSize(vmoSize); |
| var fileName = _nextInstanceWithName(name); |
| return InspectImpl(context.outgoing.diagnosticsDir(), fileName, writer); |
| } |
| |
| /// 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. |
| static void onDemand(String name, OnDemandRootFn rootNodeCallback) { |
| var context = StartupContext.fromStartupInfo(); |
| var directory = context.outgoing.diagnosticsDir(); |
| var fileName = _nextInstanceWithName(name); |
| var pseudoVmoNode = PseudoVmoFile.readOnly(() { |
| var writer = VmoWriter.withSize(vmoSize); |
| rootNodeCallback(RootNode(writer)); |
| return writer.vmo; |
| }); |
| |
| directory.addNode(fileName, pseudoVmoNode); |
| } |
| |
| static String _nextInstanceWithName(String name) { |
| nameToInstanceCount ??= <String, int>{}; |
| if (nameToInstanceCount.containsKey(name)) { |
| int val = nameToInstanceCount[name] + 1; |
| nameToInstanceCount[name] = val; |
| return '${name}_$val.inspect'; |
| } else { |
| nameToInstanceCount[name] = 1; |
| return '$name.inspect'; |
| } |
| } |
| |
| /// 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; |
| } |