mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
Implement rewind on all platforms except for dbc.
BUG= Review URL: https://codereview.chromium.org/2534413005 .
This commit is contained in:
parent
1ebeb79942
commit
e0d350aa27
|
@ -209,7 +209,8 @@ class DownCommand extends DebuggerCommand {
|
||||||
debugger.downFrame(count);
|
debugger.downFrame(count);
|
||||||
debugger.console.print('frame = ${debugger.currentFrame}');
|
debugger.console.print('frame = ${debugger.currentFrame}');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugger.console.print('frame must be in range [${e.start},${e.end-1}]');
|
debugger.console.print(
|
||||||
|
'frame must be in range [${e.start}..${e.end-1}]');
|
||||||
}
|
}
|
||||||
return new Future.value(null);
|
return new Future.value(null);
|
||||||
}
|
}
|
||||||
|
@ -243,7 +244,8 @@ class UpCommand extends DebuggerCommand {
|
||||||
debugger.upFrame(count);
|
debugger.upFrame(count);
|
||||||
debugger.console.print('frame = ${debugger.currentFrame}');
|
debugger.console.print('frame = ${debugger.currentFrame}');
|
||||||
} on RangeError catch (e) {
|
} on RangeError catch (e) {
|
||||||
debugger.console.print('frame must be in range [${e.start},${e.end-1}]');
|
debugger.console.print(
|
||||||
|
'frame must be in range [${e.start}..${e.end-1}]');
|
||||||
}
|
}
|
||||||
return new Future.value(null);
|
return new Future.value(null);
|
||||||
}
|
}
|
||||||
|
@ -279,7 +281,8 @@ class FrameCommand extends DebuggerCommand {
|
||||||
debugger.currentFrame = frame;
|
debugger.currentFrame = frame;
|
||||||
debugger.console.print('frame = ${debugger.currentFrame}');
|
debugger.console.print('frame = ${debugger.currentFrame}');
|
||||||
} on RangeError catch (e) {
|
} on RangeError catch (e) {
|
||||||
debugger.console.print('frame must be in range [${e.start},${e.end-1}]');
|
debugger.console.print(
|
||||||
|
'frame must be in range [${e.start}..${e.end-1}]');
|
||||||
}
|
}
|
||||||
return new Future.value(null);
|
return new Future.value(null);
|
||||||
}
|
}
|
||||||
|
@ -403,6 +406,73 @@ class StepCommand extends DebuggerCommand {
|
||||||
'Syntax: step\n';
|
'Syntax: step\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RewindCommand extends DebuggerCommand {
|
||||||
|
RewindCommand(Debugger debugger) : super(debugger, 'rewind', []);
|
||||||
|
|
||||||
|
Future run(List<String> args) async {
|
||||||
|
try {
|
||||||
|
int count = 1;
|
||||||
|
if (args.length == 1) {
|
||||||
|
count = int.parse(args[0]);
|
||||||
|
} else if (args.length > 1) {
|
||||||
|
debugger.console.print('rewind expects 0 or 1 argument');
|
||||||
|
return;
|
||||||
|
} else if (count < 1 || count > debugger.stackDepth) {
|
||||||
|
debugger.console.print(
|
||||||
|
'frame must be in range [1..${debugger.stackDepth - 1}]');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await debugger.rewind(count);
|
||||||
|
} on S.ServerRpcException catch(e) {
|
||||||
|
if (e.code == S.ServerRpcException.kCannotResume) {
|
||||||
|
debugger.console.printRed(e.data['details']);
|
||||||
|
} else {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String helpShort = 'Rewind the stack to a previous frame';
|
||||||
|
|
||||||
|
String helpLong =
|
||||||
|
'Rewind the stack to a previous frame.\n'
|
||||||
|
'\n'
|
||||||
|
'Syntax: rewind\n'
|
||||||
|
' rewind <count>\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReloadCommand extends DebuggerCommand {
|
||||||
|
ReloadCommand(Debugger debugger) : super(debugger, 'reload', []);
|
||||||
|
|
||||||
|
Future run(List<String> args) async {
|
||||||
|
try {
|
||||||
|
int count = 1;
|
||||||
|
if (args.length > 0) {
|
||||||
|
debugger.console.print('reload expects no arguments');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await debugger.isolate.reloadSources();
|
||||||
|
debugger.console.print('reload complete');
|
||||||
|
await debugger.refreshStack();
|
||||||
|
} on S.ServerRpcException catch(e) {
|
||||||
|
if (e.code == S.ServerRpcException.kIsolateReloadBarred ||
|
||||||
|
e.code == S.ServerRpcException.kIsolateReloadFailed ||
|
||||||
|
e.code == S.ServerRpcException.kIsolateIsReloading) {
|
||||||
|
debugger.console.printRed(e.data['details']);
|
||||||
|
} else {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String helpShort = 'Reload the sources for the current isolate';
|
||||||
|
|
||||||
|
String helpLong =
|
||||||
|
'Reload the sources for the current isolate.\n'
|
||||||
|
'\n'
|
||||||
|
'Syntax: reload\n';
|
||||||
|
}
|
||||||
|
|
||||||
class ClsCommand extends DebuggerCommand {
|
class ClsCommand extends DebuggerCommand {
|
||||||
ClsCommand(Debugger debugger) : super(debugger, 'cls', []) {}
|
ClsCommand(Debugger debugger) : super(debugger, 'cls', []) {}
|
||||||
|
|
||||||
|
@ -663,8 +733,8 @@ class BreakCommand extends DebuggerCommand {
|
||||||
var script = loc.script;
|
var script = loc.script;
|
||||||
await script.load();
|
await script.load();
|
||||||
if (loc.line < 1 || loc.line > script.lines.length) {
|
if (loc.line < 1 || loc.line > script.lines.length) {
|
||||||
debugger.console
|
debugger.console.print(
|
||||||
.print('line number must be in range [1,${script.lines.length}]');
|
'line number must be in range [1..${script.lines.length}]');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -744,7 +814,7 @@ class ClearCommand extends DebuggerCommand {
|
||||||
var script = loc.script;
|
var script = loc.script;
|
||||||
if (loc.line < 1 || loc.line > script.lines.length) {
|
if (loc.line < 1 || loc.line > script.lines.length) {
|
||||||
debugger.console
|
debugger.console
|
||||||
.print('line number must be in range [1,${script.lines.length}]');
|
.print('line number must be in range [1..${script.lines.length}]');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var lineInfo = script.getLine(loc.line);
|
var lineInfo = script.getLine(loc.line);
|
||||||
|
@ -899,7 +969,6 @@ class IsolateCommand extends DebuggerCommand {
|
||||||
: super(debugger, 'isolate', [
|
: super(debugger, 'isolate', [
|
||||||
new IsolateListCommand(debugger),
|
new IsolateListCommand(debugger),
|
||||||
new IsolateNameCommand(debugger),
|
new IsolateNameCommand(debugger),
|
||||||
new IsolateReloadCommand(debugger),
|
|
||||||
]) {
|
]) {
|
||||||
alias = 'i';
|
alias = 'i';
|
||||||
}
|
}
|
||||||
|
@ -1042,27 +1111,6 @@ class IsolateNameCommand extends DebuggerCommand {
|
||||||
'Syntax: isolate name <name>\n';
|
'Syntax: isolate name <name>\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
class IsolateReloadCommand extends DebuggerCommand {
|
|
||||||
IsolateReloadCommand(Debugger debugger) : super(debugger, 'reload', []);
|
|
||||||
|
|
||||||
Future run(List<String> args) async {
|
|
||||||
if (debugger.isolate == null) {
|
|
||||||
debugger.console.print('There is no current vm');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await debugger.isolate.reloadSources();
|
|
||||||
|
|
||||||
debugger.console.print('Isolate reloading....');
|
|
||||||
}
|
|
||||||
|
|
||||||
String helpShort = 'Reload the sources for the current isolate.';
|
|
||||||
|
|
||||||
String helpLong = 'Reload the sources for the current isolate.\n'
|
|
||||||
'\n'
|
|
||||||
'Syntax: reload\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
class InfoCommand extends DebuggerCommand {
|
class InfoCommand extends DebuggerCommand {
|
||||||
InfoCommand(Debugger debugger)
|
InfoCommand(Debugger debugger)
|
||||||
: super(debugger, 'info', [
|
: super(debugger, 'info', [
|
||||||
|
@ -1371,7 +1419,9 @@ class ObservatoryDebugger extends Debugger {
|
||||||
new LogCommand(this),
|
new LogCommand(this),
|
||||||
new PauseCommand(this),
|
new PauseCommand(this),
|
||||||
new PrintCommand(this),
|
new PrintCommand(this),
|
||||||
|
new ReloadCommand(this),
|
||||||
new RefreshCommand(this),
|
new RefreshCommand(this),
|
||||||
|
new RewindCommand(this),
|
||||||
new SetCommand(this),
|
new SetCommand(this),
|
||||||
new SmartNextCommand(this),
|
new SmartNextCommand(this),
|
||||||
new StepCommand(this),
|
new StepCommand(this),
|
||||||
|
@ -1879,6 +1929,20 @@ class ObservatoryDebugger extends Debugger {
|
||||||
return new Future.value(null);
|
return new Future.value(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future rewind(int count) {
|
||||||
|
if (isolatePaused()) {
|
||||||
|
var event = isolate.pauseEvent;
|
||||||
|
if (event is M.PauseExitEvent) {
|
||||||
|
console.print("Type 'continue' [F7] to exit the isolate");
|
||||||
|
return new Future.value(null);
|
||||||
|
}
|
||||||
|
return isolate.rewind(count);
|
||||||
|
} else {
|
||||||
|
console.print('The program must be paused');
|
||||||
|
return new Future.value(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DebuggerPageElement extends HtmlElement implements Renderable {
|
class DebuggerPageElement extends HtmlElement implements Renderable {
|
||||||
|
@ -2513,7 +2577,9 @@ class DebuggerFrameElement extends HtmlElement implements Renderable {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool matchFrame(S.Frame newFrame) {
|
bool matchFrame(S.Frame newFrame) {
|
||||||
return newFrame.function.id == _frame.function.id;
|
return (newFrame.function.id == _frame.function.id &&
|
||||||
|
newFrame.location.script.id ==
|
||||||
|
frame.location.script.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateFrame(S.Frame newFrame) {
|
void updateFrame(S.Frame newFrame) {
|
||||||
|
|
|
@ -42,11 +42,13 @@ class ServerRpcException extends RpcException implements M.RequestException {
|
||||||
static const kStreamNotSubscribed = 104;
|
static const kStreamNotSubscribed = 104;
|
||||||
static const kIsolateMustBeRunnable = 105;
|
static const kIsolateMustBeRunnable = 105;
|
||||||
static const kIsolateMustBePaused = 106;
|
static const kIsolateMustBePaused = 106;
|
||||||
|
static const kCannotResume = 107;
|
||||||
static const kIsolateIsReloading = 1000;
|
static const kIsolateIsReloading = 1000;
|
||||||
static const kFileSystemAlreadyExists = 1001;
|
static const kFileSystemAlreadyExists = 1001;
|
||||||
static const kFileSystemDoesNotExist = 1002;
|
static const kFileSystemDoesNotExist = 1002;
|
||||||
static const kFileDoesNotExist = 1003;
|
static const kFileDoesNotExist = 1003;
|
||||||
static const kIsolateReloadFailed = 1004;
|
static const kIsolateReloadFailed = 1004;
|
||||||
|
static const kIsolateReloadBarred = 1005;
|
||||||
|
|
||||||
int code;
|
int code;
|
||||||
Map data;
|
Map data;
|
||||||
|
@ -1279,7 +1281,6 @@ class Isolate extends ServiceObjectOwner implements M.Isolate {
|
||||||
bool loading = true;
|
bool loading = true;
|
||||||
bool runnable = false;
|
bool runnable = false;
|
||||||
bool ioEnabled = false;
|
bool ioEnabled = false;
|
||||||
bool reloading = false;
|
|
||||||
M.IsolateStatus get status {
|
M.IsolateStatus get status {
|
||||||
if (paused) {
|
if (paused) {
|
||||||
return M.IsolateStatus.paused;
|
return M.IsolateStatus.paused;
|
||||||
|
@ -1344,13 +1345,12 @@ class Isolate extends ServiceObjectOwner implements M.Isolate {
|
||||||
params['pause'] = pause;
|
params['pause'] = pause;
|
||||||
}
|
}
|
||||||
return invokeRpc('reloadSources', params).then((result) {
|
return invokeRpc('reloadSources', params).then((result) {
|
||||||
reloading = true;
|
_cache.clear();
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleIsolateReloadEvent(ServiceEvent event) {
|
void _handleIsolateReloadEvent(ServiceEvent event) {
|
||||||
reloading = false;
|
|
||||||
if (event.reloadError != null) {
|
if (event.reloadError != null) {
|
||||||
// Failure.
|
// Failure.
|
||||||
print('Reload failed: ${event.reloadError}');
|
print('Reload failed: ${event.reloadError}');
|
||||||
|
@ -1777,6 +1777,10 @@ class Isolate extends ServiceObjectOwner implements M.Isolate {
|
||||||
return invokeRpc('resume', {'step': 'Out'});
|
return invokeRpc('resume', {'step': 'Out'});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future rewind(int count) {
|
||||||
|
return invokeRpc('resume', {'step': 'Rewind', 'frameIndex': count});
|
||||||
|
}
|
||||||
|
|
||||||
Future setName(String newName) {
|
Future setName(String newName) {
|
||||||
return invokeRpc('setName', {'name': newName});
|
return invokeRpc('setName', {'name': newName});
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,5 +52,6 @@ hasStoppedAtBreakpoint,
|
||||||
|
|
||||||
main(args) => runIsolateTests(args, tests,
|
main(args) => runIsolateTests(args, tests,
|
||||||
testeeConcurrent: testMain,
|
testeeConcurrent: testMain,
|
||||||
trace_service: true,
|
verbose_vm: true,
|
||||||
verbose_vm: true);
|
extraArgs: [ '--trace-service',
|
||||||
|
'--trace-service-verbose' ]);
|
||||||
|
|
|
@ -86,5 +86,6 @@ main(args) => runIsolateTests(args, tests,
|
||||||
testeeConcurrent: testMain,
|
testeeConcurrent: testMain,
|
||||||
pause_on_start: true,
|
pause_on_start: true,
|
||||||
pause_on_exit: true,
|
pause_on_exit: true,
|
||||||
trace_service: true,
|
verbose_vm: true,
|
||||||
verbose_vm: true);
|
extraArgs: [ '--trace-service',
|
||||||
|
'--trace-service-verbose' ]);
|
||||||
|
|
|
@ -89,5 +89,6 @@ main(args) => runIsolateTests(args, tests,
|
||||||
testeeConcurrent: testMain,
|
testeeConcurrent: testMain,
|
||||||
pause_on_start: true,
|
pause_on_start: true,
|
||||||
pause_on_exit: true,
|
pause_on_exit: true,
|
||||||
trace_service: true,
|
verbose_vm: true,
|
||||||
verbose_vm: true);
|
extraArgs: [ '--trace-service',
|
||||||
|
'--trace-service-verbose' ]);
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
// 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.
|
||||||
|
// VMOptions=--error_on_bad_type --error_on_bad_override
|
||||||
|
|
||||||
|
import 'dart:developer';
|
||||||
|
import 'package:observatory/service_io.dart';
|
||||||
|
import 'package:unittest/unittest.dart';
|
||||||
|
import 'service_test_common.dart';
|
||||||
|
import 'test_helper.dart';
|
||||||
|
|
||||||
|
const alwaysInline = "AlwaysInline";
|
||||||
|
const noInline = "NeverInline";
|
||||||
|
|
||||||
|
int LINE_A = 35;
|
||||||
|
int LINE_B = 40;
|
||||||
|
int LINE_C = 43;
|
||||||
|
int LINE_D = 47;
|
||||||
|
|
||||||
|
int global = 0;
|
||||||
|
|
||||||
|
@noInline
|
||||||
|
b3(x) {
|
||||||
|
int sum = 0;
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < x; i++) {
|
||||||
|
sum += x;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print("caught $e");
|
||||||
|
}
|
||||||
|
if (global >= 100) {
|
||||||
|
debugger();
|
||||||
|
}
|
||||||
|
global = global + 1; // Line A
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
@alwaysInline
|
||||||
|
b2(x) => b3(x); // Line B
|
||||||
|
|
||||||
|
@alwaysInline
|
||||||
|
b1(x) => b2(x); // Line C
|
||||||
|
|
||||||
|
test() {
|
||||||
|
while (true) {
|
||||||
|
b1(10000); // Line D
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests = [
|
||||||
|
hasStoppedAtBreakpoint,
|
||||||
|
stoppedAtLine(LINE_A),
|
||||||
|
|
||||||
|
(Isolate isolate) async {
|
||||||
|
// We are at our breakpoint with global=100.
|
||||||
|
var result = await isolate.rootLibrary.evaluate('global');
|
||||||
|
print('global is $result');
|
||||||
|
expect(result.type, equals('Instance'));
|
||||||
|
expect(result.valueAsString, equals('100'));
|
||||||
|
|
||||||
|
// Rewind the top stack frame.
|
||||||
|
bool caughtException;
|
||||||
|
try {
|
||||||
|
result = await isolate.rewind(1);
|
||||||
|
expect(false, isTrue, reason:'Unreachable');
|
||||||
|
} on ServerRpcException catch(e) {
|
||||||
|
caughtException = true;
|
||||||
|
expect(e.code, equals(ServerRpcException.kCannotResume));
|
||||||
|
expect(e.message,
|
||||||
|
'Cannot rewind to frame 1 due to conflicting compiler '
|
||||||
|
'optimizations. Run the vm with --no-prune-dead-locals to '
|
||||||
|
'disallow these optimizations. Next valid rewind frame is 4.');
|
||||||
|
}
|
||||||
|
expect(caughtException, isTrue);
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
main(args) => runIsolateTests(args, tests, testeeConcurrent: test,
|
||||||
|
extraArgs:
|
||||||
|
['--trace-rewind',
|
||||||
|
'--prune-dead-locals',
|
||||||
|
'--enable-inlining-annotations',
|
||||||
|
'--no-background-compilation',
|
||||||
|
'--optimization-counter-threshold=10']);
|
173
runtime/observatory/tests/service/rewind_test.dart
Normal file
173
runtime/observatory/tests/service/rewind_test.dart
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
// 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.
|
||||||
|
// VMOptions=--error_on_bad_type --error_on_bad_override
|
||||||
|
|
||||||
|
import 'dart:developer';
|
||||||
|
import 'package:observatory/service_io.dart';
|
||||||
|
import 'package:unittest/unittest.dart';
|
||||||
|
import 'service_test_common.dart';
|
||||||
|
import 'test_helper.dart';
|
||||||
|
|
||||||
|
const alwaysInline = "AlwaysInline";
|
||||||
|
const noInline = "NeverInline";
|
||||||
|
|
||||||
|
int LINE_A = 35;
|
||||||
|
int LINE_B = 40;
|
||||||
|
int LINE_C = 43;
|
||||||
|
int LINE_D = 47;
|
||||||
|
|
||||||
|
int global = 0;
|
||||||
|
|
||||||
|
@noInline
|
||||||
|
b3(x) {
|
||||||
|
int sum = 0;
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < x; i++) {
|
||||||
|
sum += x;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print("caught $e");
|
||||||
|
}
|
||||||
|
if (global >= 100) {
|
||||||
|
debugger();
|
||||||
|
}
|
||||||
|
global = global + 1; // Line A
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
@alwaysInline
|
||||||
|
b2(x) => b3(x); // Line B
|
||||||
|
|
||||||
|
@alwaysInline
|
||||||
|
b1(x) => b2(x); // Line C
|
||||||
|
|
||||||
|
test() {
|
||||||
|
while (true) {
|
||||||
|
b1(10000); // Line D
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests = [
|
||||||
|
hasStoppedAtBreakpoint,
|
||||||
|
stoppedAtLine(LINE_A),
|
||||||
|
|
||||||
|
(Isolate isolate) async {
|
||||||
|
// We are not able to rewind frame 0.
|
||||||
|
bool caughtException;
|
||||||
|
try {
|
||||||
|
await isolate.rewind(0);
|
||||||
|
expect(false, isTrue, reason:'Unreachable');
|
||||||
|
} on ServerRpcException catch(e) {
|
||||||
|
caughtException = true;
|
||||||
|
expect(e.code, equals(ServerRpcException.kCannotResume));
|
||||||
|
expect(e.message, 'Frame must be in bounds [1..9]: saw 0');
|
||||||
|
}
|
||||||
|
expect(caughtException, isTrue);
|
||||||
|
},
|
||||||
|
|
||||||
|
(Isolate isolate) async {
|
||||||
|
// We are not able to rewind frame 10.
|
||||||
|
bool caughtException;
|
||||||
|
try {
|
||||||
|
await isolate.rewind(10);
|
||||||
|
expect(false, isTrue, reason:'Unreachable');
|
||||||
|
} on ServerRpcException catch(e) {
|
||||||
|
caughtException = true;
|
||||||
|
expect(e.code, equals(ServerRpcException.kCannotResume));
|
||||||
|
expect(e.message, 'Frame must be in bounds [1..9]: saw 10');
|
||||||
|
}
|
||||||
|
expect(caughtException, isTrue);
|
||||||
|
},
|
||||||
|
|
||||||
|
(Isolate isolate) async {
|
||||||
|
// We are at our breakpoint with global=100.
|
||||||
|
var result = await isolate.rootLibrary.evaluate('global');
|
||||||
|
print('global is $result');
|
||||||
|
expect(result.type, equals('Instance'));
|
||||||
|
expect(result.valueAsString, equals('100'));
|
||||||
|
|
||||||
|
// Rewind the top stack frame.
|
||||||
|
result = await isolate.rewind(1);
|
||||||
|
expect(result['type'], equals('Success'));
|
||||||
|
},
|
||||||
|
|
||||||
|
hasStoppedAtBreakpoint,
|
||||||
|
stoppedAtLine(LINE_B),
|
||||||
|
|
||||||
|
(Isolate isolate) async {
|
||||||
|
var result = await isolate.resume();
|
||||||
|
expect(result['type'], equals('Success'));
|
||||||
|
},
|
||||||
|
|
||||||
|
hasStoppedAtBreakpoint,
|
||||||
|
stoppedAtLine(LINE_A),
|
||||||
|
|
||||||
|
(Isolate isolate) async {
|
||||||
|
// global still is equal to 100. We did not execute "global++".
|
||||||
|
var result = await isolate.rootLibrary.evaluate('global');
|
||||||
|
print('global is $result');
|
||||||
|
expect(result.type, equals('Instance'));
|
||||||
|
expect(result.valueAsString, equals('100'));
|
||||||
|
|
||||||
|
// Resume again, for fun.
|
||||||
|
result = await isolate.resume();
|
||||||
|
expect(result['type'], equals('Success'));
|
||||||
|
},
|
||||||
|
|
||||||
|
hasStoppedAtBreakpoint,
|
||||||
|
stoppedAtLine(LINE_A),
|
||||||
|
|
||||||
|
(Isolate isolate) async {
|
||||||
|
// global is now 101.
|
||||||
|
var result = await isolate.rootLibrary.evaluate('global');
|
||||||
|
print('global is $result');
|
||||||
|
expect(result.type, equals('Instance'));
|
||||||
|
expect(result.valueAsString, equals('101'));
|
||||||
|
|
||||||
|
// Rewind up to 'test'/
|
||||||
|
result = await isolate.rewind(3);
|
||||||
|
expect(result['type'], equals('Success'));
|
||||||
|
},
|
||||||
|
|
||||||
|
hasStoppedAtBreakpoint,
|
||||||
|
stoppedAtLine(LINE_D),
|
||||||
|
|
||||||
|
(Isolate isolate) async {
|
||||||
|
// Reset global to 0 and start again.
|
||||||
|
var result = await isolate.rootLibrary.evaluate('global=0');
|
||||||
|
print('set global to $result');
|
||||||
|
expect(result.type, equals('Instance'));
|
||||||
|
expect(result.valueAsString, equals('0'));
|
||||||
|
|
||||||
|
result = await isolate.resume();
|
||||||
|
expect(result['type'], equals('Success'));
|
||||||
|
},
|
||||||
|
|
||||||
|
hasStoppedAtBreakpoint,
|
||||||
|
stoppedAtLine(LINE_A),
|
||||||
|
|
||||||
|
(Isolate isolate) async {
|
||||||
|
// We are at our breakpoint with global=100.
|
||||||
|
var result = await isolate.rootLibrary.evaluate('global');
|
||||||
|
print('global is $result');
|
||||||
|
expect(result.type, equals('Instance'));
|
||||||
|
expect(result.valueAsString, equals('100'));
|
||||||
|
|
||||||
|
// Rewind the top 2 stack frames.
|
||||||
|
result = await isolate.rewind(2);
|
||||||
|
expect(result['type'], equals('Success'));
|
||||||
|
},
|
||||||
|
|
||||||
|
hasStoppedAtBreakpoint,
|
||||||
|
stoppedAtLine(LINE_C),
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
main(args) => runIsolateTests(args, tests, testeeConcurrent: test,
|
||||||
|
extraArgs:
|
||||||
|
['--trace-rewind',
|
||||||
|
'--no-prune-dead-locals',
|
||||||
|
'--enable-inlining-annotations',
|
||||||
|
'--no-background-compilation',
|
||||||
|
'--optimization-counter-threshold=10']);
|
|
@ -64,6 +64,8 @@ evaluate_activation_in_method_class_test: CompileTimeError # Issue 24478
|
||||||
get_allocation_samples_test: RuntimeError # Profiling unimplemented.
|
get_allocation_samples_test: RuntimeError # Profiling unimplemented.
|
||||||
get_cpu_profile_timeline_rpc_test: RuntimeError # Profiling unimplemented.
|
get_cpu_profile_timeline_rpc_test: RuntimeError # Profiling unimplemented.
|
||||||
implicit_getter_setter_test: RuntimeError # Field guards unimplemented.
|
implicit_getter_setter_test: RuntimeError # Field guards unimplemented.
|
||||||
|
rewind_test: RuntimeError # Issue 27878
|
||||||
|
rewind_optimized_out_test: RuntimeError # Issue 27878
|
||||||
|
|
||||||
[ $hot_reload || $hot_reload_rollback ]
|
[ $hot_reload || $hot_reload_rollback ]
|
||||||
# Skip all service tests because random reloads interfere.
|
# Skip all service tests because random reloads interfere.
|
||||||
|
|
|
@ -234,6 +234,10 @@ IsolateTest stoppedAtLine(int line) {
|
||||||
return (Isolate isolate) async {
|
return (Isolate isolate) async {
|
||||||
print("Checking we are at line $line");
|
print("Checking we are at line $line");
|
||||||
|
|
||||||
|
// Make sure that the isolate has stopped.
|
||||||
|
isolate.reload();
|
||||||
|
expect(isolate.pauseEvent is! M.ResumeEvent, isTrue);
|
||||||
|
|
||||||
ServiceMap stack = await isolate.getStack();
|
ServiceMap stack = await isolate.getStack();
|
||||||
expect(stack.type, equals('Stack'));
|
expect(stack.type, equals('Stack'));
|
||||||
|
|
||||||
|
|
|
@ -32,4 +32,6 @@ var tests = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
main(args) async => runVMTests(args, tests, trace_service: true);
|
main(args) async => runVMTests(args, tests,
|
||||||
|
extraArgs: [ '--trace-service',
|
||||||
|
'--trace-service-verbose' ]);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
// 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.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
// VMOptions=--error_on_bad_type --error_on_bad_override --verbose_debug --trace_service
|
// VMOptions=--error_on_bad_type --error_on_bad_override --verbose_debug
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
|
@ -90,15 +90,12 @@ class _ServiceTesteeLauncher {
|
||||||
Future<Process> _spawnProcess(bool pause_on_start,
|
Future<Process> _spawnProcess(bool pause_on_start,
|
||||||
bool pause_on_exit,
|
bool pause_on_exit,
|
||||||
bool pause_on_unhandled_exceptions,
|
bool pause_on_unhandled_exceptions,
|
||||||
bool trace_service,
|
|
||||||
bool trace_compiler,
|
|
||||||
bool testeeControlsServer,
|
bool testeeControlsServer,
|
||||||
bool useAuthToken) {
|
bool useAuthToken,
|
||||||
|
List<String> extraArgs) {
|
||||||
assert(pause_on_start != null);
|
assert(pause_on_start != null);
|
||||||
assert(pause_on_exit != null);
|
assert(pause_on_exit != null);
|
||||||
assert(pause_on_unhandled_exceptions != null);
|
assert(pause_on_unhandled_exceptions != null);
|
||||||
assert(trace_service != null);
|
|
||||||
assert(trace_compiler != null);
|
|
||||||
assert(testeeControlsServer != null);
|
assert(testeeControlsServer != null);
|
||||||
assert(useAuthToken != null);
|
assert(useAuthToken != null);
|
||||||
|
|
||||||
|
@ -106,39 +103,29 @@ class _ServiceTesteeLauncher {
|
||||||
return _spawnSkyProcess(pause_on_start,
|
return _spawnSkyProcess(pause_on_start,
|
||||||
pause_on_exit,
|
pause_on_exit,
|
||||||
pause_on_unhandled_exceptions,
|
pause_on_unhandled_exceptions,
|
||||||
trace_service,
|
testeeControlsServer,
|
||||||
trace_compiler,
|
extraArgs);
|
||||||
testeeControlsServer);
|
|
||||||
} else {
|
} else {
|
||||||
return _spawnDartProcess(pause_on_start,
|
return _spawnDartProcess(pause_on_start,
|
||||||
pause_on_exit,
|
pause_on_exit,
|
||||||
pause_on_unhandled_exceptions,
|
pause_on_unhandled_exceptions,
|
||||||
trace_service,
|
|
||||||
trace_compiler,
|
|
||||||
testeeControlsServer,
|
testeeControlsServer,
|
||||||
useAuthToken);
|
useAuthToken,
|
||||||
|
extraArgs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Process> _spawnDartProcess(bool pause_on_start,
|
Future<Process> _spawnDartProcess(bool pause_on_start,
|
||||||
bool pause_on_exit,
|
bool pause_on_exit,
|
||||||
bool pause_on_unhandled_exceptions,
|
bool pause_on_unhandled_exceptions,
|
||||||
bool trace_service,
|
|
||||||
bool trace_compiler,
|
|
||||||
bool testeeControlsServer,
|
bool testeeControlsServer,
|
||||||
bool useAuthToken) {
|
bool useAuthToken,
|
||||||
|
List<String> extraArgs) {
|
||||||
assert(!_shouldLaunchSkyShell());
|
assert(!_shouldLaunchSkyShell());
|
||||||
|
|
||||||
String dartExecutable = Platform.executable;
|
String dartExecutable = Platform.executable;
|
||||||
|
|
||||||
var fullArgs = [];
|
var fullArgs = [];
|
||||||
if (trace_service) {
|
|
||||||
fullArgs.add('--trace-service');
|
|
||||||
fullArgs.add('--trace-service-verbose');
|
|
||||||
}
|
|
||||||
if (trace_compiler) {
|
|
||||||
fullArgs.add('--trace-compiler');
|
|
||||||
}
|
|
||||||
if (pause_on_start) {
|
if (pause_on_start) {
|
||||||
fullArgs.add('--pause-isolates-on-start');
|
fullArgs.add('--pause-isolates-on-start');
|
||||||
}
|
}
|
||||||
|
@ -148,6 +135,9 @@ class _ServiceTesteeLauncher {
|
||||||
if (pause_on_unhandled_exceptions) {
|
if (pause_on_unhandled_exceptions) {
|
||||||
fullArgs.add('--pause-isolates-on-unhandled-exceptions');
|
fullArgs.add('--pause-isolates-on-unhandled-exceptions');
|
||||||
}
|
}
|
||||||
|
if (extraArgs != null) {
|
||||||
|
fullArgs.addAll(extraArgs);
|
||||||
|
}
|
||||||
|
|
||||||
fullArgs.addAll(Platform.executableArguments);
|
fullArgs.addAll(Platform.executableArguments);
|
||||||
if (!testeeControlsServer) {
|
if (!testeeControlsServer) {
|
||||||
|
@ -166,22 +156,14 @@ class _ServiceTesteeLauncher {
|
||||||
Future<Process> _spawnSkyProcess(bool pause_on_start,
|
Future<Process> _spawnSkyProcess(bool pause_on_start,
|
||||||
bool pause_on_exit,
|
bool pause_on_exit,
|
||||||
bool pause_on_unhandled_exceptions,
|
bool pause_on_unhandled_exceptions,
|
||||||
bool trace_service,
|
bool testeeControlsServer,
|
||||||
bool trace_compiler,
|
List<String> extraArgs) {
|
||||||
bool testeeControlsServer) {
|
|
||||||
assert(_shouldLaunchSkyShell());
|
assert(_shouldLaunchSkyShell());
|
||||||
|
|
||||||
String dartExecutable = _skyShellPath();
|
String dartExecutable = _skyShellPath();
|
||||||
|
|
||||||
var dartFlags = [];
|
var dartFlags = [];
|
||||||
var fullArgs = [];
|
var fullArgs = [];
|
||||||
if (trace_service) {
|
|
||||||
dartFlags.add('--trace_service');
|
|
||||||
dartFlags.add('--trace_service_verbose');
|
|
||||||
}
|
|
||||||
if (trace_compiler) {
|
|
||||||
dartFlags.add('--trace_compiler');
|
|
||||||
}
|
|
||||||
if (pause_on_start) {
|
if (pause_on_start) {
|
||||||
dartFlags.add('--pause_isolates_on_start');
|
dartFlags.add('--pause_isolates_on_start');
|
||||||
fullArgs.add('--start-paused');
|
fullArgs.add('--start-paused');
|
||||||
|
@ -194,6 +176,9 @@ class _ServiceTesteeLauncher {
|
||||||
}
|
}
|
||||||
// Override mirrors.
|
// Override mirrors.
|
||||||
dartFlags.add('--enable_mirrors=true');
|
dartFlags.add('--enable_mirrors=true');
|
||||||
|
if (extraArgs != null) {
|
||||||
|
fullArgs.addAll(extraArgs);
|
||||||
|
}
|
||||||
|
|
||||||
fullArgs.addAll(Platform.executableArguments);
|
fullArgs.addAll(Platform.executableArguments);
|
||||||
if (!testeeControlsServer) {
|
if (!testeeControlsServer) {
|
||||||
|
@ -223,17 +208,15 @@ class _ServiceTesteeLauncher {
|
||||||
Future<Uri> launch(bool pause_on_start,
|
Future<Uri> launch(bool pause_on_start,
|
||||||
bool pause_on_exit,
|
bool pause_on_exit,
|
||||||
bool pause_on_unhandled_exceptions,
|
bool pause_on_unhandled_exceptions,
|
||||||
bool trace_service,
|
|
||||||
bool trace_compiler,
|
|
||||||
bool testeeControlsServer,
|
bool testeeControlsServer,
|
||||||
bool useAuthToken) {
|
bool useAuthToken,
|
||||||
|
List<String> extraArgs) {
|
||||||
return _spawnProcess(pause_on_start,
|
return _spawnProcess(pause_on_start,
|
||||||
pause_on_exit,
|
pause_on_exit,
|
||||||
pause_on_unhandled_exceptions,
|
pause_on_unhandled_exceptions,
|
||||||
trace_service,
|
|
||||||
trace_compiler,
|
|
||||||
testeeControlsServer,
|
testeeControlsServer,
|
||||||
useAuthToken).then((p) {
|
useAuthToken,
|
||||||
|
extraArgs).then((p) {
|
||||||
Completer<Uri> completer = new Completer<Uri>();
|
Completer<Uri> completer = new Completer<Uri>();
|
||||||
process = p;
|
process = p;
|
||||||
Uri uri;
|
Uri uri;
|
||||||
|
@ -288,12 +271,11 @@ void setupAddresses(Uri serverAddress) {
|
||||||
|
|
||||||
class _ServiceTesterRunner {
|
class _ServiceTesterRunner {
|
||||||
void run({List<String> mainArgs,
|
void run({List<String> mainArgs,
|
||||||
|
List<String> extraArgs,
|
||||||
List<VMTest> vmTests,
|
List<VMTest> vmTests,
|
||||||
List<IsolateTest> isolateTests,
|
List<IsolateTest> isolateTests,
|
||||||
bool pause_on_start: false,
|
bool pause_on_start: false,
|
||||||
bool pause_on_exit: false,
|
bool pause_on_exit: false,
|
||||||
bool trace_service: false,
|
|
||||||
bool trace_compiler: false,
|
|
||||||
bool verbose_vm: false,
|
bool verbose_vm: false,
|
||||||
bool pause_on_unhandled_exceptions: false,
|
bool pause_on_unhandled_exceptions: false,
|
||||||
bool testeeControlsServer: false,
|
bool testeeControlsServer: false,
|
||||||
|
@ -301,9 +283,8 @@ class _ServiceTesterRunner {
|
||||||
var process = new _ServiceTesteeLauncher();
|
var process = new _ServiceTesteeLauncher();
|
||||||
process.launch(pause_on_start, pause_on_exit,
|
process.launch(pause_on_start, pause_on_exit,
|
||||||
pause_on_unhandled_exceptions,
|
pause_on_unhandled_exceptions,
|
||||||
trace_service, trace_compiler,
|
|
||||||
testeeControlsServer,
|
testeeControlsServer,
|
||||||
useAuthToken).then((Uri serverAddress) async {
|
useAuthToken, extraArgs).then((Uri serverAddress) async {
|
||||||
if (mainArgs.contains("--gdb")) {
|
if (mainArgs.contains("--gdb")) {
|
||||||
var pid = process.process.pid;
|
var pid = process.process.pid;
|
||||||
var wait = new Duration(seconds: 10);
|
var wait = new Duration(seconds: 10);
|
||||||
|
@ -364,12 +345,11 @@ Future runIsolateTests(List<String> mainArgs,
|
||||||
testeeConcurrent(),
|
testeeConcurrent(),
|
||||||
bool pause_on_start: false,
|
bool pause_on_start: false,
|
||||||
bool pause_on_exit: false,
|
bool pause_on_exit: false,
|
||||||
bool trace_service: false,
|
|
||||||
bool trace_compiler: false,
|
|
||||||
bool verbose_vm: false,
|
bool verbose_vm: false,
|
||||||
bool pause_on_unhandled_exceptions: false,
|
bool pause_on_unhandled_exceptions: false,
|
||||||
bool testeeControlsServer: false,
|
bool testeeControlsServer: false,
|
||||||
bool useAuthToken: false}) async {
|
bool useAuthToken: false,
|
||||||
|
List<String> extraArgs}) async {
|
||||||
assert(!pause_on_start || testeeBefore == null);
|
assert(!pause_on_start || testeeBefore == null);
|
||||||
if (_isTestee()) {
|
if (_isTestee()) {
|
||||||
new _ServiceTesteeRunner().run(testeeBefore: testeeBefore,
|
new _ServiceTesteeRunner().run(testeeBefore: testeeBefore,
|
||||||
|
@ -379,11 +359,10 @@ Future runIsolateTests(List<String> mainArgs,
|
||||||
} else {
|
} else {
|
||||||
new _ServiceTesterRunner().run(
|
new _ServiceTesterRunner().run(
|
||||||
mainArgs: mainArgs,
|
mainArgs: mainArgs,
|
||||||
|
extraArgs: extraArgs,
|
||||||
isolateTests: tests,
|
isolateTests: tests,
|
||||||
pause_on_start: pause_on_start,
|
pause_on_start: pause_on_start,
|
||||||
pause_on_exit: pause_on_exit,
|
pause_on_exit: pause_on_exit,
|
||||||
trace_service: trace_service,
|
|
||||||
trace_compiler: trace_compiler,
|
|
||||||
verbose_vm: verbose_vm,
|
verbose_vm: verbose_vm,
|
||||||
pause_on_unhandled_exceptions: pause_on_unhandled_exceptions,
|
pause_on_unhandled_exceptions: pause_on_unhandled_exceptions,
|
||||||
testeeControlsServer: testeeControlsServer,
|
testeeControlsServer: testeeControlsServer,
|
||||||
|
@ -406,10 +385,9 @@ void runIsolateTestsSynchronous(List<String> mainArgs,
|
||||||
void testeeConcurrent(),
|
void testeeConcurrent(),
|
||||||
bool pause_on_start: false,
|
bool pause_on_start: false,
|
||||||
bool pause_on_exit: false,
|
bool pause_on_exit: false,
|
||||||
bool trace_service: false,
|
|
||||||
bool trace_compiler: false,
|
|
||||||
bool verbose_vm: false,
|
bool verbose_vm: false,
|
||||||
bool pause_on_unhandled_exceptions: false}) {
|
bool pause_on_unhandled_exceptions: false,
|
||||||
|
List<String> extraArgs}) {
|
||||||
assert(!pause_on_start || testeeBefore == null);
|
assert(!pause_on_start || testeeBefore == null);
|
||||||
if (_isTestee()) {
|
if (_isTestee()) {
|
||||||
new _ServiceTesteeRunner().runSync(testeeBeforeSync: testeeBefore,
|
new _ServiceTesteeRunner().runSync(testeeBeforeSync: testeeBefore,
|
||||||
|
@ -419,11 +397,10 @@ void runIsolateTestsSynchronous(List<String> mainArgs,
|
||||||
} else {
|
} else {
|
||||||
new _ServiceTesterRunner().run(
|
new _ServiceTesterRunner().run(
|
||||||
mainArgs: mainArgs,
|
mainArgs: mainArgs,
|
||||||
|
extraArgs: extraArgs,
|
||||||
isolateTests: tests,
|
isolateTests: tests,
|
||||||
pause_on_start: pause_on_start,
|
pause_on_start: pause_on_start,
|
||||||
pause_on_exit: pause_on_exit,
|
pause_on_exit: pause_on_exit,
|
||||||
trace_service: trace_service,
|
|
||||||
trace_compiler: trace_compiler,
|
|
||||||
verbose_vm: verbose_vm,
|
verbose_vm: verbose_vm,
|
||||||
pause_on_unhandled_exceptions: pause_on_unhandled_exceptions);
|
pause_on_unhandled_exceptions: pause_on_unhandled_exceptions);
|
||||||
}
|
}
|
||||||
|
@ -440,10 +417,9 @@ Future runVMTests(List<String> mainArgs,
|
||||||
testeeConcurrent(),
|
testeeConcurrent(),
|
||||||
bool pause_on_start: false,
|
bool pause_on_start: false,
|
||||||
bool pause_on_exit: false,
|
bool pause_on_exit: false,
|
||||||
bool trace_service: false,
|
|
||||||
bool trace_compiler: false,
|
|
||||||
bool verbose_vm: false,
|
bool verbose_vm: false,
|
||||||
bool pause_on_unhandled_exceptions: false}) async {
|
bool pause_on_unhandled_exceptions: false,
|
||||||
|
List<String> extraArgs}) async {
|
||||||
if (_isTestee()) {
|
if (_isTestee()) {
|
||||||
new _ServiceTesteeRunner().run(testeeBefore: testeeBefore,
|
new _ServiceTesteeRunner().run(testeeBefore: testeeBefore,
|
||||||
testeeConcurrent: testeeConcurrent,
|
testeeConcurrent: testeeConcurrent,
|
||||||
|
@ -452,11 +428,10 @@ Future runVMTests(List<String> mainArgs,
|
||||||
} else {
|
} else {
|
||||||
new _ServiceTesterRunner().run(
|
new _ServiceTesterRunner().run(
|
||||||
mainArgs: mainArgs,
|
mainArgs: mainArgs,
|
||||||
|
extraArgs: extraArgs,
|
||||||
vmTests: tests,
|
vmTests: tests,
|
||||||
pause_on_start: pause_on_start,
|
pause_on_start: pause_on_start,
|
||||||
pause_on_exit: pause_on_exit,
|
pause_on_exit: pause_on_exit,
|
||||||
trace_service: trace_service,
|
|
||||||
trace_compiler: trace_compiler,
|
|
||||||
verbose_vm: verbose_vm,
|
verbose_vm: verbose_vm,
|
||||||
pause_on_unhandled_exceptions: pause_on_unhandled_exceptions);
|
pause_on_unhandled_exceptions: pause_on_unhandled_exceptions);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2215,6 +2215,15 @@ DEFINE_RUNTIME_ENTRY(DeoptimizeMaterialize, 0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DEFINE_RUNTIME_ENTRY(RewindPostDeopt, 0) {
|
||||||
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||||
|
#if !defined(PRODUCT)
|
||||||
|
isolate->debugger()->RewindPostDeopt();
|
||||||
|
#endif // !PRODUCT
|
||||||
|
#endif // !DART_PRECOMPILED_RUNTIME
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
DEFINE_LEAF_RUNTIME_ENTRY(intptr_t,
|
DEFINE_LEAF_RUNTIME_ENTRY(intptr_t,
|
||||||
BigintCompare,
|
BigintCompare,
|
||||||
2,
|
2,
|
||||||
|
|
|
@ -551,7 +551,7 @@ RawObject* DartLibraryCalls::HandleMessage(const Object& handler,
|
||||||
// If the isolate is being debugged and the debugger was stepping
|
// If the isolate is being debugged and the debugger was stepping
|
||||||
// through code, enable single stepping so debugger will stop
|
// through code, enable single stepping so debugger will stop
|
||||||
// at the first location the user is interested in.
|
// at the first location the user is interested in.
|
||||||
isolate->debugger()->SetSingleStep();
|
isolate->debugger()->SetResumeAction(Debugger::kStepInto);
|
||||||
}
|
}
|
||||||
const Object& result =
|
const Object& result =
|
||||||
Object::Handle(zone, DartEntry::InvokeFunction(function, args));
|
Object::Handle(zone, DartEntry::InvokeFunction(function, args));
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include "include/dart_api.h"
|
#include "include/dart_api.h"
|
||||||
|
|
||||||
|
#include "platform/address_sanitizer.h"
|
||||||
|
|
||||||
#include "vm/code_generator.h"
|
#include "vm/code_generator.h"
|
||||||
#include "vm/code_patcher.h"
|
#include "vm/code_patcher.h"
|
||||||
#include "vm/compiler.h"
|
#include "vm/compiler.h"
|
||||||
|
@ -42,6 +44,7 @@ DEFINE_FLAG(bool,
|
||||||
trace_debugger_stacktrace,
|
trace_debugger_stacktrace,
|
||||||
false,
|
false,
|
||||||
"Trace debugger stacktrace collection");
|
"Trace debugger stacktrace collection");
|
||||||
|
DEFINE_FLAG(bool, trace_rewind, false, "Trace frame rewind");
|
||||||
DEFINE_FLAG(bool, verbose_debug, false, "Verbose debugger messages");
|
DEFINE_FLAG(bool, verbose_debug, false, "Verbose debugger messages");
|
||||||
DEFINE_FLAG(bool,
|
DEFINE_FLAG(bool,
|
||||||
steal_breakpoints,
|
steal_breakpoints,
|
||||||
|
@ -850,6 +853,24 @@ RawObject* ActivationFrame::GetStackVar(intptr_t slot_index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ActivationFrame::IsRewindable() const {
|
||||||
|
if (deopt_frame_.IsNull()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// TODO(turnidge): This is conservative. It looks at all values in
|
||||||
|
// the deopt_frame_ even though some of them may correspond to other
|
||||||
|
// inlined frames.
|
||||||
|
Object& obj = Object::Handle();
|
||||||
|
for (int i = 0; i < deopt_frame_.Length(); i++) {
|
||||||
|
obj = deopt_frame_.At(i);
|
||||||
|
if (obj.raw() == Symbols::OptimizedOut().raw()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ActivationFrame::PrintContextMismatchError(intptr_t ctx_slot,
|
void ActivationFrame::PrintContextMismatchError(intptr_t ctx_slot,
|
||||||
intptr_t frame_ctx_level,
|
intptr_t frame_ctx_level,
|
||||||
intptr_t var_ctx_level) {
|
intptr_t var_ctx_level) {
|
||||||
|
@ -1108,9 +1129,13 @@ void ActivationFrame::PrintToJSONObject(JSONObject* jsobj, bool full) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool IsFunctionVisible(const Function& function) {
|
||||||
|
return FLAG_show_invisible_frames || function.is_visible();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void DebuggerStackTrace::AddActivation(ActivationFrame* frame) {
|
void DebuggerStackTrace::AddActivation(ActivationFrame* frame) {
|
||||||
if (FLAG_show_invisible_frames || frame->function().is_visible()) {
|
if (IsFunctionVisible(frame->function())) {
|
||||||
trace_.Add(frame);
|
trace_.Add(frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1237,6 +1262,8 @@ Debugger::Debugger()
|
||||||
breakpoint_locations_(NULL),
|
breakpoint_locations_(NULL),
|
||||||
code_breakpoints_(NULL),
|
code_breakpoints_(NULL),
|
||||||
resume_action_(kContinue),
|
resume_action_(kContinue),
|
||||||
|
resume_frame_index_(-1),
|
||||||
|
post_deopt_frame_index_(-1),
|
||||||
ignore_breakpoints_(false),
|
ignore_breakpoints_(false),
|
||||||
pause_event_(NULL),
|
pause_event_(NULL),
|
||||||
obj_cache_(NULL),
|
obj_cache_(NULL),
|
||||||
|
@ -1300,10 +1327,13 @@ static RawFunction* ResolveLibraryFunction(const Library& library,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Debugger::SetupStepOverAsyncSuspension() {
|
bool Debugger::SetupStepOverAsyncSuspension(const char** error) {
|
||||||
ActivationFrame* top_frame = TopDartFrame();
|
ActivationFrame* top_frame = TopDartFrame();
|
||||||
if (!IsAtAsyncJump(top_frame)) {
|
if (!IsAtAsyncJump(top_frame)) {
|
||||||
// Not at an async operation.
|
// Not at an async operation.
|
||||||
|
if (error) {
|
||||||
|
*error = "Isolate must be paused at an async suspension point";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Object& closure = Object::Handle(top_frame->GetAsyncOperation());
|
Object& closure = Object::Handle(top_frame->GetAsyncOperation());
|
||||||
|
@ -1313,24 +1343,42 @@ bool Debugger::SetupStepOverAsyncSuspension() {
|
||||||
Breakpoint* bpt = SetBreakpointAtActivation(Instance::Cast(closure), true);
|
Breakpoint* bpt = SetBreakpointAtActivation(Instance::Cast(closure), true);
|
||||||
if (bpt == NULL) {
|
if (bpt == NULL) {
|
||||||
// Unable to set the breakpoint.
|
// Unable to set the breakpoint.
|
||||||
|
if (error) {
|
||||||
|
*error = "Unable to set breakpoint at async suspension point";
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Debugger::SetSingleStep() {
|
bool Debugger::SetResumeAction(ResumeAction action,
|
||||||
resume_action_ = kSingleStep;
|
intptr_t frame_index,
|
||||||
}
|
const char** error) {
|
||||||
|
if (error) {
|
||||||
|
*error = NULL;
|
||||||
void Debugger::SetStepOver() {
|
}
|
||||||
resume_action_ = kStepOver;
|
resume_frame_index_ = -1;
|
||||||
}
|
switch (action) {
|
||||||
|
case kStepInto:
|
||||||
|
case kStepOver:
|
||||||
void Debugger::SetStepOut() {
|
case kStepOut:
|
||||||
resume_action_ = kStepOut;
|
case kContinue:
|
||||||
|
resume_action_ = action;
|
||||||
|
return true;
|
||||||
|
case kStepRewind:
|
||||||
|
if (!CanRewindFrame(frame_index, error)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
resume_action_ = kStepRewind;
|
||||||
|
resume_frame_index_ = frame_index;
|
||||||
|
return true;
|
||||||
|
case kStepOverAsyncSuspension:
|
||||||
|
return SetupStepOverAsyncSuspension(error);
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1622,6 +1670,7 @@ void Debugger::PauseException(const Instance& exc) {
|
||||||
ASSERT(stack_trace_ == NULL);
|
ASSERT(stack_trace_ == NULL);
|
||||||
stack_trace_ = stack_trace;
|
stack_trace_ = stack_trace;
|
||||||
Pause(&event);
|
Pause(&event);
|
||||||
|
HandleSteppingRequest(stack_trace_); // we may get a rewind request
|
||||||
stack_trace_ = NULL;
|
stack_trace_ = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2547,7 +2596,7 @@ void Debugger::EnterSingleStepMode() {
|
||||||
void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace,
|
void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace,
|
||||||
bool skip_next_step) {
|
bool skip_next_step) {
|
||||||
stepping_fp_ = 0;
|
stepping_fp_ = 0;
|
||||||
if (resume_action_ == kSingleStep) {
|
if (resume_action_ == kStepInto) {
|
||||||
// When single stepping, we need to deoptimize because we might be
|
// When single stepping, we need to deoptimize because we might be
|
||||||
// stepping into optimized code. This happens in particular if
|
// stepping into optimized code. This happens in particular if
|
||||||
// the isolate has been interrupted, but can happen in other cases
|
// the isolate has been interrupted, but can happen in other cases
|
||||||
|
@ -2557,7 +2606,7 @@ void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace,
|
||||||
isolate_->set_single_step(true);
|
isolate_->set_single_step(true);
|
||||||
skip_next_step_ = skip_next_step;
|
skip_next_step_ = skip_next_step;
|
||||||
if (FLAG_verbose_debug) {
|
if (FLAG_verbose_debug) {
|
||||||
OS::Print("HandleSteppingRequest- kSingleStep\n");
|
OS::Print("HandleSteppingRequest- kStepInto\n");
|
||||||
}
|
}
|
||||||
} else if (resume_action_ == kStepOver) {
|
} else if (resume_action_ == kStepOver) {
|
||||||
DeoptimizeWorld();
|
DeoptimizeWorld();
|
||||||
|
@ -2582,6 +2631,244 @@ void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace,
|
||||||
if (FLAG_verbose_debug) {
|
if (FLAG_verbose_debug) {
|
||||||
OS::Print("HandleSteppingRequest- kStepOut %" Px "\n", stepping_fp_);
|
OS::Print("HandleSteppingRequest- kStepOut %" Px "\n", stepping_fp_);
|
||||||
}
|
}
|
||||||
|
} else if (resume_action_ == kStepRewind) {
|
||||||
|
if (FLAG_trace_rewind) {
|
||||||
|
OS::PrintErr("Rewinding to frame %" Pd "\n", resume_frame_index_);
|
||||||
|
OS::PrintErr(
|
||||||
|
"-------------------------\n"
|
||||||
|
"All frames...\n\n");
|
||||||
|
StackFrameIterator iterator(false);
|
||||||
|
StackFrame* frame = iterator.NextFrame();
|
||||||
|
intptr_t num = 0;
|
||||||
|
while ((frame != NULL)) {
|
||||||
|
OS::PrintErr("#%04" Pd " %s\n", num++, frame->ToCString());
|
||||||
|
frame = iterator.NextFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RewindToFrame(resume_frame_index_);
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static intptr_t FindNextRewindFrameIndex(DebuggerStackTrace* stack,
|
||||||
|
intptr_t frame_index) {
|
||||||
|
for (intptr_t i = frame_index + 1; i < stack->Length(); i++) {
|
||||||
|
ActivationFrame* frame = stack->FrameAt(i);
|
||||||
|
if (frame->IsRewindable()) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Can the top frame be rewound?
|
||||||
|
bool Debugger::CanRewindFrame(intptr_t frame_index, const char** error) const {
|
||||||
|
// check rewind pc is found
|
||||||
|
DebuggerStackTrace* stack = Isolate::Current()->debugger()->StackTrace();
|
||||||
|
intptr_t num_frames = stack->Length();
|
||||||
|
if (frame_index < 1 || frame_index >= num_frames) {
|
||||||
|
if (error) {
|
||||||
|
*error = Thread::Current()->zone()->PrintToString(
|
||||||
|
"Frame must be in bounds [1..%" Pd
|
||||||
|
"]: "
|
||||||
|
"saw %" Pd "",
|
||||||
|
num_frames - 1, frame_index);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ActivationFrame* frame = stack->FrameAt(frame_index);
|
||||||
|
if (!frame->IsRewindable()) {
|
||||||
|
intptr_t next_index = FindNextRewindFrameIndex(stack, frame_index);
|
||||||
|
if (next_index > 0) {
|
||||||
|
*error = Thread::Current()->zone()->PrintToString(
|
||||||
|
"Cannot rewind to frame %" Pd
|
||||||
|
" due to conflicting compiler "
|
||||||
|
"optimizations. "
|
||||||
|
"Run the vm with --no-prune-dead-locals to disallow these "
|
||||||
|
"optimizations. "
|
||||||
|
"Next valid rewind frame is %" Pd ".",
|
||||||
|
frame_index, next_index);
|
||||||
|
} else {
|
||||||
|
*error = Thread::Current()->zone()->PrintToString(
|
||||||
|
"Cannot rewind to frame %" Pd
|
||||||
|
" due to conflicting compiler "
|
||||||
|
"optimizations. "
|
||||||
|
"Run the vm with --no-prune-dead-locals to disallow these "
|
||||||
|
"optimizations.",
|
||||||
|
frame_index);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Given a return address pc, find the "rewind" pc, which is the pc
|
||||||
|
// before the corresponding call.
|
||||||
|
static uword LookupRewindPc(const Code& code, uword pc) {
|
||||||
|
ASSERT(!code.is_optimized());
|
||||||
|
ASSERT(code.ContainsInstructionAt(pc));
|
||||||
|
|
||||||
|
uword pc_offset = pc - code.PayloadStart();
|
||||||
|
const PcDescriptors& descriptors =
|
||||||
|
PcDescriptors::Handle(code.pc_descriptors());
|
||||||
|
PcDescriptors::Iterator iter(
|
||||||
|
descriptors, RawPcDescriptors::kRewind | RawPcDescriptors::kIcCall |
|
||||||
|
RawPcDescriptors::kUnoptStaticCall);
|
||||||
|
intptr_t rewind_deopt_id = -1;
|
||||||
|
uword rewind_pc = 0;
|
||||||
|
while (iter.MoveNext()) {
|
||||||
|
if (iter.Kind() == RawPcDescriptors::kRewind) {
|
||||||
|
// Remember the last rewind so we don't need to iterator twice.
|
||||||
|
rewind_pc = code.PayloadStart() + iter.PcOffset();
|
||||||
|
rewind_deopt_id = iter.DeoptId();
|
||||||
|
}
|
||||||
|
if ((pc_offset == iter.PcOffset()) && (iter.DeoptId() == rewind_deopt_id)) {
|
||||||
|
return rewind_pc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Debugger::RewindToFrame(intptr_t frame_index) {
|
||||||
|
Thread* thread = Thread::Current();
|
||||||
|
Zone* zone = thread->zone();
|
||||||
|
Code& code = Code::Handle(zone);
|
||||||
|
Function& function = Function::Handle(zone);
|
||||||
|
|
||||||
|
// Find the requested frame.
|
||||||
|
StackFrameIterator iterator(false);
|
||||||
|
intptr_t current_frame = 0;
|
||||||
|
for (StackFrame* frame = iterator.NextFrame(); frame != NULL;
|
||||||
|
frame = iterator.NextFrame()) {
|
||||||
|
ASSERT(frame->IsValid());
|
||||||
|
if (frame->IsDartFrame()) {
|
||||||
|
code = frame->LookupDartCode();
|
||||||
|
function = code.function();
|
||||||
|
if (!IsFunctionVisible(function)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (code.is_optimized()) {
|
||||||
|
intptr_t sub_index = 0;
|
||||||
|
for (InlinedFunctionsIterator it(code, frame->pc()); !it.Done();
|
||||||
|
it.Advance()) {
|
||||||
|
if (current_frame == frame_index) {
|
||||||
|
RewindToOptimizedFrame(frame, code, sub_index);
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
current_frame++;
|
||||||
|
sub_index++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (current_frame == frame_index) {
|
||||||
|
// We are rewinding to an unoptimized frame.
|
||||||
|
RewindToUnoptimizedFrame(frame, code);
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
current_frame++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Debugger::RewindToUnoptimizedFrame(StackFrame* frame, const Code& code) {
|
||||||
|
// We will be jumping out of the debugger rather than exiting this
|
||||||
|
// function, so prepare the debugger state.
|
||||||
|
stack_trace_ = NULL;
|
||||||
|
resume_action_ = kContinue;
|
||||||
|
resume_frame_index_ = -1;
|
||||||
|
EnterSingleStepMode();
|
||||||
|
|
||||||
|
uword rewind_pc = LookupRewindPc(code, frame->pc());
|
||||||
|
if (FLAG_trace_rewind && rewind_pc == 0) {
|
||||||
|
OS::PrintErr("Unable to find rewind pc for pc(%" Px ")\n", frame->pc());
|
||||||
|
}
|
||||||
|
ASSERT(rewind_pc != 0);
|
||||||
|
if (FLAG_trace_rewind) {
|
||||||
|
OS::PrintErr(
|
||||||
|
"===============================\n"
|
||||||
|
"Rewinding to unoptimized frame:\n"
|
||||||
|
" rewind_pc(0x%" Px ") sp(0x%" Px ") fp(0x%" Px
|
||||||
|
")\n"
|
||||||
|
"===============================\n",
|
||||||
|
rewind_pc, frame->sp(), frame->fp());
|
||||||
|
}
|
||||||
|
Exceptions::JumpToFrame(Thread::Current(), rewind_pc, frame->sp(),
|
||||||
|
frame->fp(), true /* clear lazy deopt at target */);
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Debugger::RewindToOptimizedFrame(StackFrame* frame,
|
||||||
|
const Code& optimized_code,
|
||||||
|
intptr_t sub_index) {
|
||||||
|
post_deopt_frame_index_ = sub_index;
|
||||||
|
|
||||||
|
// We will be jumping out of the debugger rather than exiting this
|
||||||
|
// function, so prepare the debugger state.
|
||||||
|
stack_trace_ = NULL;
|
||||||
|
resume_action_ = kContinue;
|
||||||
|
resume_frame_index_ = -1;
|
||||||
|
EnterSingleStepMode();
|
||||||
|
|
||||||
|
if (FLAG_trace_rewind) {
|
||||||
|
OS::PrintErr(
|
||||||
|
"===============================\n"
|
||||||
|
"Deoptimizing frame for rewind:\n"
|
||||||
|
" deopt_pc(0x%" Px ") sp(0x%" Px ") fp(0x%" Px
|
||||||
|
")\n"
|
||||||
|
"===============================\n",
|
||||||
|
frame->pc(), frame->sp(), frame->fp());
|
||||||
|
}
|
||||||
|
Thread* thread = Thread::Current();
|
||||||
|
thread->set_resume_pc(frame->pc());
|
||||||
|
uword deopt_stub_pc = StubCode::DeoptForRewind_entry()->EntryPoint();
|
||||||
|
Exceptions::JumpToFrame(thread, deopt_stub_pc, frame->sp(), frame->fp(),
|
||||||
|
true /* clear lazy deopt at target */);
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Debugger::RewindPostDeopt() {
|
||||||
|
intptr_t rewind_frame = post_deopt_frame_index_;
|
||||||
|
post_deopt_frame_index_ = -1;
|
||||||
|
if (FLAG_trace_rewind) {
|
||||||
|
OS::PrintErr("Post deopt, jumping to frame %" Pd "\n", rewind_frame);
|
||||||
|
OS::PrintErr(
|
||||||
|
"-------------------------\n"
|
||||||
|
"All frames...\n\n");
|
||||||
|
StackFrameIterator iterator(false);
|
||||||
|
StackFrame* frame = iterator.NextFrame();
|
||||||
|
intptr_t num = 0;
|
||||||
|
while ((frame != NULL)) {
|
||||||
|
OS::PrintErr("#%04" Pd " %s\n", num++, frame->ToCString());
|
||||||
|
frame = iterator.NextFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread* thread = Thread::Current();
|
||||||
|
Zone* zone = thread->zone();
|
||||||
|
Code& code = Code::Handle(zone);
|
||||||
|
|
||||||
|
StackFrameIterator iterator(false);
|
||||||
|
intptr_t current_frame = 0;
|
||||||
|
for (StackFrame* frame = iterator.NextFrame(); frame != NULL;
|
||||||
|
frame = iterator.NextFrame()) {
|
||||||
|
ASSERT(frame->IsValid());
|
||||||
|
if (frame->IsDartFrame()) {
|
||||||
|
code = frame->LookupDartCode();
|
||||||
|
ASSERT(!code.is_optimized());
|
||||||
|
if (current_frame == rewind_frame) {
|
||||||
|
RewindToUnoptimizedFrame(frame, code);
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
current_frame++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2793,7 +3080,7 @@ RawError* Debugger::PauseBreakpoint() {
|
||||||
|
|
||||||
// We are at the entry of an async function.
|
// We are at the entry of an async function.
|
||||||
// We issue a step over to resume at the point after the await statement.
|
// We issue a step over to resume at the point after the await statement.
|
||||||
SetStepOver();
|
SetResumeAction(kStepOver);
|
||||||
// When we single step from a user breakpoint, our next stepping
|
// When we single step from a user breakpoint, our next stepping
|
||||||
// point will be at the exact same pc. Skip it.
|
// point will be at the exact same pc. Skip it.
|
||||||
HandleSteppingRequest(stack_trace_, true /* skip next step */);
|
HandleSteppingRequest(stack_trace_, true /* skip next step */);
|
||||||
|
@ -2846,7 +3133,7 @@ void Debugger::PauseDeveloper(const String& msg) {
|
||||||
// We are in the native call to Developer_debugger. the developer
|
// We are in the native call to Developer_debugger. the developer
|
||||||
// gets a better experience by not seeing this call. To accomplish
|
// gets a better experience by not seeing this call. To accomplish
|
||||||
// this, we continue execution until the call exits (step out).
|
// this, we continue execution until the call exits (step out).
|
||||||
SetStepOut();
|
SetResumeAction(kStepOut);
|
||||||
HandleSteppingRequest(stack_trace_);
|
HandleSteppingRequest(stack_trace_);
|
||||||
|
|
||||||
stack_trace_ = NULL;
|
stack_trace_ = NULL;
|
||||||
|
|
|
@ -276,6 +276,9 @@ class ActivationFrame : public ZoneAllocated {
|
||||||
// to the user and can be debugged.
|
// to the user and can be debugged.
|
||||||
bool IsDebuggable() const;
|
bool IsDebuggable() const;
|
||||||
|
|
||||||
|
// Returns true if it is possible to rewind the debugger to this frame.
|
||||||
|
bool IsRewindable() const;
|
||||||
|
|
||||||
// The context level of a frame is the context level at the
|
// The context level of a frame is the context level at the
|
||||||
// PC/token index of the frame. It determines the depth of the context
|
// PC/token index of the frame. It determines the depth of the context
|
||||||
// chain that belongs to the function of this activation frame.
|
// chain that belongs to the function of this activation frame.
|
||||||
|
@ -373,6 +376,15 @@ class DebuggerStackTrace : public ZoneAllocated {
|
||||||
|
|
||||||
class Debugger {
|
class Debugger {
|
||||||
public:
|
public:
|
||||||
|
enum ResumeAction {
|
||||||
|
kContinue,
|
||||||
|
kStepInto,
|
||||||
|
kStepOver,
|
||||||
|
kStepOut,
|
||||||
|
kStepRewind,
|
||||||
|
kStepOverAsyncSuspension,
|
||||||
|
};
|
||||||
|
|
||||||
typedef void EventHandler(ServiceEvent* event);
|
typedef void EventHandler(ServiceEvent* event);
|
||||||
|
|
||||||
Debugger();
|
Debugger();
|
||||||
|
@ -412,11 +424,10 @@ class Debugger {
|
||||||
void RemoveBreakpoint(intptr_t bp_id);
|
void RemoveBreakpoint(intptr_t bp_id);
|
||||||
Breakpoint* GetBreakpointById(intptr_t id);
|
Breakpoint* GetBreakpointById(intptr_t id);
|
||||||
|
|
||||||
// Will return false if we are not at an await.
|
bool SetResumeAction(ResumeAction action,
|
||||||
bool SetupStepOverAsyncSuspension();
|
intptr_t frame_index = 1,
|
||||||
void SetStepOver();
|
const char** error = NULL);
|
||||||
void SetSingleStep();
|
|
||||||
void SetStepOut();
|
|
||||||
bool IsStepping() const { return resume_action_ != kContinue; }
|
bool IsStepping() const { return resume_action_ != kContinue; }
|
||||||
|
|
||||||
bool IsPaused() const { return pause_event_ != NULL; }
|
bool IsPaused() const { return pause_event_ != NULL; }
|
||||||
|
@ -513,11 +524,15 @@ class Debugger {
|
||||||
|
|
||||||
intptr_t limitBreakpointId() { return next_id_; }
|
intptr_t limitBreakpointId() { return next_id_; }
|
||||||
|
|
||||||
private:
|
// Callback to the debugger to continue frame rewind, post-deoptimization.
|
||||||
enum ResumeAction { kContinue, kStepOver, kStepOut, kSingleStep };
|
void RewindPostDeopt();
|
||||||
|
|
||||||
|
private:
|
||||||
RawError* PauseRequest(ServiceEvent::EventKind kind);
|
RawError* PauseRequest(ServiceEvent::EventKind kind);
|
||||||
|
|
||||||
|
// Will return false if we are not at an await.
|
||||||
|
bool SetupStepOverAsyncSuspension(const char** error);
|
||||||
|
|
||||||
bool NeedsIsolateEvents();
|
bool NeedsIsolateEvents();
|
||||||
bool NeedsDebugEvents();
|
bool NeedsDebugEvents();
|
||||||
void InvokeEventHandler(ServiceEvent* event);
|
void InvokeEventHandler(ServiceEvent* event);
|
||||||
|
@ -588,6 +603,15 @@ class Debugger {
|
||||||
void HandleSteppingRequest(DebuggerStackTrace* stack_trace,
|
void HandleSteppingRequest(DebuggerStackTrace* stack_trace,
|
||||||
bool skip_next_step = false);
|
bool skip_next_step = false);
|
||||||
|
|
||||||
|
// Can we rewind to the indicated frame?
|
||||||
|
bool CanRewindFrame(intptr_t frame_index, const char** error) const;
|
||||||
|
|
||||||
|
void RewindToFrame(intptr_t frame_index);
|
||||||
|
void RewindToUnoptimizedFrame(StackFrame* frame, const Code& code);
|
||||||
|
void RewindToOptimizedFrame(StackFrame* frame,
|
||||||
|
const Code& code,
|
||||||
|
intptr_t post_deopt_frame_index);
|
||||||
|
|
||||||
Isolate* isolate_;
|
Isolate* isolate_;
|
||||||
Dart_Port isolate_id_; // A unique ID for the isolate in the debugger.
|
Dart_Port isolate_id_; // A unique ID for the isolate in the debugger.
|
||||||
bool initialized_;
|
bool initialized_;
|
||||||
|
@ -601,6 +625,8 @@ class Debugger {
|
||||||
|
|
||||||
// Tells debugger what to do when resuming execution after a breakpoint.
|
// Tells debugger what to do when resuming execution after a breakpoint.
|
||||||
ResumeAction resume_action_;
|
ResumeAction resume_action_;
|
||||||
|
intptr_t resume_frame_index_;
|
||||||
|
intptr_t post_deopt_frame_index_;
|
||||||
|
|
||||||
// Do not call back to breakpoint handler if this flag is set.
|
// Do not call back to breakpoint handler if this flag is set.
|
||||||
// Effectively this means ignoring breakpoints. Set when Dart code may
|
// Effectively this means ignoring breakpoints. Set when Dart code may
|
||||||
|
|
|
@ -485,7 +485,7 @@ DART_EXPORT Dart_Handle Dart_SetStepOver() {
|
||||||
DARTSCOPE(Thread::Current());
|
DARTSCOPE(Thread::Current());
|
||||||
Isolate* I = T->isolate();
|
Isolate* I = T->isolate();
|
||||||
CHECK_DEBUGGER(I);
|
CHECK_DEBUGGER(I);
|
||||||
I->debugger()->SetStepOver();
|
I->debugger()->SetResumeAction(Debugger::kStepOver);
|
||||||
return Api::Success();
|
return Api::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,7 +494,7 @@ DART_EXPORT Dart_Handle Dart_SetStepInto() {
|
||||||
DARTSCOPE(Thread::Current());
|
DARTSCOPE(Thread::Current());
|
||||||
Isolate* I = T->isolate();
|
Isolate* I = T->isolate();
|
||||||
CHECK_DEBUGGER(I);
|
CHECK_DEBUGGER(I);
|
||||||
I->debugger()->SetSingleStep();
|
I->debugger()->SetResumeAction(Debugger::kStepInto);
|
||||||
return Api::Success();
|
return Api::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,7 +503,7 @@ DART_EXPORT Dart_Handle Dart_SetStepOut() {
|
||||||
DARTSCOPE(Thread::Current());
|
DARTSCOPE(Thread::Current());
|
||||||
Isolate* I = T->isolate();
|
Isolate* I = T->isolate();
|
||||||
CHECK_DEBUGGER(I);
|
CHECK_DEBUGGER(I);
|
||||||
I->debugger()->SetStepOut();
|
I->debugger()->SetResumeAction(Debugger::kStepOut);
|
||||||
return Api::Success();
|
return Api::Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -189,7 +189,6 @@ static bool FindExceptionHandler(Thread* thread,
|
||||||
static void FindErrorHandler(uword* handler_pc,
|
static void FindErrorHandler(uword* handler_pc,
|
||||||
uword* handler_sp,
|
uword* handler_sp,
|
||||||
uword* handler_fp) {
|
uword* handler_fp) {
|
||||||
// TODO(turnidge): Is there a faster way to get the next entry frame?
|
|
||||||
StackFrameIterator frames(StackFrameIterator::kDontValidateFrames);
|
StackFrameIterator frames(StackFrameIterator::kDontValidateFrames);
|
||||||
StackFrame* frame = frames.NextFrame();
|
StackFrame* frame = frames.NextFrame();
|
||||||
ASSERT(frame != NULL);
|
ASSERT(frame != NULL);
|
||||||
|
@ -228,7 +227,17 @@ static uword RemapExceptionPCForDeopt(Thread* thread,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
#endif // !DBC
|
||||||
|
return program_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ClearLazyDeopts(Thread* thread, uword frame_pointer) {
|
||||||
|
#if !defined(TARGET_ARCH_DBC)
|
||||||
|
MallocGrowableArray<PendingLazyDeopt>* pending_deopts =
|
||||||
|
thread->isolate()->pending_deopts();
|
||||||
|
if (pending_deopts->length() > 0) {
|
||||||
// We may be jumping over frames scheduled for lazy deopt. Remove these
|
// We may be jumping over frames scheduled for lazy deopt. Remove these
|
||||||
// frames from the pending deopt table, but only after unmarking them so
|
// frames from the pending deopt table, but only after unmarking them so
|
||||||
// any stack walk that happens before the stack is unwound will still work.
|
// any stack walk that happens before the stack is unwound will still work.
|
||||||
|
@ -264,7 +273,6 @@ static uword RemapExceptionPCForDeopt(Thread* thread,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif // !DBC
|
#endif // !DBC
|
||||||
return program_counter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -281,14 +289,18 @@ static void JumpToExceptionHandler(Thread* thread,
|
||||||
thread->set_resume_pc(remapped_pc);
|
thread->set_resume_pc(remapped_pc);
|
||||||
uword run_exception_pc = StubCode::RunExceptionHandler_entry()->EntryPoint();
|
uword run_exception_pc = StubCode::RunExceptionHandler_entry()->EntryPoint();
|
||||||
Exceptions::JumpToFrame(thread, run_exception_pc, stack_pointer,
|
Exceptions::JumpToFrame(thread, run_exception_pc, stack_pointer,
|
||||||
frame_pointer);
|
frame_pointer, false /* do not clear deopt */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Exceptions::JumpToFrame(Thread* thread,
|
void Exceptions::JumpToFrame(Thread* thread,
|
||||||
uword program_counter,
|
uword program_counter,
|
||||||
uword stack_pointer,
|
uword stack_pointer,
|
||||||
uword frame_pointer) {
|
uword frame_pointer,
|
||||||
|
bool clear_deopt_at_target) {
|
||||||
|
uword fp_for_clearing =
|
||||||
|
(clear_deopt_at_target ? frame_pointer + 1 : frame_pointer);
|
||||||
|
ClearLazyDeopts(thread, fp_for_clearing);
|
||||||
#if defined(USING_SIMULATOR)
|
#if defined(USING_SIMULATOR)
|
||||||
// Unwinding of the C++ frames and destroying of their stack resources is done
|
// Unwinding of the C++ frames and destroying of their stack resources is done
|
||||||
// by the simulator, because the target stack_pointer is a simulated stack
|
// by the simulator, because the target stack_pointer is a simulated stack
|
||||||
|
|
|
@ -83,7 +83,8 @@ class Exceptions : AllStatic {
|
||||||
static void JumpToFrame(Thread* thread,
|
static void JumpToFrame(Thread* thread,
|
||||||
uword program_counter,
|
uword program_counter,
|
||||||
uword stack_pointer,
|
uword stack_pointer,
|
||||||
uword frame_pointer);
|
uword frame_pointer,
|
||||||
|
bool clear_deopt_at_target);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_COPY_AND_ASSIGN(Exceptions);
|
DISALLOW_COPY_AND_ASSIGN(Exceptions);
|
||||||
|
|
|
@ -1271,6 +1271,7 @@ void FlowGraphCompiler::GenerateStaticCall(intptr_t deopt_id,
|
||||||
kNumArgsChecked)
|
kNumArgsChecked)
|
||||||
->raw();
|
->raw();
|
||||||
}
|
}
|
||||||
|
AddCurrentDescriptor(RawPcDescriptors::kRewind, deopt_id, token_pos);
|
||||||
EmitUnoptimizedStaticCall(argument_count, deopt_id, token_pos, locs,
|
EmitUnoptimizedStaticCall(argument_count, deopt_id, token_pos, locs,
|
||||||
call_ic_data);
|
call_ic_data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3075,6 +3075,8 @@ void InstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||||
} else {
|
} else {
|
||||||
// Unoptimized code.
|
// Unoptimized code.
|
||||||
ASSERT(!HasICData());
|
ASSERT(!HasICData());
|
||||||
|
compiler->AddCurrentDescriptor(RawPcDescriptors::kRewind, deopt_id(),
|
||||||
|
token_pos());
|
||||||
bool is_smi_two_args_op = false;
|
bool is_smi_two_args_op = false;
|
||||||
const StubEntry* stub_entry = TwoArgsSmiOpInlineCacheEntry(token_kind());
|
const StubEntry* stub_entry = TwoArgsSmiOpInlineCacheEntry(token_kind());
|
||||||
if (stub_entry != NULL) {
|
if (stub_entry != NULL) {
|
||||||
|
|
|
@ -139,6 +139,8 @@ static const char* GetJSONRpcErrorMessage(intptr_t code) {
|
||||||
return "Isolate must be runnable";
|
return "Isolate must be runnable";
|
||||||
case kIsolateMustBePaused:
|
case kIsolateMustBePaused:
|
||||||
return "Isolate must be paused";
|
return "Isolate must be paused";
|
||||||
|
case kCannotResume:
|
||||||
|
return "Cannot resume execution";
|
||||||
case kIsolateIsReloading:
|
case kIsolateIsReloading:
|
||||||
return "Isolate is reloading";
|
return "Isolate is reloading";
|
||||||
case kFileSystemAlreadyExists:
|
case kFileSystemAlreadyExists:
|
||||||
|
|
|
@ -53,6 +53,7 @@ enum JSONRpcErrorCode {
|
||||||
kStreamNotSubscribed = 104,
|
kStreamNotSubscribed = 104,
|
||||||
kIsolateMustBeRunnable = 105,
|
kIsolateMustBeRunnable = 105,
|
||||||
kIsolateMustBePaused = 106,
|
kIsolateMustBePaused = 106,
|
||||||
|
kCannotResume = 107,
|
||||||
|
|
||||||
// Experimental (used in private rpcs).
|
// Experimental (used in private rpcs).
|
||||||
kIsolateIsReloading = 1000,
|
kIsolateIsReloading = 1000,
|
||||||
|
|
|
@ -11700,6 +11700,8 @@ const char* PcDescriptors::KindAsStr(RawPcDescriptors::Kind kind) {
|
||||||
return "runtime-call ";
|
return "runtime-call ";
|
||||||
case RawPcDescriptors::kOsrEntry:
|
case RawPcDescriptors::kOsrEntry:
|
||||||
return "osr-entry ";
|
return "osr-entry ";
|
||||||
|
case RawPcDescriptors::kRewind:
|
||||||
|
return "rewind ";
|
||||||
case RawPcDescriptors::kOther:
|
case RawPcDescriptors::kOther:
|
||||||
return "other ";
|
return "other ";
|
||||||
case RawPcDescriptors::kAnyKind:
|
case RawPcDescriptors::kAnyKind:
|
||||||
|
|
|
@ -1202,7 +1202,8 @@ class RawPcDescriptors : public RawObject {
|
||||||
kUnoptStaticCall = kIcCall << 1, // Call to a known target via stub.
|
kUnoptStaticCall = kIcCall << 1, // Call to a known target via stub.
|
||||||
kRuntimeCall = kUnoptStaticCall << 1, // Runtime call.
|
kRuntimeCall = kUnoptStaticCall << 1, // Runtime call.
|
||||||
kOsrEntry = kRuntimeCall << 1, // OSR entry point in unopt. code.
|
kOsrEntry = kRuntimeCall << 1, // OSR entry point in unopt. code.
|
||||||
kOther = kOsrEntry << 1,
|
kRewind = kOsrEntry << 1, // Call rewind target address.
|
||||||
|
kOther = kRewind << 1,
|
||||||
kLastKind = kOther,
|
kLastKind = kOther,
|
||||||
kAnyKind = -1
|
kAnyKind = -1
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,6 +40,7 @@ namespace dart {
|
||||||
V(TraceFunctionEntry) \
|
V(TraceFunctionEntry) \
|
||||||
V(TraceFunctionExit) \
|
V(TraceFunctionExit) \
|
||||||
V(DeoptimizeMaterialize) \
|
V(DeoptimizeMaterialize) \
|
||||||
|
V(RewindPostDeopt) \
|
||||||
V(UpdateFieldCid) \
|
V(UpdateFieldCid) \
|
||||||
V(InitStaticField) \
|
V(InitStaticField) \
|
||||||
V(GrowRegExpStack) \
|
V(GrowRegExpStack) \
|
||||||
|
|
|
@ -2988,18 +2988,57 @@ static bool GetVMTimeline(Thread* thread, JSONStream* js) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char* const step_enum_names[] = {
|
||||||
|
"None", "Into", "Over", "Out", "Rewind", "OverAsyncSuspension", NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const Debugger::ResumeAction step_enum_values[] = {
|
||||||
|
Debugger::kContinue, Debugger::kStepInto,
|
||||||
|
Debugger::kStepOver, Debugger::kStepOut,
|
||||||
|
Debugger::kStepRewind, Debugger::kStepOverAsyncSuspension,
|
||||||
|
Debugger::kContinue, // Default value
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static const MethodParameter* resume_params[] = {
|
static const MethodParameter* resume_params[] = {
|
||||||
RUNNABLE_ISOLATE_PARAMETER, NULL,
|
RUNNABLE_ISOLATE_PARAMETER,
|
||||||
|
new EnumParameter("step", false, step_enum_names),
|
||||||
|
new UIntParameter("frameIndex", false), NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static bool Resume(Thread* thread, JSONStream* js) {
|
static bool Resume(Thread* thread, JSONStream* js) {
|
||||||
const char* step_param = js->LookupParam("step");
|
const char* step_param = js->LookupParam("step");
|
||||||
|
Debugger::ResumeAction step = Debugger::kContinue;
|
||||||
|
if (step_param != NULL) {
|
||||||
|
step = EnumMapper(step_param, step_enum_names, step_enum_values);
|
||||||
|
}
|
||||||
|
#if defined(TARGET_ARCH_DBC)
|
||||||
|
if (step == Debugger::kStepRewind) {
|
||||||
|
js->PrintError(kCannotResume,
|
||||||
|
"Rewind not yet implemented on this architecture");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
intptr_t frame_index = 1;
|
||||||
|
const char* frame_index_param = js->LookupParam("frameIndex");
|
||||||
|
if (frame_index_param != NULL) {
|
||||||
|
if (step != Debugger::kStepRewind) {
|
||||||
|
// Only rewind supports the frameIndex parameter.
|
||||||
|
js->PrintError(
|
||||||
|
kInvalidParams,
|
||||||
|
"%s: the 'frameIndex' parameter can only be used when rewinding",
|
||||||
|
js->method());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
frame_index = UIntParameter::Parse(js->LookupParam("frameIndex"));
|
||||||
|
}
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
if (isolate->message_handler()->is_paused_on_start()) {
|
if (isolate->message_handler()->is_paused_on_start()) {
|
||||||
// If the user is issuing a 'Over' or an 'Out' step, that is the
|
// If the user is issuing a 'Over' or an 'Out' step, that is the
|
||||||
// same as a regular resume request.
|
// same as a regular resume request.
|
||||||
if ((step_param != NULL) && (strcmp(step_param, "Into") == 0)) {
|
if (step == Debugger::kStepInto) {
|
||||||
isolate->debugger()->EnterSingleStepMode();
|
isolate->debugger()->EnterSingleStepMode();
|
||||||
}
|
}
|
||||||
isolate->message_handler()->set_should_pause_on_start(false);
|
isolate->message_handler()->set_should_pause_on_start(false);
|
||||||
|
@ -3028,31 +3067,18 @@ static bool Resume(Thread* thread, JSONStream* js) {
|
||||||
PrintSuccess(js);
|
PrintSuccess(js);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (isolate->debugger()->PauseEvent() != NULL) {
|
if (isolate->debugger()->PauseEvent() == NULL) {
|
||||||
if (step_param != NULL) {
|
js->PrintError(kIsolateMustBePaused, NULL);
|
||||||
if (strcmp(step_param, "Into") == 0) {
|
|
||||||
isolate->debugger()->SetSingleStep();
|
|
||||||
} else if (strcmp(step_param, "Over") == 0) {
|
|
||||||
isolate->debugger()->SetStepOver();
|
|
||||||
} else if (strcmp(step_param, "Out") == 0) {
|
|
||||||
isolate->debugger()->SetStepOut();
|
|
||||||
} else if (strcmp(step_param, "OverAsyncSuspension") == 0) {
|
|
||||||
if (!isolate->debugger()->SetupStepOverAsyncSuspension()) {
|
|
||||||
js->PrintError(kInvalidParams,
|
|
||||||
"Isolate must be paused at an async suspension point");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PrintInvalidParamError(js, "step");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isolate->SetResumeRequest();
|
|
||||||
PrintSuccess(js);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
js->PrintError(kIsolateMustBePaused, NULL);
|
const char* error = NULL;
|
||||||
|
if (!isolate->debugger()->SetResumeAction(step, frame_index, &error)) {
|
||||||
|
js->PrintError(kCannotResume, error);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
isolate->SetResumeRequest();
|
||||||
|
PrintSuccess(js);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3715,14 +3715,25 @@ void Simulator::JumpToFrame(uword pc, uword sp, uword fp, Thread* thread) {
|
||||||
fp_ = reinterpret_cast<RawObject**>(fp);
|
fp_ = reinterpret_cast<RawObject**>(fp);
|
||||||
|
|
||||||
if (pc == StubCode::RunExceptionHandler_entry()->EntryPoint()) {
|
if (pc == StubCode::RunExceptionHandler_entry()->EntryPoint()) {
|
||||||
// Instead of executing the RunException stub, we implement its
|
// The RunExceptionHandler stub is a placeholder. We implement
|
||||||
// behavior here.
|
// its behavior here.
|
||||||
RawObject* raw_exception = thread->active_exception();
|
RawObject* raw_exception = thread->active_exception();
|
||||||
RawObject* raw_stacktrace = thread->active_stacktrace();
|
RawObject* raw_stacktrace = thread->active_stacktrace();
|
||||||
ASSERT(raw_exception != Object::null());
|
ASSERT(raw_exception != Object::null());
|
||||||
special_[kExceptionSpecialIndex] = raw_exception;
|
special_[kExceptionSpecialIndex] = raw_exception;
|
||||||
special_[kStacktraceSpecialIndex] = raw_stacktrace;
|
special_[kStacktraceSpecialIndex] = raw_stacktrace;
|
||||||
pc_ = thread->resume_pc();
|
pc_ = thread->resume_pc();
|
||||||
|
} else if (pc == StubCode::DeoptForRewind_entry()->EntryPoint()) {
|
||||||
|
// The DeoptForRewind stub is a placeholder. We will eventually
|
||||||
|
// implement its behavior here.
|
||||||
|
//
|
||||||
|
// TODO(turnidge): Refactor the Deopt bytecode so that we can use
|
||||||
|
// the implementation here too. The deopt pc is stored in
|
||||||
|
// Thread::resume_pc(). After invoking deoptimization, we usually
|
||||||
|
// call into Debugger::RewindPostDeopt(), but I need to figure out
|
||||||
|
// if that makes any sense (it would JumpToFrame during a
|
||||||
|
// JumpToFrame, which seems wrong).
|
||||||
|
UNIMPLEMENTED();
|
||||||
} else {
|
} else {
|
||||||
pc_ = pc;
|
pc_ = pc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ class Deserializer;
|
||||||
V(GetStackPointer) \
|
V(GetStackPointer) \
|
||||||
V(JumpToFrame) \
|
V(JumpToFrame) \
|
||||||
V(RunExceptionHandler) \
|
V(RunExceptionHandler) \
|
||||||
|
V(DeoptForRewind) \
|
||||||
V(UpdateStoreBuffer) \
|
V(UpdateStoreBuffer) \
|
||||||
V(PrintStopMessage) \
|
V(PrintStopMessage) \
|
||||||
V(CallToRuntime) \
|
V(CallToRuntime) \
|
||||||
|
@ -75,6 +76,7 @@ class Deserializer;
|
||||||
V(LazyCompile) \
|
V(LazyCompile) \
|
||||||
V(OptimizeFunction) \
|
V(OptimizeFunction) \
|
||||||
V(RunExceptionHandler) \
|
V(RunExceptionHandler) \
|
||||||
|
V(DeoptForRewind) \
|
||||||
V(FixCallersTarget) \
|
V(FixCallersTarget) \
|
||||||
V(Deoptimize) \
|
V(Deoptimize) \
|
||||||
V(DeoptimizeLazyFromReturn) \
|
V(DeoptimizeLazyFromReturn) \
|
||||||
|
@ -195,6 +197,12 @@ class StubCode : public AllStatic {
|
||||||
|
|
||||||
enum DeoptStubKind { kLazyDeoptFromReturn, kLazyDeoptFromThrow, kEagerDeopt };
|
enum DeoptStubKind { kLazyDeoptFromReturn, kLazyDeoptFromThrow, kEagerDeopt };
|
||||||
|
|
||||||
|
// Zap value used to indicate unused CODE_REG in deopt.
|
||||||
|
static const uword kZapCodeReg = 0xf1f1f1f1;
|
||||||
|
|
||||||
|
// Zap value used to indicate unused return address in deopt.
|
||||||
|
static const uword kZapReturnAddress = 0xe1e1e1e1;
|
||||||
|
|
||||||
} // namespace dart
|
} // namespace dart
|
||||||
|
|
||||||
#endif // RUNTIME_VM_STUB_CODE_H_
|
#endif // RUNTIME_VM_STUB_CODE_H_
|
||||||
|
|
|
@ -537,19 +537,20 @@ static void GenerateDeoptimizationSequence(Assembler* assembler,
|
||||||
__ LeaveStubFrame();
|
__ LeaveStubFrame();
|
||||||
// Remove materialization arguments.
|
// Remove materialization arguments.
|
||||||
__ add(SP, SP, Operand(R2, ASR, kSmiTagSize));
|
__ add(SP, SP, Operand(R2, ASR, kSmiTagSize));
|
||||||
__ Ret();
|
// The caller is responsible for emitting the return instruction.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// R0: result, must be preserved
|
// R0: result, must be preserved
|
||||||
void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
||||||
// Push zap value instead of CODE_REG for lazy deopt.
|
// Push zap value instead of CODE_REG for lazy deopt.
|
||||||
__ LoadImmediate(IP, 0xf1f1f1f1);
|
__ LoadImmediate(IP, kZapCodeReg);
|
||||||
__ Push(IP);
|
__ Push(IP);
|
||||||
// Return address for "call" to deopt stub.
|
// Return address for "call" to deopt stub.
|
||||||
__ LoadImmediate(LR, 0xe1e1e1e1);
|
__ LoadImmediate(LR, kZapReturnAddress);
|
||||||
__ ldr(CODE_REG, Address(THR, Thread::lazy_deopt_from_return_stub_offset()));
|
__ ldr(CODE_REG, Address(THR, Thread::lazy_deopt_from_return_stub_offset()));
|
||||||
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromReturn);
|
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromReturn);
|
||||||
|
__ Ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -557,17 +558,19 @@ void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
||||||
// R1: stacktrace, must be preserved
|
// R1: stacktrace, must be preserved
|
||||||
void StubCode::GenerateDeoptimizeLazyFromThrowStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeLazyFromThrowStub(Assembler* assembler) {
|
||||||
// Push zap value instead of CODE_REG for lazy deopt.
|
// Push zap value instead of CODE_REG for lazy deopt.
|
||||||
__ LoadImmediate(IP, 0xf1f1f1f1);
|
__ LoadImmediate(IP, kZapCodeReg);
|
||||||
__ Push(IP);
|
__ Push(IP);
|
||||||
// Return address for "call" to deopt stub.
|
// Return address for "call" to deopt stub.
|
||||||
__ LoadImmediate(LR, 0xe1e1e1e1);
|
__ LoadImmediate(LR, kZapReturnAddress);
|
||||||
__ ldr(CODE_REG, Address(THR, Thread::lazy_deopt_from_throw_stub_offset()));
|
__ ldr(CODE_REG, Address(THR, Thread::lazy_deopt_from_throw_stub_offset()));
|
||||||
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromThrow);
|
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromThrow);
|
||||||
|
__ Ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void StubCode::GenerateDeoptimizeStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeStub(Assembler* assembler) {
|
||||||
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
||||||
|
__ Ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1899,6 +1902,26 @@ void StubCode::GenerateRunExceptionHandlerStub(Assembler* assembler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Deoptimize a frame on the call stack before rewinding.
|
||||||
|
// The arguments are stored in the Thread object.
|
||||||
|
// No result.
|
||||||
|
void StubCode::GenerateDeoptForRewindStub(Assembler* assembler) {
|
||||||
|
// Push zap value instead of CODE_REG.
|
||||||
|
__ LoadImmediate(IP, kZapCodeReg);
|
||||||
|
__ Push(IP);
|
||||||
|
|
||||||
|
// Load the deopt pc into LR.
|
||||||
|
__ LoadFromOffset(kWord, LR, THR, Thread::resume_pc_offset());
|
||||||
|
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
||||||
|
|
||||||
|
// After we have deoptimized, jump to the correct frame.
|
||||||
|
__ EnterStubFrame();
|
||||||
|
__ CallRuntime(kRewindPostDeoptRuntimeEntry, 0);
|
||||||
|
__ LeaveStubFrame();
|
||||||
|
__ bkpt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Calls to the runtime to optimize the given function.
|
// Calls to the runtime to optimize the given function.
|
||||||
// R8: function to be reoptimized.
|
// R8: function to be reoptimized.
|
||||||
// R4: argument descriptor (preserved).
|
// R4: argument descriptor (preserved).
|
||||||
|
|
|
@ -558,19 +558,20 @@ static void GenerateDeoptimizationSequence(Assembler* assembler,
|
||||||
__ LeaveStubFrame();
|
__ LeaveStubFrame();
|
||||||
// Remove materialization arguments.
|
// Remove materialization arguments.
|
||||||
__ add(SP, SP, Operand(R2));
|
__ add(SP, SP, Operand(R2));
|
||||||
__ ret();
|
// The caller is responsible for emitting the return instruction.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// R0: result, must be preserved
|
// R0: result, must be preserved
|
||||||
void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
||||||
// Push zap value instead of CODE_REG for lazy deopt.
|
// Push zap value instead of CODE_REG for lazy deopt.
|
||||||
__ LoadImmediate(TMP, 0xf1f1f1f1);
|
__ LoadImmediate(TMP, kZapCodeReg);
|
||||||
__ Push(TMP);
|
__ Push(TMP);
|
||||||
// Return address for "call" to deopt stub.
|
// Return address for "call" to deopt stub.
|
||||||
__ LoadImmediate(LR, 0xe1e1e1e1);
|
__ LoadImmediate(LR, kZapReturnAddress);
|
||||||
__ ldr(CODE_REG, Address(THR, Thread::lazy_deopt_from_return_stub_offset()));
|
__ ldr(CODE_REG, Address(THR, Thread::lazy_deopt_from_return_stub_offset()));
|
||||||
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromReturn);
|
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromReturn);
|
||||||
|
__ ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -578,17 +579,19 @@ void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
||||||
// R1: stacktrace, must be preserved
|
// R1: stacktrace, must be preserved
|
||||||
void StubCode::GenerateDeoptimizeLazyFromThrowStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeLazyFromThrowStub(Assembler* assembler) {
|
||||||
// Push zap value instead of CODE_REG for lazy deopt.
|
// Push zap value instead of CODE_REG for lazy deopt.
|
||||||
__ LoadImmediate(TMP, 0xf1f1f1f1);
|
__ LoadImmediate(TMP, kZapCodeReg);
|
||||||
__ Push(TMP);
|
__ Push(TMP);
|
||||||
// Return address for "call" to deopt stub.
|
// Return address for "call" to deopt stub.
|
||||||
__ LoadImmediate(LR, 0xe1e1e1e1);
|
__ LoadImmediate(LR, kZapReturnAddress);
|
||||||
__ ldr(CODE_REG, Address(THR, Thread::lazy_deopt_from_throw_stub_offset()));
|
__ ldr(CODE_REG, Address(THR, Thread::lazy_deopt_from_throw_stub_offset()));
|
||||||
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromThrow);
|
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromThrow);
|
||||||
|
__ ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void StubCode::GenerateDeoptimizeStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeStub(Assembler* assembler) {
|
||||||
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
||||||
|
__ ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1949,6 +1952,26 @@ void StubCode::GenerateRunExceptionHandlerStub(Assembler* assembler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Deoptimize a frame on the call stack before rewinding.
|
||||||
|
// The arguments are stored in the Thread object.
|
||||||
|
// No result.
|
||||||
|
void StubCode::GenerateDeoptForRewindStub(Assembler* assembler) {
|
||||||
|
// Push zap value instead of CODE_REG.
|
||||||
|
__ LoadImmediate(TMP, kZapCodeReg);
|
||||||
|
__ Push(TMP);
|
||||||
|
|
||||||
|
// Load the deopt pc into LR.
|
||||||
|
__ LoadFromOffset(LR, THR, Thread::resume_pc_offset());
|
||||||
|
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
||||||
|
|
||||||
|
// After we have deoptimized, jump to the correct frame.
|
||||||
|
__ EnterStubFrame();
|
||||||
|
__ CallRuntime(kRewindPostDeoptRuntimeEntry, 0);
|
||||||
|
__ LeaveStubFrame();
|
||||||
|
__ brk(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Calls to the runtime to optimize the given function.
|
// Calls to the runtime to optimize the given function.
|
||||||
// R6: function to be re-optimized.
|
// R6: function to be re-optimized.
|
||||||
// R4: argument descriptor (preserved).
|
// R4: argument descriptor (preserved).
|
||||||
|
|
|
@ -47,6 +47,12 @@ void StubCode::GenerateRunExceptionHandlerStub(Assembler* assembler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Not executed, but used as a sentinel in Simulator::JumpToFrame.
|
||||||
|
void StubCode::GenerateDeoptForRewindStub(Assembler* assembler) {
|
||||||
|
__ Trap();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO(vegorov) Don't generate this stub.
|
// TODO(vegorov) Don't generate this stub.
|
||||||
void StubCode::GenerateFixCallersTargetStub(Assembler* assembler) {
|
void StubCode::GenerateFixCallersTargetStub(Assembler* assembler) {
|
||||||
__ Trap();
|
__ Trap();
|
||||||
|
|
|
@ -351,8 +351,6 @@ static void PushArgumentsArray(Assembler* assembler) {
|
||||||
// +------------------+
|
// +------------------+
|
||||||
// | return-address | (deoptimization point)
|
// | return-address | (deoptimization point)
|
||||||
// +------------------+
|
// +------------------+
|
||||||
// | Saved CODE_REG |
|
|
||||||
// +------------------+
|
|
||||||
// | ... | <- SP of optimized frame
|
// | ... | <- SP of optimized frame
|
||||||
//
|
//
|
||||||
// Parts of the code cannot GC, part of the code can GC.
|
// Parts of the code cannot GC, part of the code can GC.
|
||||||
|
@ -462,15 +460,16 @@ static void GenerateDeoptimizationSequence(Assembler* assembler,
|
||||||
__ popl(ECX); // Pop return address.
|
__ popl(ECX); // Pop return address.
|
||||||
__ addl(ESP, EBX); // Remove materialization arguments.
|
__ addl(ESP, EBX); // Remove materialization arguments.
|
||||||
__ pushl(ECX); // Push return address.
|
__ pushl(ECX); // Push return address.
|
||||||
__ ret();
|
// The caller is responsible for emitting the return instruction.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// EAX: result, must be preserved
|
// EAX: result, must be preserved
|
||||||
void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
||||||
// Return address for "call" to deopt stub.
|
// Return address for "call" to deopt stub.
|
||||||
__ pushl(Immediate(0xe1e1e1e1));
|
__ pushl(Immediate(kZapReturnAddress));
|
||||||
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromReturn);
|
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromReturn);
|
||||||
|
__ ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -478,13 +477,15 @@ void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
||||||
// EDX: stacktrace, must be preserved
|
// EDX: stacktrace, must be preserved
|
||||||
void StubCode::GenerateDeoptimizeLazyFromThrowStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeLazyFromThrowStub(Assembler* assembler) {
|
||||||
// Return address for "call" to deopt stub.
|
// Return address for "call" to deopt stub.
|
||||||
__ pushl(Immediate(0xe1e1e1e1));
|
__ pushl(Immediate(kZapReturnAddress));
|
||||||
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromThrow);
|
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromThrow);
|
||||||
|
__ ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void StubCode::GenerateDeoptimizeStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeStub(Assembler* assembler) {
|
||||||
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
||||||
|
__ ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1835,6 +1836,22 @@ void StubCode::GenerateRunExceptionHandlerStub(Assembler* assembler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Deoptimize a frame on the call stack before rewinding.
|
||||||
|
// The arguments are stored in the Thread object.
|
||||||
|
// No result.
|
||||||
|
void StubCode::GenerateDeoptForRewindStub(Assembler* assembler) {
|
||||||
|
// Push the deopt pc.
|
||||||
|
__ pushl(Address(THR, Thread::resume_pc_offset()));
|
||||||
|
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
||||||
|
|
||||||
|
// After we have deoptimized, jump to the correct frame.
|
||||||
|
__ EnterStubFrame();
|
||||||
|
__ CallRuntime(kRewindPostDeoptRuntimeEntry, 0);
|
||||||
|
__ LeaveFrame();
|
||||||
|
__ int3();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Calls to the runtime to optimize the given function.
|
// Calls to the runtime to optimize the given function.
|
||||||
// EBX: function to be reoptimized.
|
// EBX: function to be reoptimized.
|
||||||
// EDX: argument descriptor (preserved).
|
// EDX: argument descriptor (preserved).
|
||||||
|
|
|
@ -549,18 +549,19 @@ static void GenerateDeoptimizationSequence(Assembler* assembler,
|
||||||
// Remove materialization arguments.
|
// Remove materialization arguments.
|
||||||
__ SmiUntag(T1);
|
__ SmiUntag(T1);
|
||||||
__ addu(SP, SP, T1);
|
__ addu(SP, SP, T1);
|
||||||
__ Ret();
|
// The caller is responsible for emitting the return instruction.
|
||||||
}
|
}
|
||||||
|
|
||||||
// V0: result, must be preserved
|
// V0: result, must be preserved
|
||||||
void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
||||||
// Push zap value instead of CODE_REG for lazy deopt.
|
// Push zap value instead of CODE_REG for lazy deopt.
|
||||||
__ LoadImmediate(TMP, 0xf1f1f1f1);
|
__ LoadImmediate(TMP, kZapCodeReg);
|
||||||
__ Push(TMP);
|
__ Push(TMP);
|
||||||
// Return address for "call" to deopt stub.
|
// Return address for "call" to deopt stub.
|
||||||
__ LoadImmediate(RA, 0xe1e1e1e1);
|
__ LoadImmediate(RA, kZapReturnAddress);
|
||||||
__ lw(CODE_REG, Address(THR, Thread::lazy_deopt_from_return_stub_offset()));
|
__ lw(CODE_REG, Address(THR, Thread::lazy_deopt_from_return_stub_offset()));
|
||||||
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromReturn);
|
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromReturn);
|
||||||
|
__ Ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -568,17 +569,19 @@ void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
||||||
// V1: stacktrace, must be preserved
|
// V1: stacktrace, must be preserved
|
||||||
void StubCode::GenerateDeoptimizeLazyFromThrowStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeLazyFromThrowStub(Assembler* assembler) {
|
||||||
// Push zap value instead of CODE_REG for lazy deopt.
|
// Push zap value instead of CODE_REG for lazy deopt.
|
||||||
__ LoadImmediate(TMP, 0xf1f1f1f1);
|
__ LoadImmediate(TMP, kZapCodeReg);
|
||||||
__ Push(TMP);
|
__ Push(TMP);
|
||||||
// Return address for "call" to deopt stub.
|
// Return address for "call" to deopt stub.
|
||||||
__ LoadImmediate(RA, 0xe1e1e1e1);
|
__ LoadImmediate(RA, kZapReturnAddress);
|
||||||
__ lw(CODE_REG, Address(THR, Thread::lazy_deopt_from_throw_stub_offset()));
|
__ lw(CODE_REG, Address(THR, Thread::lazy_deopt_from_throw_stub_offset()));
|
||||||
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromThrow);
|
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromThrow);
|
||||||
|
__ Ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void StubCode::GenerateDeoptimizeStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeStub(Assembler* assembler) {
|
||||||
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
||||||
|
__ Ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2028,6 +2031,26 @@ void StubCode::GenerateRunExceptionHandlerStub(Assembler* assembler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Deoptimize a frame on the call stack before rewinding.
|
||||||
|
// The arguments are stored in the Thread object.
|
||||||
|
// No result.
|
||||||
|
void StubCode::GenerateDeoptForRewindStub(Assembler* assembler) {
|
||||||
|
// Push zap value instead of CODE_REG.
|
||||||
|
__ LoadImmediate(TMP, kZapCodeReg);
|
||||||
|
__ Push(TMP);
|
||||||
|
|
||||||
|
// Load the deopt pc into RA.
|
||||||
|
__ lw(RA, Address(THR, Thread::resume_pc_offset()));
|
||||||
|
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
||||||
|
|
||||||
|
// After we have deoptimized, jump to the correct frame.
|
||||||
|
__ EnterStubFrame();
|
||||||
|
__ CallRuntime(kRewindPostDeoptRuntimeEntry, 0);
|
||||||
|
__ LeaveStubFrame();
|
||||||
|
__ break_(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Calls to the runtime to optimize the given function.
|
// Calls to the runtime to optimize the given function.
|
||||||
// T0: function to be reoptimized.
|
// T0: function to be reoptimized.
|
||||||
// S4: argument descriptor (preserved).
|
// S4: argument descriptor (preserved).
|
||||||
|
|
|
@ -498,18 +498,19 @@ static void GenerateDeoptimizationSequence(Assembler* assembler,
|
||||||
__ popq(RCX); // Pop return address.
|
__ popq(RCX); // Pop return address.
|
||||||
__ addq(RSP, RBX); // Remove materialization arguments.
|
__ addq(RSP, RBX); // Remove materialization arguments.
|
||||||
__ pushq(RCX); // Push return address.
|
__ pushq(RCX); // Push return address.
|
||||||
__ ret();
|
// The caller is responsible for emitting the return instruction.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// RAX: result, must be preserved
|
// RAX: result, must be preserved
|
||||||
void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
||||||
// Push zap value instead of CODE_REG for lazy deopt.
|
// Push zap value instead of CODE_REG for lazy deopt.
|
||||||
__ pushq(Immediate(0xf1f1f1f1));
|
__ pushq(Immediate(kZapCodeReg));
|
||||||
// Return address for "call" to deopt stub.
|
// Return address for "call" to deopt stub.
|
||||||
__ pushq(Immediate(0xe1e1e1e1));
|
__ pushq(Immediate(kZapReturnAddress));
|
||||||
__ movq(CODE_REG, Address(THR, Thread::lazy_deopt_from_return_stub_offset()));
|
__ movq(CODE_REG, Address(THR, Thread::lazy_deopt_from_return_stub_offset()));
|
||||||
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromReturn);
|
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromReturn);
|
||||||
|
__ ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -517,16 +518,18 @@ void StubCode::GenerateDeoptimizeLazyFromReturnStub(Assembler* assembler) {
|
||||||
// RDX: stacktrace, must be preserved
|
// RDX: stacktrace, must be preserved
|
||||||
void StubCode::GenerateDeoptimizeLazyFromThrowStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeLazyFromThrowStub(Assembler* assembler) {
|
||||||
// Push zap value instead of CODE_REG for lazy deopt.
|
// Push zap value instead of CODE_REG for lazy deopt.
|
||||||
__ pushq(Immediate(0xf1f1f1f1));
|
__ pushq(Immediate(kZapCodeReg));
|
||||||
// Return address for "call" to deopt stub.
|
// Return address for "call" to deopt stub.
|
||||||
__ pushq(Immediate(0xe1e1e1e1));
|
__ pushq(Immediate(kZapReturnAddress));
|
||||||
__ movq(CODE_REG, Address(THR, Thread::lazy_deopt_from_throw_stub_offset()));
|
__ movq(CODE_REG, Address(THR, Thread::lazy_deopt_from_throw_stub_offset()));
|
||||||
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromThrow);
|
GenerateDeoptimizationSequence(assembler, kLazyDeoptFromThrow);
|
||||||
|
__ ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void StubCode::GenerateDeoptimizeStub(Assembler* assembler) {
|
void StubCode::GenerateDeoptimizeStub(Assembler* assembler) {
|
||||||
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
||||||
|
__ ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1891,6 +1894,25 @@ void StubCode::GenerateRunExceptionHandlerStub(Assembler* assembler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Deoptimize a frame on the call stack before rewinding.
|
||||||
|
// The arguments are stored in the Thread object.
|
||||||
|
// No result.
|
||||||
|
void StubCode::GenerateDeoptForRewindStub(Assembler* assembler) {
|
||||||
|
// Push zap value instead of CODE_REG.
|
||||||
|
__ pushq(Immediate(kZapCodeReg));
|
||||||
|
|
||||||
|
// Push the deopt pc.
|
||||||
|
__ pushq(Address(THR, Thread::resume_pc_offset()));
|
||||||
|
GenerateDeoptimizationSequence(assembler, kEagerDeopt);
|
||||||
|
|
||||||
|
// After we have deoptimized, jump to the correct frame.
|
||||||
|
__ EnterStubFrame();
|
||||||
|
__ CallRuntime(kRewindPostDeoptRuntimeEntry, 0);
|
||||||
|
__ LeaveStubFrame();
|
||||||
|
__ int3();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Calls to the runtime to optimize the given function.
|
// Calls to the runtime to optimize the given function.
|
||||||
// RDI: function to be reoptimized.
|
// RDI: function to be reoptimized.
|
||||||
// R10: argument descriptor (preserved).
|
// R10: argument descriptor (preserved).
|
||||||
|
|
Loading…
Reference in a new issue