Added the 'references' command to the debugger protocol to provide access to the mirror features of retreiving all the objects referencing a given object and all objects instantiated by a given function.

Added commands 'references' and 'instances' to the developer shell for using this new debugger protocol command.
Review URL: http://codereview.chromium.org/21080

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1234 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
sgjesse@chromium.org 2009-02-05 14:19:36 +00:00
parent 05a56bf1ea
commit 6b4d372696
4 changed files with 262 additions and 34 deletions

128
src/d8.js
View File

@ -282,6 +282,14 @@ function DebugRequest(cmd_line) {
this.request_ = this.dirCommandToJSONRequest_(args);
break;
case 'references':
this.request_ = this.referencesCommandToJSONRequest_(args);
break;
case 'instances':
this.request_ = this.instancesCommandToJSONRequest_(args);
break;
case 'source':
this.request_ = this.sourceCommandToJSONRequest_(args);
break;
@ -363,7 +371,7 @@ DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) {
// Check if the expression is a handle id in the form #<handle>#.
var handle_match = expression.match(/^#([0-9]*)#$/);
if (handle_match) {
// Build an evaluate request.
// Build a lookup request.
var request = this.createRequest('lookup');
request.arguments = {};
request.arguments.handle = parseInt(handle_match[1]);
@ -378,6 +386,21 @@ DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) {
};
// Create a JSON request for the references/instances command.
DebugRequest.prototype.makeReferencesJSONRequest_ = function(handle, type) {
// Build a references request.
var handle_match = handle.match(/^#([0-9]*)#$/);
if (handle_match) {
var request = this.createRequest('references');
request.arguments = {};
request.arguments.type = type;
request.arguments.handle = parseInt(handle_match[1]);
return request.toJSONProtocol();
} else {
throw new Error('Invalid object id.');
}
};
// Create a JSON request for the continue command.
DebugRequest.prototype.continueCommandToJSONRequest_ = function(args) {
@ -505,6 +528,29 @@ DebugRequest.prototype.dirCommandToJSONRequest_ = function(args) {
};
// Create a JSON request for the references command.
DebugRequest.prototype.referencesCommandToJSONRequest_ = function(args) {
// Build an evaluate request from the text command.
if (args.length == 0) {
throw new Error('Missing object id.');
}
return this.makeReferencesJSONRequest_(args, 'referencedBy');
};
// Create a JSON request for the instances command.
DebugRequest.prototype.instancesCommandToJSONRequest_ = function(args) {
// Build an evaluate request from the text command.
if (args.length == 0) {
throw new Error('Missing object id.');
}
// Build a references request.
return this.makeReferencesJSONRequest_(args, 'constructedBy');
};
// Create a JSON request for the source command.
DebugRequest.prototype.sourceCommandToJSONRequest_ = function(args) {
// Build a evaluate request from the text command.
@ -660,6 +706,40 @@ function formatHandleReference_(value) {
}
function formatObject_(value, include_properties) {
var result = '';
result += formatHandleReference_(value);
result += ', type: object'
result += ', constructor ';
var ctor = value.constructorFunctionValue();
result += formatHandleReference_(ctor);
result += ', __proto__ ';
var proto = value.protoObjectValue();
result += formatHandleReference_(proto);
result += ', ';
result += value.propertyCount();
result += ' properties.';
if (include_properties) {
result += '\n';
for (var i = 0; i < value.propertyCount(); i++) {
result += ' ';
result += value.propertyName(i);
result += ': ';
var property_value = value.propertyValue(i);
if (property_value && property_value.type()) {
result += property_value.type();
} else {
result += '<no type>';
}
result += ' ';
result += formatHandleReference_(property_value);
result += '\n';
}
}
return result;
}
// Convert a JSON response to text for display in a text based debugger.
function DebugResponseDetails(json_response) {
details = {text:'', running:false}
@ -719,31 +799,7 @@ function DebugResponseDetails(json_response) {
} else {
var value = response.bodyValue();
if (value.isObject()) {
result += formatHandleReference_(value);
result += ', type: object'
result += ', constructor ';
var ctor = value.constructorFunctionValue();
result += formatHandleReference_(ctor);
result += ', __proto__ ';
var proto = value.protoObjectValue();
result += formatHandleReference_(proto);
result += ', ';
result += value.propertyCount();
result += ' properties.\n';
for (var i = 0; i < value.propertyCount(); i++) {
result += ' ';
result += value.propertyName(i);
result += ': ';
var property_value = value.propertyValue(i);
if (property_value && property_value.type()) {
result += property_value.type();
} else {
result += '<no type>';
}
result += ' ';
result += formatHandleReference_(property_value);
result += '\n';
}
result += formatObject_(value, true);
} else {
result += 'type: ';
result += value.type();
@ -762,6 +818,18 @@ function DebugResponseDetails(json_response) {
}
details.text = result;
break;
case 'references':
var count = body.length;
result += 'found ' + count + ' objects';
result += '\n';
for (var i = 0; i < count; i++) {
var value = response.bodyValue(i);
result += formatObject_(value, false);
result += '\n';
}
details.text = result;
break;
case 'source':
// Get the source from the response.
@ -915,8 +983,12 @@ ProtocolPackage.prototype.body = function() {
}
ProtocolPackage.prototype.bodyValue = function() {
return new ProtocolValue(this.packet_.body, this);
ProtocolPackage.prototype.bodyValue = function(index) {
if (IS_UNDEFINED(index)) {
return new ProtocolValue(this.packet_.body, this);
} else {
return new ProtocolValue(this.packet_.body[index], this);
}
}

View File

@ -1078,6 +1078,8 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request)
this.evaluateRequest_(request, response);
} else if (request.command == 'lookup') {
this.lookupRequest_(request, response);
} else if (request.command == 'references') {
this.referencesRequest_(request, response);
} else if (request.command == 'source') {
this.sourceRequest_(request, response);
} else if (request.command == 'scripts') {
@ -1461,6 +1463,41 @@ DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) {
};
DebugCommandProcessor.prototype.referencesRequest_ =
function(request, response) {
if (!request.arguments) {
return response.failed('Missing arguments');
}
// Pull out arguments.
var type = request.arguments.type;
var handle = request.arguments.handle;
// Check for legal arguments.
if (IS_UNDEFINED(type)) {
return response.failed('Argument "type" missing');
}
if (IS_UNDEFINED(handle)) {
return response.failed('Argument "handle" missing');
}
if (type != 'referencedBy' && type != 'constructedBy') {
return response.failed('Invalid type "' + type + '"');
}
// Lookup handle and return objects with references the object.
var mirror = LookupMirror(handle);
if (mirror) {
if (type == 'referencedBy') {
response.body = mirror.referencedBy();
} else {
response.body = mirror.constructedBy();
}
} else {
return response.failed('Object #' + handle + '# not found');
}
};
DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) {
// No frames no source.
if (this.exec_state_.frameCount() == 0) {

View File

@ -690,15 +690,16 @@ ObjectMirror.prototype.lookupProperty = function(value) {
/**
* Returns objects which has direct references to this object
* @param {number} opt_max_instances Optional parameter specifying the maximum
* number of instances to return.
* @param {number} opt_max_objects Optional parameter specifying the maximum
* number of referencing objects to return.
* @return {Array} The objects which has direct references to this object.
*/
ObjectMirror.prototype.referencedBy = function(opt_max_instances) {
// Find all objects constructed from this function.
var result = %DebugReferencedBy(this.value_, Mirror.prototype, opt_max_instances || 0);
ObjectMirror.prototype.referencedBy = function(opt_max_objects) {
// Find all objects with direct references to this object.
var result = %DebugReferencedBy(this.value_,
Mirror.prototype, opt_max_objects || 0);
// Make mirrors for all the instances found.
// Make mirrors for all the references found.
for (var i = 0; i < result.length; i++) {
result[i] = MakeMirror(result[i]);
}

View File

@ -0,0 +1,118 @@
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-debug-as debug
// Get the Debug object exposed from the debug context global object.
Debug = debug.Debug
listenerComplete = false;
exception = false;
// The base part of all evaluate requests.
var base_request = '"seq":0,"type":"request","command":"references"'
function safeEval(code) {
try {
return eval('(' + code + ')');
} catch (e) {
assertEquals(void 0, e);
return undefined;
}
}
function testRequest(dcp, arguments, success, count) {
// Generate request with the supplied arguments.
var request;
if (arguments) {
request = '{' + base_request + ',"arguments":' + arguments + '}';
} else {
request = '{' + base_request + '}'
}
// Process the request and check expectation.
var response = safeEval(dcp.processDebugJSONRequest(request));
if (success) {
assertTrue(response.success, request + ' -> ' + response.message);
assertTrue(response.body instanceof Array);
if (count) {
assertEquals(count, response.body.length);
} else {
assertTrue(response.body.length > 0);
}
} else {
assertFalse(response.success, request + ' -> ' + response.message);
}
assertFalse(response.running, request + ' -> expected not running');
}
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break) {
// Get the debug command processor.
var dcp = exec_state.debugCommandProcessor();
// Test some illegal references requests.
testRequest(dcp, void 0, false);
testRequest(dcp, '{"handle":"a"}', false);
testRequest(dcp, '{"handle":1}', false);
testRequest(dcp, '{"type":"referencedBy"}', false);
testRequest(dcp, '{"type":"constructedBy"}', false);
// Evaluate Point.
var evaluate_point = '{"seq":0,"type":"request","command":"evaluate",' +
'"arguments":{"expression":"Point"}}';
var response = safeEval(dcp.processDebugJSONRequest(evaluate_point));
assertTrue(response.success, "Evaluation of Point failed");
var handle = response.body.handle;
// Test some legal references requests.
testRequest(dcp, '{"handle":' + handle + ',"type":"referencedBy"}', true);
testRequest(dcp, '{"handle":' + handle + ',"type":"constructedBy"}',
true, 2);
// Indicate that all was processed.
listenerComplete = true;
}
} catch (e) {
exception = e
};
};
// Add the debug event listener.
Debug.setListener(listener);
// Test constructor and objects.
function Point(x, y) { this.x_ = x; this.y_ = y;}
p = new Point(0,0);
q = new Point(1,2);
// Enter debugger causing the event listener to be called.
debugger;
// Make sure that the debug event listener vas invoked.
assertFalse(exception, "exception in listener")
assertTrue(listenerComplete, "listener did not run to completion");