// Copyright (c) 2016, 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:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; import 'package:analysis_server/protocol/protocol_generated.dart'; import 'package:test/test.dart'; import '../../test/integration/support/integration_tests.dart'; void printMemoryResults(String id, String description, List sizes) { int minMemory = sizes.fold(sizes.first, min); int maxMemory = sizes.fold(sizes.first, max); String now = new DateTime.now().toUtc().toIso8601String(); print('$now ========== $id'); print('memory: $sizes'); print('min_memory: $minMemory'); print('max_memory: $maxMemory'); print(description.trim()); print('--------------------'); print(''); print(''); } /** * Base class for analysis server memory usage tests. */ class AnalysisServerMemoryUsageTest extends AbstractAnalysisServerIntegrationTest { static const int vmServicePort = 12345; int getMemoryUsage() { String vmService = 'http://localhost:$vmServicePort/_getAllocationProfile\?isolateId=isolates/root\&gc=full'; ProcessResult result; if (Platform.isWindows) { result = _run( 'powershell', ['-Command', '(curl "$vmService").Content']); } else { result = _run('curl', [vmService]); } Map jsonData = json.decode(result.stdout); Map heaps = jsonData['result']['heaps']; int newSpace = heaps['new']['used']; int oldSpace = heaps['old']['used']; return newSpace + oldSpace; } /** * Send the server an 'analysis.setAnalysisRoots' command directing it to * analyze [sourceDirectory]. */ Future setAnalysisRoot() => sendAnalysisSetAnalysisRoots([sourceDirectory.path], []); /** * The server is automatically started before every test. */ @override Future setUp({bool useCFE: false}) { onAnalysisErrors.listen((AnalysisErrorsParams params) { currentAnalysisErrors[params.file] = params.errors; }); onServerError.listen((ServerErrorParams params) { // A server error should never happen during an integration test. fail('${params.message}\n${params.stackTrace}'); }); Completer serverConnected = new Completer(); onServerConnected.listen((_) { outOfTestExpect(serverConnected.isCompleted, isFalse); serverConnected.complete(); }); return startServer(servicesPort: vmServicePort, cfe: useCFE).then((_) { server.listenToOutput(dispatchNotification); server.exitCode.then((_) { skipShutdown = true; }); return serverConnected.future; }); } /** * After every test, the server is stopped. */ Future shutdown() async => await shutdownIfNeeded(); /** * Enable [ServerService.STATUS] notifications so that [analysisFinished] * can be used. */ Future subscribeToStatusNotifications() async { await sendServerSetSubscriptions([ServerService.STATUS]); } /** * Synchronously run the given [executable] with the given [arguments]. Return * the result of running the process. */ ProcessResult _run(String executable, List arguments) { return Process.runSync(executable, arguments, stderrEncoding: utf8, stdoutEncoding: utf8); } /** * 1. Start Analysis Server. * 2. Set the analysis [roots]. * 3. Wait for analysis to complete. * 4. Record the heap size after analysis is finished. * 5. Shutdown. * 6. Go to (1). */ static Future> start_waitInitialAnalysis_shutdown( {List roots, int numOfRepeats}) async { outOfTestExpect(roots, isNotNull, reason: 'roots'); outOfTestExpect(numOfRepeats, isNotNull, reason: 'numOfRepeats'); // Repeat. List sizes = []; for (int i = 0; i < numOfRepeats; i++) { AnalysisServerMemoryUsageTest test = new AnalysisServerMemoryUsageTest(); // Initialize Analysis Server. await test.setUp(); await test.subscribeToStatusNotifications(); // Set roots and analyze. await test.sendAnalysisSetAnalysisRoots(roots, []); await test.analysisFinished; sizes.add(test.getMemoryUsage()); // Stop the server. await test.shutdown(); } return sizes; } }