blob: e21b0365476c625feb1577e3aea66a72625311b7 [file] [log] [blame]
// Copyright 2019 The Chromium 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 'package:meta/meta.dart';
import 'package:vm_service/vm_service.dart';
import '../config_specific/allowed_error.dart';
import '../globals.dart';
import '../profiler/cpu_profile_service.dart';
import '../vm_service_wrapper.dart';
import 'timeline_controller.dart';
import 'timeline_model.dart';
import 'timeline_protocol.dart';
/// Manages interactions between the Timeline and the VmService.
class TimelineService {
TimelineService(this.timelineController) {
_initListeners();
}
final TimelineController timelineController;
void _initListeners() async {
serviceManager.onConnectionAvailable.listen(_handleConnectionStart);
// Do not start the timeline for Dart web apps.
if (serviceManager.hasConnection &&
!await serviceManager.connectedApp.isDartWebApp) {
_handleConnectionStart(serviceManager.service);
}
serviceManager.onConnectionClosed.listen(_handleConnectionStop);
}
void _handleConnectionStart(VmServiceWrapper service) {
allowedError(serviceManager.service
.setFlag('profile_period', '$defaultSamplePeriod'));
serviceManager.service.onEvent('Timeline').listen((Event event) {
final List<dynamic> list = event.json['timelineEvents'];
final List<Map<String, dynamic>> events =
list.cast<Map<String, dynamic>>();
if (!offlineMode &&
!timelineController.manuallyPaused &&
!timelineController.paused) {
for (Map<String, dynamic> json in events) {
final TraceEvent e = TraceEvent(json);
timelineController.timelineProtocol?.processTraceEvent(e);
}
}
});
}
void _handleConnectionStop(dynamic event) {
// TODO(kenzie): investigate if we need to do anything here.
}
Future<void> startTimeline() async {
timelineController.timelineData = TimelineData();
await serviceManager.serviceAvailable.future;
await allowedError(serviceManager.service
.setVMTimelineFlags(<String>['GC', 'Dart', 'Embedder']));
await allowedError(serviceManager.service.clearVMTimeline());
final Timeline timeline = await serviceManager.service.getVMTimeline();
final List<dynamic> list = timeline.json['traceEvents'];
final List<Map<String, dynamic>> traceEvents =
list.cast<Map<String, dynamic>>();
final List<TraceEvent> events = traceEvents
.map((Map<String, dynamic> event) => TraceEvent(event))
.where((TraceEvent event) {
return event.name == 'thread_name';
}).toList();
// TODO(kenzie): Remove this logic once ui/gpu distinction changes are
// available in the engine.
int uiThreadId;
int gpuThreadId;
// Store the thread names for debugging purposes. If [uiThreadId] or
// [gpuThreadId] are null, we will print all the thread names we received
// to console.
final threadNames = [];
for (TraceEvent event in events) {
final name = event.args['name'];
threadNames.add(name);
// iOS: "io.flutter.1.ui (12652)", Android: "1.ui (12652)",
// Dream (g3): "io.flutter.ui (12652)"
if (name.contains('.ui')) {
uiThreadId = event.threadId;
}
// iOS: "io.flutter.1.gpu (12651)", Android: "1.gpu (12651)",
// Dream (g3): "io.flutter.gpu (12651)"
if (name.contains('.gpu')) {
gpuThreadId = event.threadId;
}
}
if (uiThreadId == null || gpuThreadId == null) {
timelineController.logNonFatalError(
'Could not find UI thread and / or GPU thread from names: '
'$threadNames');
}
timelineController.timelineProtocol = TimelineProtocol(
uiThreadId: uiThreadId,
gpuThreadId: gpuThreadId,
timelineController: timelineController,
);
}
Future<void> updateListeningState({
@required bool shouldBeRunning,
@required bool isRunning,
}) async {
await serviceManager.serviceAvailable.future;
if (shouldBeRunning && isRunning && !timelineController.hasStarted) {
await startTimeline();
} else if (shouldBeRunning && !isRunning) {
timelineController.resume();
await allowedError(serviceManager.service
.setVMTimelineFlags(<String>['GC', 'Dart', 'Embedder']));
} else if (!shouldBeRunning && isRunning) {
// TODO(devoncarew): turn off the events
timelineController.pause();
await allowedError(serviceManager.service.setVMTimelineFlags(<String>[]));
}
}
}