blob: 2688ec923c63ae0bf6f6cdebbc267be7882670a0 [file] [edit]
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. 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:io' as io;
import 'dart:isolate';
import 'package:dart_data_home/dart_data_home.dart';
import 'package:path/path.dart' as p;
final String? _controlSocketsDirectory = () {
try {
final dir = getDartDataHome('perf');
// Ensure that directory exists.
io.Directory(dir).createSync(recursive: true);
return dir;
} catch (_) {
// Ignore any sort of exceptions.
return null;
}
}();
List<({int pid, String socketPath})> getAllControlSockets() {
if (_controlSocketsDirectory == null) {
return const [];
}
try {
// Caveat: on Windows Unix Domain Sockets are represented as link rather
// than file objects, so filtering this list to io.File objects will miss
// socket objects entirely.
final allPidFiles = io.Directory(_controlSocketsDirectory!)
.listSync()
.map((e) => e.path)
.where((path) => io.FileSystemEntity.typeSync(path) == .unixDomainSock);
final result = [
for (var path in allPidFiles)
if (int.tryParse(p.basenameWithoutExtension(path)) case final pid?)
(pid: pid, socketPath: path),
];
return result;
} catch (_) {
// Ignore
return [];
}
}
String? controlSocketPathForPid(int pid, {String? controlSocketDirectory}) {
final dirPath = controlSocketDirectory ?? _controlSocketsDirectory;
if (dirPath == null) {
return null;
}
return p.join(dirPath, '$pid');
}
final String? controlSocketPath = () {
return controlSocketPathForPid(io.pid);
}();
final String? recorderSocketPath = () {
final dirPath = _controlSocketsDirectory;
if (dirPath == null) {
return null;
}
return p.join(dirPath, 'rec');
}();
abstract class UnixDomainSocket {
static Future<io.Socket> connect(String path) => io.Socket.connect(
io.InternetAddress(path, type: io.InternetAddressType.unix),
0,
);
static Future<io.ServerSocket> bind(String path) async {
if (io.FileSystemEntity.typeSync(path) !=
io.FileSystemEntityType.notFound) {
io.File(path).deleteSync();
}
return await io.ServerSocket.bind(
io.InternetAddress(path, type: io.InternetAddressType.unix),
0,
);
}
}
Future<void> waitForUserToQuit({bool waitForQKeyPress = false}) async {
if (waitForQKeyPress) {
await Isolate.run(() {
print('Press Q to exit');
try {
io.stdin.echoMode = false;
io.stdin.lineMode = false;
} catch (_) {
// Ignore any issues.
}
int byte;
while ((byte = io.stdin.readByteSync()) != -1) {
if (byte == 'Q'.codeUnitAt(0) || byte == 'q'.codeUnitAt(0)) {
break;
}
}
});
} else {
final signalFired = io.ProcessSignal.sigint.watch().first;
print('Press Ctrl-C to exit');
await signalFired;
}
}