Skip to content

Commit b8b41cb

Browse files
bwilkersonCommit Queue
authored andcommitted
Add support for logging communications from plugins
I believe that this is the final piece to having all of the information needed by the replay tool. Change-Id: I367d628302c06397706172ba65c0de64972a2a34 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/459543 Reviewed-by: Keerti Parthasarathy <keertip@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
1 parent 160fd0a commit b8b41cb

File tree

10 files changed

+277
-2
lines changed

10 files changed

+277
-2
lines changed

pkg/analysis_server/lib/src/analysis_server.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ abstract class AnalysisServer {
351351
sdkManager.defaultSdkDirectory,
352352
notificationManager,
353353
instrumentationService,
354+
sessionLogger,
354355
);
355356
var pluginWatcher = PluginWatcher(resourceProvider, pluginManager);
356357

pkg/analysis_server/lib/src/plugin/plugin_isolate.dart

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ import 'dart:io' show Platform;
1111

1212
import 'package:analysis_server/src/plugin/notification_manager.dart';
1313
import 'package:analysis_server/src/plugin/plugin_manager.dart';
14+
import 'package:analysis_server/src/plugin/server_isolate_channel.dart';
15+
import 'package:analysis_server/src/session_logger/session_logger.dart';
1416
import 'package:analyzer/dart/analysis/context_root.dart' as analyzer;
1517
import 'package:analyzer/exception/exception.dart';
1618
import 'package:analyzer/instrumentation/instrumentation.dart';
1719
import 'package:analyzer_plugin/channel/channel.dart';
1820
import 'package:analyzer_plugin/protocol/protocol.dart';
1921
import 'package:analyzer_plugin/protocol/protocol_constants.dart';
2022
import 'package:analyzer_plugin/protocol/protocol_generated.dart';
21-
import 'package:analyzer_plugin/src/channel/isolate_channel.dart';
2223
import 'package:analyzer_plugin/src/protocol/protocol_internal.dart';
2324
import 'package:meta/meta.dart';
2425

@@ -43,6 +44,9 @@ class PluginIsolate {
4344
/// The instrumentation service that is being used by the analysis server.
4445
final InstrumentationService _instrumentationService;
4546

47+
/// The session logger that is to be used by the isolate.
48+
final SessionLogger sessionLogger;
49+
4650
/// The context roots that are currently using the results produced by the
4751
/// plugin.
4852
Set<analyzer.ContextRoot> contextRoots = HashSet<analyzer.ContextRoot>();
@@ -60,7 +64,8 @@ class PluginIsolate {
6064
this.executionPath,
6165
this.packagesPath,
6266
this._notificationManager,
63-
this._instrumentationService, {
67+
this._instrumentationService,
68+
this.sessionLogger, {
6469
required this.isLegacy,
6570
});
6671

@@ -207,6 +212,7 @@ class PluginIsolate {
207212
Uri.file(executionPath!, windows: Platform.isWindows),
208213
Uri.file(packagesPath!, windows: Platform.isWindows),
209214
_instrumentationService,
215+
sessionLogger,
210216
);
211217
}
212218

pkg/analysis_server/lib/src/plugin/plugin_manager.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'dart:io' show Platform, Process, ProcessResult;
1313
import 'package:analysis_server/src/analytics/percentile_calculator.dart';
1414
import 'package:analysis_server/src/plugin/notification_manager.dart';
1515
import 'package:analysis_server/src/plugin/plugin_isolate.dart';
16+
import 'package:analysis_server/src/session_logger/session_logger.dart';
1617
import 'package:analysis_server/src/utilities/sdk.dart';
1718
import 'package:analyzer/dart/analysis/context_root.dart' as analyzer;
1819
import 'package:analyzer/exception/exception.dart';
@@ -87,6 +88,9 @@ class PluginManager {
8788
/// The instrumentation service that is being used by the analysis server.
8889
final InstrumentationService instrumentationService;
8990

91+
/// The session logger that is being used by the analysis server.
92+
final SessionLogger sessionLogger;
93+
9094
/// A table mapping the paths of plugins to information about those plugins.
9195
final Map<String, PluginIsolate> _pluginMap = <String, PluginIsolate>{};
9296

@@ -125,6 +129,7 @@ class PluginManager {
125129
this._sdkPath,
126130
this._notificationManager,
127131
this.instrumentationService,
132+
this.sessionLogger,
128133
);
129134

130135
/// All of the legacy plugins that are currently known.
@@ -167,6 +172,7 @@ class PluginManager {
167172
null,
168173
_notificationManager,
169174
instrumentationService,
175+
sessionLogger,
170176
isLegacy: isLegacyPlugin,
171177
);
172178
pluginIsolate.reportException(CaughtException(exception, stackTrace));
@@ -179,6 +185,7 @@ class PluginManager {
179185
pluginFiles.packageConfig.path,
180186
_notificationManager,
181187
instrumentationService,
188+
sessionLogger,
182189
isLegacy: isLegacyPlugin,
183190
);
184191
_pluginMap[path] = pluginIsolate;
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
import 'dart:convert';
7+
import 'dart:isolate';
8+
9+
import 'package:analysis_server/src/session_logger/process_id.dart';
10+
import 'package:analysis_server/src/session_logger/session_logger.dart';
11+
import 'package:analyzer/instrumentation/instrumentation.dart';
12+
import 'package:analyzer_plugin/channel/channel.dart';
13+
import 'package:analyzer_plugin/protocol/protocol.dart';
14+
import 'package:analyzer_plugin/protocol/protocol_generated.dart';
15+
16+
/// The type of the function used to run a built-in plugin in an isolate.
17+
typedef EntryPoint = void Function(SendPort sendPort);
18+
19+
/// A communication channel appropriate for built-in plugins.
20+
class BuiltInServerIsolateChannel extends ServerIsolateChannel {
21+
/// The entry point
22+
final EntryPoint entryPoint;
23+
24+
@override
25+
final String pluginId;
26+
27+
/// Initialize a newly created channel to communicate with an isolate running
28+
/// the given [entryPoint].
29+
BuiltInServerIsolateChannel(
30+
this.entryPoint,
31+
this.pluginId,
32+
InstrumentationService instrumentationService,
33+
SessionLogger sessionLogger,
34+
) : super._(instrumentationService, sessionLogger);
35+
36+
@override
37+
Future<Isolate> _spawnIsolate() {
38+
return Isolate.spawn(
39+
(message) => entryPoint(message as SendPort),
40+
_receivePort?.sendPort,
41+
onError: _errorPort?.sendPort,
42+
onExit: _exitPort?.sendPort,
43+
);
44+
}
45+
}
46+
47+
/// A communication channel appropriate for discovered plugins.
48+
class DiscoveredServerIsolateChannel extends ServerIsolateChannel {
49+
/// The URI for the Dart file that will be run in the isolate that this
50+
/// channel communicates with.
51+
final Uri pluginUri;
52+
53+
/// The URI for the '.packages' file that will control how 'package:' URIs are
54+
/// resolved.
55+
final Uri packagesUri;
56+
57+
/// Initialize a newly created channel to communicate with an isolate running
58+
/// the code at the given [uri].
59+
DiscoveredServerIsolateChannel(
60+
this.pluginUri,
61+
this.packagesUri,
62+
InstrumentationService instrumentationService,
63+
SessionLogger sessionLogger,
64+
) : super._(instrumentationService, sessionLogger);
65+
66+
@override
67+
String get pluginId => pluginUri.toString();
68+
69+
@override
70+
Future<Isolate> _spawnIsolate() {
71+
return Isolate.spawnUri(
72+
pluginUri,
73+
<String>[],
74+
_receivePort?.sendPort,
75+
onError: _errorPort?.sendPort,
76+
onExit: _exitPort?.sendPort,
77+
packageConfig: packagesUri,
78+
);
79+
}
80+
}
81+
82+
/// A communication channel that allows an analysis server to send [Request]s
83+
/// to, and to receive both [Response]s and [Notification]s from, a plugin.
84+
abstract class ServerIsolateChannel implements ServerCommunicationChannel {
85+
/// The instrumentation service that is being used by the analysis server.
86+
final InstrumentationService instrumentationService;
87+
88+
/// The session logger that is to be used by this channel.
89+
final SessionLogger sessionLogger;
90+
91+
/// The isolate in which the plugin is running, or `null` if the plugin has
92+
/// not yet been started by invoking [listen].
93+
Isolate? _isolate;
94+
95+
/// The port used to send requests to the plugin, or `null` if the plugin has
96+
/// not yet been started by invoking [listen].
97+
SendPort? _sendPort;
98+
99+
/// The port used to receive responses and notifications from the plugin.
100+
ReceivePort? _receivePort;
101+
102+
/// The port used to receive unhandled exceptions thrown in the plugin.
103+
ReceivePort? _errorPort;
104+
105+
/// The port used to receive notification when the plugin isolate has exited.
106+
ReceivePort? _exitPort;
107+
108+
/// Return a communication channel appropriate for communicating with a
109+
/// built-in plugin.
110+
// TODO(brianwilkerson): This isn't currently referenced. We should decide
111+
// whether there's any value to supporting built-in plugins and remove this
112+
// if we decide there isn't.
113+
factory ServerIsolateChannel.builtIn(
114+
EntryPoint entryPoint,
115+
String pluginId,
116+
InstrumentationService instrumentationService,
117+
SessionLogger sessionLogger,
118+
) = BuiltInServerIsolateChannel;
119+
120+
/// Return a communication channel appropriate for communicating with a
121+
/// discovered plugin.
122+
factory ServerIsolateChannel.discovered(
123+
Uri pluginUri,
124+
Uri packagesUri,
125+
InstrumentationService instrumentationService,
126+
SessionLogger sessionLogger,
127+
) = DiscoveredServerIsolateChannel;
128+
129+
/// Initialize a newly created channel.
130+
ServerIsolateChannel._(this.instrumentationService, this.sessionLogger);
131+
132+
/// Return the id of the plugin running in the isolate, used to identify the
133+
/// plugin to the instrumentation service.
134+
String get pluginId;
135+
136+
@override
137+
void close() {
138+
_receivePort?.close();
139+
_errorPort?.close();
140+
_exitPort?.close();
141+
_isolate = null;
142+
}
143+
144+
@override
145+
void kill() {
146+
_isolate?.kill(priority: Isolate.immediate);
147+
}
148+
149+
@override
150+
Future<void> listen(
151+
void Function(Response response) onResponse,
152+
void Function(Notification notification) onNotification, {
153+
void Function(dynamic error)? onError,
154+
void Function()? onDone,
155+
}) async {
156+
if (_isolate != null) {
157+
throw StateError('Cannot listen to the same channel more than once.');
158+
}
159+
160+
var receivePort = ReceivePort();
161+
_receivePort = receivePort;
162+
163+
if (onError != null) {
164+
var errorPort = ReceivePort();
165+
_errorPort = errorPort;
166+
errorPort.listen((error) {
167+
onError(error);
168+
});
169+
}
170+
171+
if (onDone != null) {
172+
var exitPort = ReceivePort();
173+
_exitPort = exitPort;
174+
exitPort.listen((_) {
175+
onDone();
176+
});
177+
}
178+
179+
try {
180+
_isolate = await _spawnIsolate();
181+
} catch (exception, stackTrace) {
182+
instrumentationService.logPluginError(
183+
PluginData(pluginId, null, null),
184+
RequestErrorCode.PLUGIN_ERROR.toString(),
185+
exception.toString(),
186+
stackTrace.toString(),
187+
);
188+
if (onError != null) {
189+
onError([exception.toString(), stackTrace.toString()]);
190+
}
191+
if (onDone != null) {
192+
onDone();
193+
}
194+
close();
195+
return;
196+
}
197+
198+
var channelReady = Completer<void>();
199+
receivePort.listen((dynamic input) {
200+
if (input is SendPort) {
201+
_sendPort = input;
202+
channelReady.complete(null);
203+
} else if (input is Map<String, Object?>) {
204+
if (input.containsKey('id')) {
205+
var encodedInput = json.encode(input);
206+
instrumentationService.logPluginResponse(pluginId, encodedInput);
207+
sessionLogger.logMessage(
208+
from: ProcessId.plugin,
209+
to: ProcessId.server,
210+
message: input,
211+
);
212+
onResponse(Response.fromJson(input));
213+
} else if (input.containsKey('event')) {
214+
var encodedInput = json.encode(input);
215+
instrumentationService.logPluginNotification(pluginId, encodedInput);
216+
sessionLogger.logMessage(
217+
from: ProcessId.plugin,
218+
to: ProcessId.server,
219+
message: input,
220+
);
221+
onNotification(Notification.fromJson(input));
222+
}
223+
}
224+
});
225+
226+
return channelReady.future;
227+
}
228+
229+
@override
230+
void sendRequest(Request request) {
231+
var sendPort = _sendPort;
232+
if (sendPort != null) {
233+
var jsonData = request.toJson();
234+
var encodedRequest = json.encode(jsonData);
235+
instrumentationService.logPluginRequest(pluginId, encodedRequest);
236+
sessionLogger.logMessage(
237+
from: ProcessId.server,
238+
to: ProcessId.plugin,
239+
message: jsonData,
240+
);
241+
sendPort.send(jsonData);
242+
}
243+
}
244+
245+
/// Spawn the isolate in which the plugin is running.
246+
Future<Isolate> _spawnIsolate();
247+
}

pkg/analysis_server/test/edit/assists_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:analysis_server/protocol/protocol_generated.dart';
66
import 'package:analysis_server/src/plugin/plugin_isolate.dart';
77
import 'package:analysis_server/src/services/correction/assist_internal.dart';
8+
import 'package:analysis_server/src/session_logger/session_logger.dart';
89
import 'package:analyzer/instrumentation/service.dart';
910
import 'package:analyzer/src/test_utilities/platform.dart';
1011
import 'package:analyzer_plugin/protocol/protocol_common.dart';
@@ -58,6 +59,7 @@ class AssistsTest extends PubPackageAnalysisServerTest {
5859
'c',
5960
TestNotificationManager(),
6061
InstrumentationService.NULL_SERVICE,
62+
SessionLogger(),
6163
isLegacy: true,
6264
);
6365
var message = 'From a plugin';

pkg/analysis_server/test/edit/fixes_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'dart:async';
77
import 'package:analysis_server/protocol/protocol_generated.dart';
88
import 'package:analysis_server/src/plugin/plugin_isolate.dart';
99
import 'package:analysis_server/src/services/correction/fix_internal.dart';
10+
import 'package:analysis_server/src/session_logger/session_logger.dart';
1011
import 'package:analyzer/file_system/file_system.dart';
1112
import 'package:analyzer/instrumentation/service.dart';
1213
import 'package:analyzer/utilities/package_config_file_builder.dart';
@@ -101,6 +102,7 @@ void f() {
101102
'c',
102103
TestNotificationManager(),
103104
InstrumentationService.NULL_SERVICE,
105+
SessionLogger(),
104106
isLegacy: true,
105107
);
106108
var fixes = plugin.AnalysisErrorFixes(

pkg/analysis_server/test/lsp/server_abstract.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ abstract class AbstractLspAnalysisServerTest
112112
'c',
113113
server.notificationManager,
114114
server.instrumentationService,
115+
server.sessionLogger,
115116
isLegacy: true,
116117
);
117118
pluginManager.pluginIsolates.add(pluginIsolate);

pkg/analysis_server/test/src/analytics/analytics_manager_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:analysis_server/src/analytics/percentile_calculator.dart';
1111
import 'package:analysis_server/src/plugin/plugin_isolate.dart';
1212
import 'package:analysis_server/src/plugin/plugin_manager.dart';
1313
import 'package:analysis_server/src/protocol_server.dart';
14+
import 'package:analysis_server/src/session_logger/session_logger.dart';
1415
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
1516
import 'package:analyzer/file_system/file_system.dart';
1617
import 'package:analyzer/instrumentation/service.dart';
@@ -570,6 +571,7 @@ class AnalyticsManagerTest with ResourceProviderMixin {
570571
'/some/packages/path',
571572
TestNotificationManager(),
572573
InstrumentationService.NULL_SERVICE,
574+
SessionLogger(),
573575
isLegacy: true,
574576
);
575577
}

0 commit comments

Comments
 (0)