guru_sdk/guru_ui/example/lib/console/vm_helper.dart

257 lines
6.6 KiB
Dart

import 'dart:ui';
import 'dart:async';
import 'dart:developer';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:vm_service/utils.dart';
import 'package:vm_service/vm_service.dart';
import 'package:vm_service/vm_service_io.dart';
class VMHelper with ChangeNotifier {
factory VMHelper() => _vMHelper;
VMHelper._() {
assert(() {
_startConnect().whenComplete(() {
_timer = Timer.periodic(const Duration(seconds: 2), (Timer timer) {
VMHelper()._updateMemoryUsage().whenComplete(() {
notifyListeners();
});
});
});
return true;
}());
}
static final VMHelper _vMHelper = VMHelper._();
late bool connected;
VmService? serviceClient;
VM? vm;
late MemoryUsage mainMemoryUsage;
late Timer _timer;
List<MyMemoryUsage> mainHistoryMemoryInfo = <MyMemoryUsage>[];
IsolateRef? get main => vm!.isolates!
.firstWhereOrNull((IsolateRef element) => element.name == 'main');
int get count => _count;
int _count = 0;
Future<void> _startConnect() async {
try {
final ServiceProtocolInfo info = await Service.getInfo();
if (info.serverUri == null) {
print('Service protocol url is null, start vm service fail.');
return;
}
final Uri uri = convertToWebSocketUrl(
serviceProtocolUrl: info.serverUri!,
);
serviceClient = await vmServiceConnectUri(
uri.toString(),
log: StdoutLog(),
);
print('Socket connected in service $info');
connected = true;
vm = await serviceClient!.getVM();
await _updateMemoryUsage();
} catch (e) {
print(
'Socket connect failed due to $e. '
"Make sure you're not using simulators.",
);
}
}
Future<void> _updateMemoryUsage() async {
if (vm != null && connected) {
final MemoryUsage memoryUsage = await serviceClient!.getMemoryUsage(
main!.id!,
);
mainMemoryUsage = memoryUsage;
final MyMemoryUsage latest = MyMemoryUsage.copyFromMemoryUsage(
memoryUsage,
);
mainHistoryMemoryInfo.add(latest);
mainHistoryMemoryInfo.removeWhere(
(MyMemoryUsage element) => element.dataTime.isBefore(
latest.dataTime.subtract(const Duration(minutes: 1)),
),
);
}
}
void clear() {
_count = 0;
mainHistoryMemoryInfo.clear();
}
@override
void dispose() {
_timer.cancel();
super.dispose();
}
void forceGC() {
if (kDebugMode)
serviceClient?.getAllocationProfile(main?.id ?? '', gc: true);
}
}
class MyMemoryUsage {
MyMemoryUsage({
required int externalUsage,
required int heapCapacity,
required int heapUsage,
}) : dataTime = DateTime.now(),
externalUsage = externalUsage / 1024 / 1024,
heapCapacity = heapCapacity / 1024 / 1024,
heapUsage = heapUsage / 1024 / 1024;
final DateTime dataTime;
/// The amount of non-Dart memory that is retained by Dart objects. For
/// example, memory associated with Dart objects through APIs such as
/// Dart_NewFinalizableHandle, Dart_NewWeakPersistentHandle and
/// Dart_NewExternalTypedData. This usage is only as accurate as the values
/// supplied to these APIs from the VM embedder or native extensions. This
/// external memory applies GC pressure, but is separate from heapUsage and
/// heapCapacity.
final double externalUsage;
/// The total capacity of the heap in bytes. This is the amount of memory used
/// by the Dart heap from the perspective of the operating system.
final double heapCapacity;
/// The current heap memory usage in bytes. Heap usage is always less than or
/// equal to the heap capacity.
final double heapUsage;
static MyMemoryUsage copyFromMemoryUsage(MemoryUsage memoryUsage) =>
MyMemoryUsage(
externalUsage: memoryUsage.externalUsage!,
heapCapacity: memoryUsage.heapCapacity!,
heapUsage: memoryUsage.heapUsage!,
);
double toDouble(double d) {
return double.parse(d.toStringAsFixed(2));
}
}
class StdoutLog extends Log {
@override
void warning(String message) => print(message);
@override
void severe(String message) => print(message);
}
class ByteUtil {
const ByteUtil._();
static String toByteString(int bytes) {
if (bytes <= 1024) {
return '${bytes}B';
} else if (bytes <= 1024 * 1024) {
return '${(bytes / (1024)).toStringAsFixed(2)}K';
} else {
return '${(bytes / (1024 * 1024)).toStringAsFixed(2)}M';
}
}
}
class MemoryUsageView extends StatefulWidget {
@override
_MemoryUsageViewState createState() => _MemoryUsageViewState();
}
class _MemoryUsageViewState extends State<MemoryUsageView> {
@override
void initState() {
super.initState();
VMHelper().addListener(_updateMemoryUsage);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
}
void _updateMemoryUsage() {
if (mounted) {
setState(() {});
}
}
@override
void dispose() {
VMHelper().removeListener(_updateMemoryUsage);
super.dispose();
}
@override
Widget build(BuildContext context) {
if (VMHelper().serviceClient == null) {
return Container();
}
final MemoryUsage main = VMHelper().mainMemoryUsage;
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
const Text('Used: '),
const SizedBox(
height: 5,
),
Text(
ByteUtil.toByteString(main.heapUsage!),
style: const TextStyle(
color: Colors.red,
height: 1.4,
fontSize: 16,
),
),
Divider(
color: Colors.white.withOpacity(0.1),
thickness: 1,
),
const Text('Capacity: '),
const SizedBox(
height: 5,
),
Text(
ByteUtil.toByteString(main.heapCapacity!),
style: const TextStyle(
color: Colors.blue,
height: 1.4,
fontSize: 16,
),
),
Divider(
color: Colors.white.withOpacity(0.1),
thickness: 1,
),
const Text('External: '),
const SizedBox(
height: 5,
),
Text(
ByteUtil.toByteString(main.externalUsage!),
style: const TextStyle(
color: Colors.green,
height: 1.4,
fontSize: 16,
),
),
],
);
}
}