Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(81)

Side by Side Diff: pkg/analysis_server/lib/src/plugin/plugin_manager.dart

Issue 2893803004: Capture the request time for performance data and support forced shutdown (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | pkg/analysis_server/test/src/plugin/plugin_manager_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import 'dart:async'; 5 import 'dart:async';
6 import 'dart:collection'; 6 import 'dart:collection';
7 import 'dart:io' show Platform; 7 import 'dart:io' show Platform;
8 8
9 import 'package:analysis_server/src/plugin/notification_manager.dart'; 9 import 'package:analysis_server/src/plugin/notification_manager.dart';
10 import 'package:analyzer/context/context_root.dart' as analyzer; 10 import 'package:analyzer/context/context_root.dart' as analyzer;
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
228 currentSession.sendRequest(params); 228 currentSession.sendRequest(params);
229 } 229 }
230 } 230 }
231 } 231 }
232 232
233 /** 233 /**
234 * An object used to manage the currently running plugins. 234 * An object used to manage the currently running plugins.
235 */ 235 */
236 class PluginManager { 236 class PluginManager {
237 /** 237 /**
238 * A table, keyed by both a plugin and a request method, to a list of the
239 * times that it took the plugin to return a response to requests with the
240 * method.
241 */
242 static Map<PluginInfo, Map<String, List<int>>> pluginResponseTimes =
243 <PluginInfo, Map<String, List<int>>>{};
244
245 /**
238 * The resource provider used to access the file system. 246 * The resource provider used to access the file system.
239 */ 247 */
240 final ResourceProvider resourceProvider; 248 final ResourceProvider resourceProvider;
241 249
242 /** 250 /**
243 * The absolute path of the directory containing the on-disk byte store, or 251 * The absolute path of the directory containing the on-disk byte store, or
244 * `null` if there is no on-disk store. 252 * `null` if there is no on-disk store.
245 */ 253 */
246 final String byteStorePath; 254 final String byteStorePath;
247 255
(...skipping 312 matching lines...) Expand 10 before | Expand all | Expand 10 after
560 return computePaths(executionFolder, runPub: true); 568 return computePaths(executionFolder, runPub: true);
561 } 569 }
562 570
563 /** 571 /**
564 * Return a hex-encoded MD5 signature of the given file [path]. 572 * Return a hex-encoded MD5 signature of the given file [path].
565 */ 573 */
566 String _uniqueDirectoryName(String path) { 574 String _uniqueDirectoryName(String path) {
567 List<int> bytes = md5.convert(path.codeUnits).bytes; 575 List<int> bytes = md5.convert(path.codeUnits).bytes;
568 return hex.encode(bytes); 576 return hex.encode(bytes);
569 } 577 }
578
579 /**
580 * Record the fact that the given [plugin] responded to a request with the
581 * given [method] in the given [time].
582 */
583 static void recordResponseTime(PluginInfo plugin, String method, int time) {
584 pluginResponseTimes
585 .putIfAbsent(plugin, () => <String, List<int>>{})
586 .putIfAbsent(method, () => <int>[])
587 .add(time);
588 }
570 } 589 }
571 590
572 /** 591 /**
573 * Information about the execution a single plugin. 592 * Information about the execution a single plugin.
574 */ 593 */
575 @visibleForTesting 594 @visibleForTesting
576 class PluginSession { 595 class PluginSession {
577 /** 596 /**
597 * The maximum number of milliseconds that server should wait for a response
598 * from a plugin before deciding that the plugin is hung.
599 */
600 static const Duration MAXIMUM_RESPONSE_TIME = const Duration(minutes: 2);
601
602 /**
603 * The length of time to wait after sending a 'plugin.shutdown' request before
604 * a failure to terminate will cause the isolate to be killed.
605 */
606 static const Duration WAIT_FOR_SHUTDOWN_DURATION =
607 const Duration(seconds: 10);
608
609 /**
578 * The information about the plugin being executed. 610 * The information about the plugin being executed.
579 */ 611 */
580 final PluginInfo info; 612 final PluginInfo info;
581 613
582 /** 614 /**
583 * The completer used to signal when the plugin has stopped. 615 * The completer used to signal when the plugin has stopped.
584 */ 616 */
585 Completer<Null> pluginStoppedCompleter = new Completer<Null>(); 617 Completer<Null> pluginStoppedCompleter = new Completer<Null>();
586 618
587 /** 619 /**
588 * The channel used to communicate with the plugin. 620 * The channel used to communicate with the plugin.
589 */ 621 */
590 ServerCommunicationChannel channel; 622 ServerCommunicationChannel channel;
591 623
592 /** 624 /**
593 * The index of the next request to be sent to the plugin. 625 * The index of the next request to be sent to the plugin.
594 */ 626 */
595 int requestId = 0; 627 int requestId = 0;
596 628
597 /** 629 /**
598 * A table mapping the id's of requests to the functions used to handle the 630 * A table mapping the id's of requests to the functions used to handle the
599 * response to those requests. 631 * response to those requests.
600 */ 632 */
601 Map<String, Completer<Response>> pendingRequests = 633 Map<String, _PendingRequest> pendingRequests = <String, _PendingRequest>{};
602 <String, Completer<Response>>{};
603 634
604 /** 635 /**
605 * A boolean indicating whether the plugin is compatible with the version of 636 * A boolean indicating whether the plugin is compatible with the version of
606 * the plugin API being used by this server. 637 * the plugin API being used by this server.
607 */ 638 */
608 bool isCompatible = true; 639 bool isCompatible = true;
609 640
610 /** 641 /**
611 * The contact information to include when reporting problems related to the 642 * The contact information to include when reporting problems related to the
612 * plugin. 643 * plugin.
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
682 // print(' $message'); 713 // print(' $message');
683 // print(' ${new StackTrace.fromString(stackTrace)}'); 714 // print(' ${new StackTrace.fromString(stackTrace)}');
684 // pluginStoppedCompleter.completeError(message, new StackTrace.fromString(st ackTrace)); 715 // pluginStoppedCompleter.completeError(message, new StackTrace.fromString(st ackTrace));
685 } 716 }
686 717
687 /** 718 /**
688 * Handle a [response] from the plugin by completing the future that was 719 * Handle a [response] from the plugin by completing the future that was
689 * created when the request was sent. 720 * created when the request was sent.
690 */ 721 */
691 void handleResponse(Response response) { 722 void handleResponse(Response response) {
692 Completer<Response> completer = pendingRequests.remove(response.id); 723 _PendingRequest requestData = pendingRequests.remove(response.id);
724 int responseTime = new DateTime.now().millisecondsSinceEpoch;
725 int duration = responseTime - requestData.requestTime;
726 PluginManager.recordResponseTime(info, requestData.method, duration);
727 Completer<Response> completer = requestData.completer;
693 if (completer != null) { 728 if (completer != null) {
694 completer.complete(response); 729 completer.complete(response);
695 } 730 }
696 } 731 }
697 732
698 /** 733 /**
734 * Return `true` if there are any requests that have not been responded to
735 * within the maximum allowed amount of time.
736 */
737 bool isNonResponsive() {
738 // TODO(brianwilkerson) Figure out when to invoke this method in order to
739 // identify non-responsive plugins and kill them.
740 int cutOffTime = new DateTime.now().millisecondsSinceEpoch -
741 MAXIMUM_RESPONSE_TIME.inMilliseconds;
742 for (var requestData in pendingRequests.values) {
743 if (requestData.requestTime < cutOffTime) {
744 return true;
745 }
746 }
747 return false;
748 }
749
750 /**
699 * Send a request, based on the given [parameters]. Return a future that will 751 * Send a request, based on the given [parameters]. Return a future that will
700 * complete when a response is received. 752 * complete when a response is received.
701 */ 753 */
702 Future<Response> sendRequest(RequestParams parameters) { 754 Future<Response> sendRequest(RequestParams parameters) {
703 if (channel == null) { 755 if (channel == null) {
704 throw new StateError( 756 throw new StateError(
705 'Cannot send a request to a plugin that has stopped.'); 757 'Cannot send a request to a plugin that has stopped.');
706 } 758 }
707 String id = nextRequestId; 759 String id = nextRequestId;
708 Completer<Response> completer = new Completer(); 760 Completer<Response> completer = new Completer();
709 pendingRequests[id] = completer; 761 int requestTime = new DateTime.now().millisecondsSinceEpoch;
710 channel.sendRequest(parameters.toRequest(id)); 762 Request request = parameters.toRequest(id);
763 pendingRequests[id] =
764 new _PendingRequest(request.method, requestTime, completer);
765 channel.sendRequest(request);
711 return completer.future; 766 return completer.future;
712 } 767 }
713 768
714 /** 769 /**
715 * Start a new isolate that is running this plugin. The plugin will be sent 770 * Start a new isolate that is running this plugin. The plugin will be sent
716 * the given [byteStorePath]. Return `true` if the plugin is compatible and 771 * the given [byteStorePath]. Return `true` if the plugin is compatible and
717 * running. 772 * running.
718 */ 773 */
719 Future<bool> start(String byteStorePath, String sdkPath) async { 774 Future<bool> start(String byteStorePath, String sdkPath) async {
720 if (channel != null) { 775 if (channel != null) {
(...skipping 29 matching lines...) Expand all
750 return true; 805 return true;
751 } 806 }
752 807
753 /** 808 /**
754 * Request that the plugin shutdown. 809 * Request that the plugin shutdown.
755 */ 810 */
756 Future<Null> stop() { 811 Future<Null> stop() {
757 if (channel == null) { 812 if (channel == null) {
758 throw new StateError('Cannot stop a plugin that is not running.'); 813 throw new StateError('Cannot stop a plugin that is not running.');
759 } 814 }
760 // TODO(brianwilkerson) Ensure that the isolate is killed if it does not
761 // terminate normally.
762 sendRequest(new PluginShutdownParams()); 815 sendRequest(new PluginShutdownParams());
816 new Future.delayed(WAIT_FOR_SHUTDOWN_DURATION, () {
817 if (channel != null) {
818 channel.kill();
819 channel = null;
820 }
821 });
763 return pluginStoppedCompleter.future; 822 return pluginStoppedCompleter.future;
764 } 823 }
765 } 824 }
825
826 /**
827 * Information about a request that has been sent but for which a response has
828 * not yet been received.
829 */
830 class _PendingRequest {
831 /**
832 * The method of the request.
833 */
834 final String method;
835
836 /**
837 * The time at which the request was sent to the plugin.
838 */
839 final int requestTime;
scheglov 2017/05/18 21:36:16 Or you could use new Stopwatch()..start() instead.
Brian Wilkerson 2017/05/18 21:51:39 I hadn't considered that, but now that I think abo
scheglov 2017/05/18 21:55:55 It's fine with my any way, just thought about othe
840
841 /**
842 * The completer that will be used to complete the future when the response is
843 * received from the plugin.
844 */
845 final Completer<Response> completer;
846
847 /**
848 * Initialize a pending request.
849 */
850 _PendingRequest(this.method, this.requestTime, this.completer);
851 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analysis_server/test/src/plugin/plugin_manager_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698