Index: runtime/observatory/lib/src/elements/debugger.dart |
diff --git a/runtime/observatory/lib/src/elements/debugger.dart b/runtime/observatory/lib/src/elements/debugger.dart |
index 4a9bdf83bf0bc91a33c30a04c1519c21cd68b4ea..0bd8e019dac1d422fce3f0000212fb7fbc5fbc7b 100644 |
--- a/runtime/observatory/lib/src/elements/debugger.dart |
+++ b/runtime/observatory/lib/src/elements/debugger.dart |
@@ -30,9 +30,9 @@ class HelpCommand extends DebuggerCommand { |
String _nameAndAlias(Command cmd) { |
if (cmd.alias == null) { |
- return cmd.name; |
+ return cmd.fullName; |
} else { |
- return '${cmd.name}, ${cmd.alias}'; |
+ return '${cmd.fullName}, ${cmd.alias}'; |
} |
} |
@@ -51,7 +51,7 @@ class HelpCommand extends DebuggerCommand { |
"\nFor more information on a specific command type 'help <command>'\n" |
"\n" |
"Command prefixes are accepted (e.g. 'h' for 'help')\n" |
- "Hit [TAB] to complete a command (try 'i[TAB][TAB]')\n" |
+ "Hit [TAB] to complete a command (try 'is[TAB][TAB]')\n" |
"Hit [ENTER] to repeat the last command\n" |
"Use up/down arrow for command history\n"); |
return new Future.value(null); |
@@ -409,7 +409,7 @@ class BreakCommand extends DebuggerCommand { |
Future<List<String>> complete(List<String> args) { |
if (args.length != 1) { |
- return new Future.value([]); |
+ return new Future.value([args.join('')]); |
} |
// TODO - fix SourceLocation complete |
return new Future.value(SourceLocation.complete(debugger, args[0])); |
@@ -493,7 +493,7 @@ class ClearCommand extends DebuggerCommand { |
Future<List<String>> complete(List<String> args) { |
if (args.length != 1) { |
- return new Future.value([]); |
+ return new Future.value([args.join('')]); |
} |
return new Future.value(SourceLocation.complete(debugger, args[0])); |
} |
@@ -603,14 +603,116 @@ class InfoBreakpointsCommand extends DebuggerCommand { |
'Syntax: info breakpoints\n'; |
} |
-class InfoIsolatesCommand extends DebuggerCommand { |
- InfoIsolatesCommand(Debugger debugger) : super(debugger, 'isolates', []); |
+class InfoFrameCommand extends DebuggerCommand { |
+ InfoFrameCommand(Debugger debugger) : super(debugger, 'frame', []); |
+ |
+ Future run(List<String> args) { |
+ if (args.length > 0) { |
+ debugger.console.print('info frame expects no arguments'); |
+ return new Future.value(null); |
+ } |
+ debugger.console.print('frame = ${debugger.currentFrame}'); |
+ return new Future.value(null); |
+ } |
+ |
+ String helpShort = 'Show current frame'; |
+ |
+ String helpLong = |
+ 'Show current frame.\n' |
+ '\n' |
+ 'Syntax: info frame\n'; |
+} |
+ |
+class IsolateCommand extends DebuggerCommand { |
+ IsolateCommand(Debugger debugger) : super(debugger, 'isolate', [ |
+ new IsolateListCommand(debugger), |
+ new IsolateNameCommand(debugger), |
+ ]) { |
+ alias = 'i'; |
+ } |
+ |
+ Future run(List<String> args) { |
+ if (args.length != 1) { |
+ debugger.console.print('isolate expects one argument'); |
+ return new Future.value(null); |
+ } |
+ var arg = args[0].trim(); |
+ var num = int.parse(arg, onError:(_) => null); |
+ |
+ var candidate; |
+ for (var isolate in debugger.vm.isolates) { |
+ if (num != null && num == isolate.number) { |
+ candidate = isolate; |
+ break; |
+ } else if (arg == isolate.name) { |
+ if (candidate != null) { |
+ debugger.console.print( |
+ "Isolate identifier '${arg}' is ambiguous: " |
+ 'use the isolate number instead'); |
+ return new Future.value(null); |
+ } |
+ candidate = isolate; |
+ } |
+ } |
+ if (candidate == null) { |
+ debugger.console.print("Invalid isolate identifier '${arg}'"); |
+ } else { |
+ if (candidate == debugger.isolate) { |
+ debugger.console.print( |
+ "Current isolate is already ${candidate.number} '${candidate.name}'"); |
+ } else { |
+ debugger.console.print( |
+ "Switching to isolate ${candidate.number} '${candidate.name}'"); |
+ debugger.isolate = candidate; |
+ } |
+ } |
+ return new Future.value(null); |
+ } |
+ |
+ Future<List<String>> complete(List<String> args) { |
+ if (args.length != 1) { |
+ return new Future.value([args.join('')]); |
+ } |
+ var isolates = debugger.vm.isolates.toList(); |
+ isolates.sort((a, b) => a.startTime.compareTo(b.startTime)); |
+ var result = []; |
+ for (var isolate in isolates) { |
+ var str = isolate.number.toString(); |
+ if (str.startsWith(args[0])) { |
+ result.add('$str '); |
+ } |
+ } |
+ for (var isolate in isolates) { |
+ if (isolate.name.startsWith(args[0])) { |
+ result.add('${isolate.name} '); |
+ } |
+ } |
+ return new Future.value(result); |
+ } |
+ String helpShort = 'Switch the current isolate'; |
+ |
+ String helpLong = |
+ 'Switch the current isolate.\n' |
+ '\n' |
+ 'Syntax: isolate <number>\n' |
+ ' isolate <name>\n'; |
+} |
+ |
+class IsolateListCommand extends DebuggerCommand { |
+ IsolateListCommand(Debugger debugger) : super(debugger, 'list', []); |
Future run(List<String> args) { |
- for (var isolate in debugger.isolate.vm.isolates) { |
+ if (debugger.vm == null) { |
+ debugger.console.print( |
+ "Internal error: vm has not been set"); |
+ return new Future.value(null); |
+ } |
+ var isolates = debugger.vm.isolates.toList(); |
+ isolates.sort((a, b) => a.startTime.compareTo(b.startTime)); |
+ for (var isolate in isolates) { |
String current = (isolate == debugger.isolate ? ' *' : ''); |
debugger.console.print( |
- "Isolate ${isolate.id} '${isolate.name}'${current}"); |
+ "Isolate ${isolate.number} '${isolate.name}'${current}"); |
} |
return new Future.value(null); |
} |
@@ -620,35 +722,32 @@ class InfoIsolatesCommand extends DebuggerCommand { |
String helpLong = |
'List all isolates.\n' |
'\n' |
- 'Syntax: info isolates\n'; |
+ 'Syntax: isolate list\n'; |
} |
-class InfoFrameCommand extends DebuggerCommand { |
- InfoFrameCommand(Debugger debugger) : super(debugger, 'frame', []); |
+class IsolateNameCommand extends DebuggerCommand { |
+ IsolateNameCommand(Debugger debugger) : super(debugger, 'name', []); |
Future run(List<String> args) { |
- if (args.length > 0) { |
- debugger.console.print('info frame expects 1 argument'); |
+ if (args.length != 1) { |
+ debugger.console.print('isolate name expects one argument'); |
return new Future.value(null); |
} |
- debugger.console.print('frame = ${debugger.currentFrame}'); |
- return new Future.value(null); |
+ return debugger.isolate.setName(args[0]); |
} |
- String helpShort = 'Show current frame'; |
+ String helpShort = 'Rename an isolate'; |
String helpLong = |
- 'Show current frame.\n' |
+ 'Rename an isolate.\n' |
'\n' |
- 'Syntax: info frame\n'; |
+ 'Syntax: isolate name <name>\n'; |
} |
class InfoCommand extends DebuggerCommand { |
InfoCommand(Debugger debugger) : super(debugger, 'info', [ |
new InfoBreakpointsCommand(debugger), |
- new InfoIsolatesCommand(debugger), |
- new InfoFrameCommand(debugger), |
- ]); |
+ new InfoFrameCommand(debugger)]); |
Future run(List<String> args) { |
debugger.console.print("'info' expects a subcommand (see 'help info')"); |
@@ -724,6 +823,7 @@ class RefreshCommand extends DebuggerCommand { |
// Tracks the state for an isolate debugging session. |
class ObservatoryDebugger extends Debugger { |
RootCommand cmd; |
+ DebuggerPageElement page; |
DebuggerConsoleElement console; |
DebuggerStackElement stackElement; |
ServiceMap stack; |
@@ -758,14 +858,17 @@ class ObservatoryDebugger extends Debugger { |
new ClearCommand(this), |
new DeleteCommand(this), |
new InfoCommand(this), |
+ new IsolateCommand(this), |
new RefreshCommand(this), |
]); |
} |
- void set isolate(Isolate iso) { |
+ VM get vm => page.app.vm; |
+ |
+ void updateIsolate(Isolate iso) { |
_isolate = iso; |
if (_isolate != null) { |
- _isolate.reload().then((_) { |
+ _isolate.reload().then((response) { |
// TODO(turnidge): Currently the debugger relies on all libs |
// being loaded. Fix this. |
var pending = []; |
@@ -775,26 +878,46 @@ class ObservatoryDebugger extends Debugger { |
} |
} |
Future.wait(pending).then((_) { |
- _isolate.vm.events.stream.listen(_onEvent); |
+ if (_subscription == null) { |
+ _subscription = vm.events.stream.listen(_onEvent); |
+ } |
_refreshStack(isolate.pauseEvent).then((_) { |
reportStatus(); |
}); |
}); |
}); |
+ } else { |
+ reportStatus(); |
} |
} |
+ |
+ void set isolate(Isolate iso) { |
+ // Setting the page's isolate will trigger updateIsolate to be called. |
+ // |
+ // TODO(turnidge): Rework ownership of the ObservatoryDebugger in another |
+ // change. |
+ page.isolate = iso; |
+ } |
Isolate get isolate => _isolate; |
Isolate _isolate; |
+ var _subscription; |
void init() { |
console.newline(); |
console.printBold("Type 'h' for help"); |
+ // Wait a bit and if polymer still hasn't set up the isolate, |
+ // report this to the user. |
+ new Timer(const Duration(seconds:1), () { |
+ if (isolate == null) { |
+ reportStatus(); |
+ } |
+ }); |
} |
Future refreshStack() { |
return _refreshStack(isolate.pauseEvent).then((_) { |
- reportStatus(); |
- }); |
+ reportStatus(); |
+ }); |
} |
bool isolatePaused() { |
@@ -830,7 +953,9 @@ class ObservatoryDebugger extends Debugger { |
} |
void reportStatus() { |
- if (_isolate.idle) { |
+ if (_isolate == null) { |
+ console.print('No current isolate'); |
+ } else if (_isolate.idle) { |
console.print('Isolate is idle'); |
} else if (_isolate.running) { |
console.print("Isolate is running (type 'pause' to interrupt)"); |
@@ -902,13 +1027,28 @@ class ObservatoryDebugger extends Debugger { |
} |
void _onEvent(ServiceEvent event) { |
- if (event.owner != isolate) { |
- return; |
- } |
switch(event.eventType) { |
- case ServiceEvent.kIsolateExit: |
- console.print('Isolate shutdown'); |
- isolate = null; |
+ case ServiceEvent.kIsolateStart: { |
+ var iso = event.owner; |
+ console.print( |
+ "Isolate ${iso.number} '${iso.name}' has been created"); |
+ break; |
+ } |
+ |
+ case ServiceEvent.kIsolateExit: { |
+ var iso = event.owner; |
+ if (iso == isolate) { |
+ console.print("The current isolate has exited"); |
+ } else { |
+ console.print( |
+ "Isolate ${iso.number} '${iso.name}' has exited"); |
+ } |
+ break; |
+ } |
+ |
+ case ServiceEvent.kIsolateUpdate: |
+ var iso = event.owner; |
+ console.print("Isolate ${iso.number} renamed to '${iso.name}'"); |
break; |
case ServiceEvent.kPauseStart: |
@@ -916,25 +1056,30 @@ class ObservatoryDebugger extends Debugger { |
case ServiceEvent.kPauseBreakpoint: |
case ServiceEvent.kPauseInterrupted: |
case ServiceEvent.kPauseException: |
- _refreshStack(event).then((_) { |
- _reportPause(event); |
- }); |
+ if (event.owner == isolate) { |
+ _refreshStack(event).then((_) { |
+ _reportPause(event); |
+ }); |
+ } |
break; |
case ServiceEvent.kResume: |
- console.print('Continuing...'); |
+ if (event.owner == isolate) { |
+ console.print('Continuing...'); |
+ } |
break; |
case ServiceEvent.kBreakpointAdded: |
case ServiceEvent.kBreakpointResolved: |
case ServiceEvent.kBreakpointRemoved: |
- _reportBreakpointEvent(event); |
+ if (event.owner == isolate) { |
+ _reportBreakpointEvent(event); |
+ } |
break; |
case ServiceEvent.kIsolateStart: |
case ServiceEvent.kGraph: |
case ServiceEvent.kGC: |
- // Ignore these events for now. |
break; |
default: |
@@ -1008,16 +1153,19 @@ class ObservatoryDebugger extends Debugger { |
@CustomTag('debugger-page') |
class DebuggerPageElement extends ObservatoryElement { |
+ @published ObservatoryApplication app; |
@published Isolate isolate; |
isolateChanged(oldValue) { |
if (isolate != null) { |
- debugger.isolate = isolate; |
+ debugger.updateIsolate(isolate); |
} |
} |
ObservatoryDebugger debugger = new ObservatoryDebugger(); |
- DebuggerPageElement.created() : super.created(); |
+ DebuggerPageElement.created() : super.created() { |
+ debugger.page = this; |
+ } |
@override |
void attached() { |