// Copyright (c) 2023, 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:developer'; import 'package:test/test.dart'; import 'package:vm_service/vm_service.dart'; import 'common/service_test_common.dart'; import 'common/test_helper.dart'; // AUTOGENERATED START // // Update these constants by running: // // dart pkg/vm_service/test/update_line_numbers.dart pkg/vm_service/test/rewind_test.dart // const LINE_0 = 39; const LINE_A = 41; const LINE_B = 46; const LINE_C = 49; const LINE_D = 53; // AUTOGENERATED END int global = 0; @pragma('vm:never-inline') int b3(int x) { int sum = 0; try { for (int i = 0; i < x; i++) { sum += x; } } catch (e) { print('caught $e'); } if (global >= 100) { debugger(); // LINE_0. } global = global + 1; // LINE_A. return sum; } @pragma('vm:prefer-inline') int b2(x) => b3(x); // LINE_B. @pragma('vm:prefer-inline') int b1(x) => b2(x); // LINE_C. void test() { while (true) { b1(10000); // LINE_D. } } final tests = [ hasStoppedAtBreakpoint, stoppedAtLine(LINE_0), stepOver, hasStoppedAtBreakpoint, stoppedAtLine(LINE_A), (VmService service, IsolateRef isolateRef) async { final isolateId = isolateRef.id!; // We are not able to rewind frame 0. bool caughtException = false; try { await service.resume( isolateId, step: StepOption.kRewind, frameIndex: 0, ); fail('Unreachable'); } on RPCError catch (e) { caughtException = true; expect(e.code, RPCErrorKind.kIsolateCannotBeResumed.code); expect(e.details, 'Frame must be in bounds [1..8]: saw 0'); } expect(caughtException, true); }, (VmService service, IsolateRef isolateRef) async { final isolateId = isolateRef.id!; // We are not able to rewind frame 13. bool caughtException = false; try { await service.resume( isolateId, step: StepOption.kRewind, frameIndex: 13, ); fail('Unreachable'); } on RPCError catch (e) { caughtException = true; expect(e.code, RPCErrorKind.kIsolateCannotBeResumed.code); expect(e.details, 'Frame must be in bounds [1..8]: saw 13'); } expect(caughtException, true); }, (VmService service, IsolateRef isolateRef) async { final isolateId = isolateRef.id!; final isolate = await service.getIsolate(isolateId); final rootLibId = isolate.rootLib!.id!; // We are at our breakpoint with global=100. final result = await service.evaluate( isolateId, rootLibId, 'global', ) as InstanceRef; print('global is $result'); expect(result.valueAsString, '100'); // Rewind the top stack frame. await service.resume(isolateId, step: StepOption.kRewind, frameIndex: 1); }, hasStoppedAtBreakpoint, stoppedAtLine(LINE_B), resumeIsolate, hasStoppedAtBreakpoint, stoppedAtLine(LINE_0), stepOver, hasStoppedAtBreakpoint, stoppedAtLine(LINE_A), (VmService service, IsolateRef isolateRef) async { final isolateId = isolateRef.id!; final isolate = await service.getIsolate(isolateId); final rootLibId = isolate.rootLib!.id!; // global still is equal to 100. We did not execute 'global++'. final result = await service.evaluate( isolateId, rootLibId, 'global', ) as InstanceRef; print('global is $result'); expect(result.valueAsString, '100'); // Rewind up to 'test'/ await service.resume(isolateId, step: StepOption.kRewind, frameIndex: 3); }, hasStoppedAtBreakpoint, stoppedAtLine(LINE_D), (VmService service, IsolateRef isolateRef) async { final isolateId = isolateRef.id!; final isolate = await service.getIsolate(isolateId); final rootLibId = isolate.rootLib!.id!; // Reset global to 0 and start again. final result = await service.evaluate( isolateId, rootLibId, 'global = 0', ) as InstanceRef; expect(result.valueAsString, '0'); }, resumeIsolate, hasStoppedAtBreakpoint, stoppedAtLine(LINE_0), stepOver, hasStoppedAtBreakpoint, stoppedAtLine(LINE_A), (VmService service, IsolateRef isolateRef) async { final isolateId = isolateRef.id!; final isolate = await service.getIsolate(isolateId); final rootLibId = isolate.rootLib!.id!; // We are at our breakpoint with global=100. final result = await service.evaluate( isolateId, rootLibId, 'global', ) as InstanceRef; print('global is $result'); expect(result.valueAsString, '100'); // Rewind the top 2 stack frames. await service.resume(isolateId, step: StepOption.kRewind, frameIndex: 2); }, hasStoppedAtBreakpoint, stoppedAtLine(LINE_C), ]; void main([args = const []]) => runIsolateTests( args, tests, 'rewind_test.dart', testeeConcurrent: test, extraArgs: [ '--trace-rewind', '--no-prune-dead-locals', '--no-background-compilation', '--optimization-counter-threshold=10', ], );