From 8481a756988dba0e9686927dba390c5d6f857d22 Mon Sep 17 00:00:00 2001 From: "sgjesse@chromium.org" Date: Wed, 2 Mar 2011 08:10:38 +0000 Subject: [PATCH] Adding debugger interface and runtime functions hooks for supporting LiveObjectList functionality. Patch by Mark Lam from Hewlett-Packard Development Company, LP Review URL: http://codereview.chromium.org/6351007 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7011 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/d8.cc | 6 + src/d8.js | 618 ++++++++++++++++++++++++++++++++++++++++++ src/debug-debugger.js | 121 +++++++++ src/runtime.cc | 202 ++++++++++++++ src/runtime.h | 16 +- 5 files changed, 962 insertions(+), 1 deletion(-) diff --git a/src/d8.cc b/src/d8.cc index 9d447ad65..349ec9041 100644 --- a/src/d8.cc +++ b/src/d8.cc @@ -425,6 +425,12 @@ void Shell::Initialize() { global_template->Set(String::New("quit"), FunctionTemplate::New(Quit)); global_template->Set(String::New("version"), FunctionTemplate::New(Version)); +#ifdef LIVE_OBJECT_LIST + global_template->Set(String::New("lol_is_enabled"), Boolean::New(true)); +#else + global_template->Set(String::New("lol_is_enabled"), Boolean::New(false)); +#endif + Handle os_templ = ObjectTemplate::New(); AddOSMethods(os_templ); global_template->Set(String::New("os"), os_templ); diff --git a/src/d8.js b/src/d8.js index b0edb706a..979807888 100644 --- a/src/d8.js +++ b/src/d8.js @@ -117,6 +117,10 @@ Debug.State = { var trace_compile = false; // Tracing all compile events? var trace_debug_json = false; // Tracing all debug json packets? var last_cmd_line = ''; +//var lol_is_enabled; // Set to true in d8.cc if LIVE_OBJECT_LIST is defined. +var lol_next_dump_index = 0; +const kDefaultLolLinesToPrintAtATime = 10; +const kMaxLolLinesToPrintAtATime = 1000; var repeat_cmd_line = ''; var is_running = true; @@ -495,6 +499,13 @@ function DebugRequest(cmd_line) { this.request_ = void 0; break; + case 'liveobjectlist': + case 'lol': + if (lol_is_enabled) { + this.request_ = this.lolToJSONRequest_(args, is_repeating); + break; + } + default: throw new Error('Unknown command "' + cmd + '"'); } @@ -539,10 +550,54 @@ DebugRequest.prototype.createRequest = function(command) { }; +// Note: we use detected command repetition as a signal for continuation here. +DebugRequest.prototype.createLOLRequest = function(command, + start_index, + lines_to_dump, + is_continuation) { + if (is_continuation) { + start_index = lol_next_dump_index; + } + + if (lines_to_dump) { + lines_to_dump = parseInt(lines_to_dump); + } else { + lines_to_dump = kDefaultLolLinesToPrintAtATime; + } + if (lines_to_dump > kMaxLolLinesToPrintAtATime) { + lines_to_dump = kMaxLolLinesToPrintAtATime; + } + + // Save the next start_index to dump from: + lol_next_dump_index = start_index + lines_to_dump; + + var request = this.createRequest(command); + request.arguments = {}; + request.arguments.start = start_index; + request.arguments.count = lines_to_dump; + + return request; +}; + + // Create a JSON request for the evaluation command. DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) { // Global varaible used to store whether a handle was requested. lookup_handle = null; + + if (lol_is_enabled) { + // Check if the expression is a obj id in the form @. + var obj_id_match = expression.match(/^@([0-9]+)$/); + if (obj_id_match) { + var obj_id = parseInt(obj_id_match[1]); + // Build a dump request. + var request = this.createRequest('getobj'); + request.arguments = {}; + request.arguments.obj_id = obj_id; + return request.toJSONProtocol(); + } + } + // Check if the expression is a handle id in the form ##. var handle_match = expression.match(/^#([0-9]*)#$/); if (handle_match) { @@ -1103,6 +1158,10 @@ DebugRequest.prototype.infoCommandToJSONRequest_ = function(args) { // Build a evaluate request from the text command. request = this.createRequest('frame'); last_cmd = 'info args'; + } else if (lol_is_enabled && + args && (args == 'liveobjectlist' || args == 'lol')) { + // Build a evaluate request from the text command. + return this.liveObjectListToJSONRequest_(null); } else { throw new Error('Invalid info arguments.'); } @@ -1153,6 +1212,262 @@ DebugRequest.prototype.gcToJSONRequest_ = function(args) { }; +// Args: [v[erbose]] [] [i[ndex] ] [t[ype] ] [sp[ace] ] +DebugRequest.prototype.lolMakeListRequest = + function(cmd, args, first_arg_index, is_repeating) { + + var request; + var start_index = 0; + var dump_limit = void 0; + var type_filter = void 0; + var space_filter = void 0; + var prop_filter = void 0; + var is_verbose = false; + var i; + + for (i = first_arg_index; i < args.length; i++) { + var arg = args[i]; + // Check for [v[erbose]]: + if (arg === 'verbose' || arg === 'v') { + // Nothing to do. This is already implied by args.length > 3. + is_verbose = true; + + // Check for []: + } else if (arg.match(/^[0-9]+$/)) { + dump_limit = arg; + is_verbose = true; + + // Check for i[ndex] : + } else if (arg === 'index' || arg === 'i') { + i++; + if (args.length < i) { + throw new Error('Missing index after ' + arg + '.'); + } + start_index = parseInt(args[i]); + // The user input start index starts at 1: + if (start_index <= 0) { + throw new Error('Invalid index ' + args[i] + '.'); + } + start_index -= 1; + is_verbose = true; + + // Check for t[ype] : + } else if (arg === 'type' || arg === 't') { + i++; + if (args.length < i) { + throw new Error('Missing type after ' + arg + '.'); + } + type_filter = args[i]; + + // Check for space : + } else if (arg === 'space' || arg === 'sp') { + i++; + if (args.length < i) { + throw new Error('Missing space name after ' + arg + '.'); + } + space_filter = args[i]; + + // Check for property : + } else if (arg === 'property' || arg === 'prop') { + i++; + if (args.length < i) { + throw new Error('Missing property name after ' + arg + '.'); + } + prop_filter = args[i]; + + } else { + throw new Error('Unknown args at ' + arg + '.'); + } + } + + // Build the verbose request: + if (is_verbose) { + request = this.createLOLRequest('lol-'+cmd, + start_index, + dump_limit, + is_repeating); + request.arguments.verbose = true; + } else { + request = this.createRequest('lol-'+cmd); + request.arguments = {}; + } + + request.arguments.filter = {}; + if (type_filter) { + request.arguments.filter.type = type_filter; + } + if (space_filter) { + request.arguments.filter.space = space_filter; + } + if (prop_filter) { + request.arguments.filter.prop = prop_filter; + } + + return request; +} + + +function extractObjId(args) { + var id = args; + id = id.match(/^@([0-9]+)$/); + if (id) { + id = id[1]; + } else { + throw new Error('Invalid obj id ' + args + '.'); + } + return parseInt(id); +} + + +DebugRequest.prototype.lolToJSONRequest_ = function(args, is_repeating) { + var request; + // Use default command if one is not specified: + if (!args) { + args = 'info'; + } + + var orig_args = args; + var first_arg_index; + + var arg, i; + var args = args.split(/\s+/g); + var cmd = args[0]; + var id; + + // Command: [v[erbose]] ... + if (cmd.match(/^[0-9]+$/)) { + // Convert to the padded list command: + // Command: l[ist] [v[erbose]] ... + + // Insert the implicit 'list' in front and process as normal: + cmd = 'list'; + args.unshift(cmd); + } + + switch(cmd) { + // Command: c[apture] + case 'capture': + case 'c': + request = this.createRequest('lol-capture'); + break; + + // Command: clear|d[elete] |all + case 'clear': + case 'delete': + case 'del': { + if (args.length < 2) { + throw new Error('Missing argument after ' + cmd + '.'); + } else if (args.length > 2) { + throw new Error('Too many arguments after ' + cmd + '.'); + } + id = args[1]; + if (id.match(/^[0-9]+$/)) { + // Delete a specific lol record: + request = this.createRequest('lol-delete'); + request.arguments = {}; + request.arguments.id = parseInt(id); + } else if (id === 'all') { + // Delete all: + request = this.createRequest('lol-reset'); + } else { + throw new Error('Invalid argument after ' + cmd + '.'); + } + break; + } + + // Command: diff [] + case 'diff': + first_arg_index = 3; + + // Command: list [] + case 'list': + + // Command: ret[ainers] [] + case 'retainers': + case 'ret': + case 'retaining-paths': + case 'rp': { + if (cmd === 'ret') cmd = 'retainers'; + else if (cmd === 'rp') cmd = 'retaining-paths'; + + if (!first_arg_index) first_arg_index = 2; + + if (args.length < first_arg_index) { + throw new Error('Too few arguments after ' + cmd + '.'); + } + + var request_cmd = (cmd === 'list') ? 'diff':cmd; + request = this.lolMakeListRequest(request_cmd, + args, + first_arg_index, + is_repeating); + + if (cmd === 'diff') { + request.arguments.id1 = parseInt(args[1]); + request.arguments.id2 = parseInt(args[2]); + } else if (cmd == 'list') { + request.arguments.id1 = 0; + request.arguments.id2 = parseInt(args[1]); + } else { + request.arguments.id = extractObjId(args[1]); + } + break; + } + + // Command: getid + case 'getid': { + request = this.createRequest('lol-getid'); + request.arguments = {}; + request.arguments.address = args[1]; + break; + } + + // Command: inf[o] [] + case 'info': + case 'inf': { + if (args.length > 2) { + throw new Error('Too many arguments after ' + cmd + '.'); + } + // Built the info request: + request = this.createLOLRequest('lol-info', 0, args[1], is_repeating); + break; + } + + // Command: path + case 'path': { + request = this.createRequest('lol-path'); + request.arguments = {}; + if (args.length > 2) { + request.arguments.id1 = extractObjId(args[1]); + request.arguments.id2 = extractObjId(args[2]); + } else { + request.arguments.id1 = 0; + request.arguments.id2 = extractObjId(args[1]); + } + break; + } + + // Command: print + case 'print': { + request = this.createRequest('lol-print'); + request.arguments = {}; + request.arguments.id = extractObjId(args[1]); + break; + } + + // Command: reset + case 'reset': { + request = this.createRequest('lol-reset'); + break; + } + + default: + throw new Error('Invalid arguments.'); + } + return request.toJSONProtocol(); +}; + + // Create a JSON request for the threads command. DebugRequest.prototype.threadsCommandToJSONRequest_ = function(args) { // Build a threads request from the text command. @@ -1239,6 +1554,49 @@ DebugRequest.prototype.helpCommand_ = function(args) { print(''); print('gc - runs the garbage collector'); print(''); + + if (lol_is_enabled) { + print('liveobjectlist|lol - live object list tracking.'); + print(' where can be:'); + print(' c[apture] - captures a LOL list.'); + print(' clear|del[ete] |all - clears LOL of id .'); + print(' If \'all\' is unspecified instead, will clear all.'); + print(' diff []'); + print(' - prints the diff between LOLs id1 and id2.'); + print(' - also see below.'); + print(' getid
- gets the obj id for the specified address if available.'); + print(' The address must be in hex form prefixed with 0x.'); + print(' inf[o] [] - lists summary info of all LOL lists.'); + print(' If N is specified, will print N items at a time.'); + print(' [l[ist]] []'); + print(' - prints the listing of objects in LOL id.'); + print(' - also see below.'); + print(' reset - clears all LOL lists.'); + print(' ret[ainers] []'); + print(' - prints the list of retainers of obj id.'); + print(' - also see below.'); + print(' path - prints the retaining path from obj id1 to id2.'); + print(' If only one id is specified, will print the path from'); + print(' roots to the specified object if available.'); + print(' print - prints the obj for the specified obj id if available.'); + print(''); + print(' includes:'); + print(' [v[erbose]] - do verbose dump.'); + print(' [] - dump N items at a time. Implies verbose dump.'); + print(' If unspecified, N will default to '+ + kDefaultLolLinesToPrintAtATime+'. Max N is '+ + kMaxLolLinesToPrintAtATime+'.'); + print(' [i[ndex] ] - start dump from index i. Implies verbose dump.'); + print(' [t[ype] ] - filter by type.'); + print(' [sp[ace] ] - filter by heap space where is one of'); + print(' { cell, code, lo, map, new, old-data, old-pointer }.'); + print(''); + print(' If the verbose option, or an option that implies a verbose dump'); + print(' is specified, then a verbose dump will requested. Else, a summary dump'); + print(' will be requested.'); + print(''); + } + print('trace compile'); // hidden command: trace debug json - toggles tracing of debug json packets print(''); @@ -1339,6 +1697,237 @@ function refObjectToString_(protocolPackage, handle) { } +function decodeLolCaptureResponse(body) { + var result; + result = 'Captured live object list '+ body.id + + ': count '+ body.count + ' size ' + body.size; + return result; +} + + +function decodeLolDeleteResponse(body) { + var result; + result = 'Deleted live object list '+ body.id; + return result; +} + + +function digitsIn(value) { + var digits = 0; + if (value === 0) value = 1; + while (value >= 1) { + digits++; + value /= 10; + } + return digits; +} + + +function padding(value, max_digits) { + var padding_digits = max_digits - digitsIn(value); + var padding = ''; + while (padding_digits > 0) { + padding += ' '; + padding_digits--; + } + return padding; +} + + +function decodeLolInfoResponse(body) { + var result; + var lists = body.lists; + var length = lists.length; + var first_index = body.first_index + 1; + var has_more = ((first_index + length) <= body.count); + result = 'captured live object lists'; + if (has_more || (first_index != 1)) { + result += ' ['+ length +' of '+ body.count + + ': starting from '+ first_index +']'; + } + result += ':\n'; + var max_digits = digitsIn(body.count); + var last_count = 0; + var last_size = 0; + for (var i = 0; i < length; i++) { + var entry = lists[i]; + var count = entry.count; + var size = entry.size; + var index = first_index + i; + result += ' [' + padding(index, max_digits) + index + '] id '+ entry.id + + ': count '+ count; + if (last_count > 0) { + result += '(+' + (count - last_count) + ')'; + } + result += ' size '+ size; + if (last_size > 0) { + result += '(+' + (size - last_size) + ')'; + } + result += '\n'; + last_count = count; + last_size = size; + } + result += ' total: '+length+' lists\n'; + if (has_more) { + result += ' -- press for more --\n'; + } else { + repeat_cmd_line = ''; + } + if (length === 0) result += ' none\n'; + + return result; +} + + +function decodeLolListResponse(body, title) { + + var result; + var total_count = body.count; + var total_size = body.size; + var length; + var max_digits; + var i; + var entry; + var index; + + var max_count_digits = digitsIn(total_count); + var max_size_digits; + + var summary = body.summary; + if (summary) { + + var roots_count = 0; + var found_root = body.found_root || 0; + var found_weak_root = body.found_weak_root || 0; + + // Print the summary result: + result = 'summary of objects:\n'; + length = summary.length; + if (found_root !== 0) { + roots_count++; + } + if (found_weak_root !== 0) { + roots_count++; + } + max_digits = digitsIn(length + roots_count); + max_size_digits = digitsIn(total_size); + + index = 1; + if (found_root !== 0) { + result += ' [' + padding(index, max_digits) + index + '] ' + + ' count '+ 1 + padding(0, max_count_digits) + + ' '+ padding(0, max_size_digits+1) + + ' : \n'; + index++; + } + if (found_weak_root !== 0) { + result += ' [' + padding(index, max_digits) + index + '] ' + + ' count '+ 1 + padding(0, max_count_digits) + + ' '+ padding(0, max_size_digits+1) + + ' : \n'; + index++; + } + + for (i = 0; i < length; i++) { + entry = summary[i]; + var count = entry.count; + var size = entry.size; + result += ' [' + padding(index, max_digits) + index + '] ' + + ' count '+ count + padding(count, max_count_digits) + + ' size '+ size + padding(size, max_size_digits) + + ' : <' + entry.desc + '>\n'; + index++; + } + result += '\n total count: '+(total_count+roots_count)+'\n'; + if (body.size) { + result += ' total size: '+body.size+'\n'; + } + + } else { + // Print the full dump result: + var first_index = body.first_index + 1; + var elements = body.elements; + length = elements.length; + var has_more = ((first_index + length) <= total_count); + result = title; + if (has_more || (first_index != 1)) { + result += ' ['+ length +' of '+ total_count + + ': starting from '+ first_index +']'; + } + result += ':\n'; + if (length === 0) result += ' none\n'; + max_digits = digitsIn(length); + + var max_id = 0; + var max_size = 0; + for (i = 0; i < length; i++) { + entry = elements[i]; + if (entry.id > max_id) max_id = entry.id; + if (entry.size > max_size) max_size = entry.size; + } + var max_id_digits = digitsIn(max_id); + max_size_digits = digitsIn(max_size); + + for (i = 0; i < length; i++) { + entry = elements[i]; + index = first_index + i; + result += ' ['+ padding(index, max_digits) + index +']'; + if (entry.id !== 0) { + result += ' @' + entry.id + padding(entry.id, max_id_digits) + + ': size ' + entry.size + ', ' + + padding(entry.size, max_size_digits) + entry.desc + '\n'; + } else { + // Must be a root or weak root: + result += ' ' + entry.desc + '\n'; + } + } + if (has_more) { + result += ' -- press for more --\n'; + } else { + repeat_cmd_line = ''; + } + if (length === 0) result += ' none\n'; + } + + return result; +} + + +function decodeLolDiffResponse(body) { + var title = 'objects'; + return decodeLolListResponse(body, title); +} + + +function decodeLolRetainersResponse(body) { + var title = 'retainers for @' + body.id; + return decodeLolListResponse(body, title); +} + + +function decodeLolPathResponse(body) { + return body.path; +} + + +function decodeLolResetResponse(body) { + return 'Reset all live object lists.'; +} + + +function decodeLolGetIdResponse(body) { + if (body.id == 0) { + return 'Address is invalid, or object has been moved or collected'; + } + return 'obj id is @' + body.id; +} + + +function decodeLolPrintResponse(body) { + return body.dump; +} + + // Rounds number 'num' to 'length' decimal places. function roundNumber(num, length) { var factor = Math.pow(10, length); @@ -1510,6 +2099,7 @@ function DebugResponseDetails(response) { case 'evaluate': case 'lookup': + case 'getobj': if (last_cmd == 'p' || last_cmd == 'print') { result = body.text; } else { @@ -1671,6 +2261,34 @@ function DebugResponseDetails(response) { } break; + case 'lol-capture': + details.text = decodeLolCaptureResponse(body); + break; + case 'lol-delete': + details.text = decodeLolDeleteResponse(body); + break; + case 'lol-diff': + details.text = decodeLolDiffResponse(body); + break; + case 'lol-getid': + details.text = decodeLolGetIdResponse(body); + break; + case 'lol-info': + details.text = decodeLolInfoResponse(body); + break; + case 'lol-print': + details.text = decodeLolPrintResponse(body); + break; + case 'lol-reset': + details.text = decodeLolResetResponse(body); + break; + case 'lol-retainers': + details.text = decodeLolRetainersResponse(body); + break; + case 'lol-path': + details.text = decodeLolPathResponse(body); + break; + default: details.text = 'Response for unknown command \'' + response.command() + '\'' + diff --git a/src/debug-debugger.js b/src/debug-debugger.js index 1adf73ac7..bc0f966fb 100644 --- a/src/debug-debugger.js +++ b/src/debug-debugger.js @@ -109,6 +109,7 @@ var debugger_flags = { } }, }; +var lol_is_enabled = %HasLOLEnabled(); // Create a new break point object and add it to the list of break points. @@ -1391,6 +1392,8 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) this.scopeRequest_(request, response); } else if (request.command == 'evaluate') { this.evaluateRequest_(request, response); + } else if (lol_is_enabled && request.command == 'getobj') { + this.getobjRequest_(request, response); } else if (request.command == 'lookup') { this.lookupRequest_(request, response); } else if (request.command == 'references') { @@ -1418,6 +1421,28 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) } else if (request.command == 'gc') { this.gcRequest_(request, response); + // LiveObjectList tools: + } else if (lol_is_enabled && request.command == 'lol-capture') { + this.lolCaptureRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-delete') { + this.lolDeleteRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-diff') { + this.lolDiffRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-getid') { + this.lolGetIdRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-info') { + this.lolInfoRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-reset') { + this.lolResetRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-retainers') { + this.lolRetainersRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-path') { + this.lolPathRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-print') { + this.lolPrintRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-stats') { + this.lolStatsRequest_(request, response); + } else { throw new Error('Unknown command "' + request.command + '" in request'); } @@ -2011,6 +2036,24 @@ DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) { }; +DebugCommandProcessor.prototype.getobjRequest_ = function(request, response) { + if (!request.arguments) { + return response.failed('Missing arguments'); + } + + // Pull out arguments. + var obj_id = request.arguments.obj_id; + + // Check for legal arguments. + if (IS_UNDEFINED(obj_id)) { + return response.failed('Argument "obj_id" missing'); + } + + // Dump the object. + response.body = MakeMirror(%GetLOLObj(obj_id)); +}; + + DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) { if (!request.arguments) { return response.failed('Missing arguments'); @@ -2341,6 +2384,84 @@ DebugCommandProcessor.prototype.gcRequest_ = function(request, response) { }; +DebugCommandProcessor.prototype.lolCaptureRequest_ = + function(request, response) { + response.body = %CaptureLOL(); +}; + + +DebugCommandProcessor.prototype.lolDeleteRequest_ = + function(request, response) { + var id = request.arguments.id; + var result = %DeleteLOL(id); + if (result) { + response.body = { id: id }; + } else { + response.failed('Failed to delete: live object list ' + id + ' not found.'); + } +}; + + +DebugCommandProcessor.prototype.lolDiffRequest_ = function(request, response) { + var id1 = request.arguments.id1; + var id2 = request.arguments.id2; + var verbose = request.arguments.verbose; + var filter = request.arguments.filter; + if (verbose === true) { + var start = request.arguments.start; + var count = request.arguments.count; + response.body = %DumpLOL(id1, id2, start, count, filter); + } else { + response.body = %SummarizeLOL(id1, id2, filter); + } +}; + + +DebugCommandProcessor.prototype.lolGetIdRequest_ = function(request, response) { + var address = request.arguments.address; + response.body = {}; + response.body.id = %GetLOLObjId(address); +}; + + +DebugCommandProcessor.prototype.lolInfoRequest_ = function(request, response) { + var start = request.arguments.start; + var count = request.arguments.count; + response.body = %InfoLOL(start, count); +}; + + +DebugCommandProcessor.prototype.lolResetRequest_ = function(request, response) { + %ResetLOL(); +}; + + +DebugCommandProcessor.prototype.lolRetainersRequest_ = + function(request, response) { + var id = request.arguments.id; + var verbose = request.arguments.verbose; + var start = request.arguments.start; + var count = request.arguments.count; + var filter = request.arguments.filter; + + response.body = %GetLOLObjRetainers(id, Mirror.prototype, verbose, + start, count, filter); +}; + + +DebugCommandProcessor.prototype.lolPathRequest_ = function(request, response) { + var id1 = request.arguments.id1; + var id2 = request.arguments.id2; + response.body = {}; + response.body.path = %GetLOLPath(id1, id2, Mirror.prototype); +}; + + +DebugCommandProcessor.prototype.lolPrintRequest_ = function(request, response) { + var id = request.arguments.id; + response.body = {}; + response.body.dump = %PrintLOLObj(id); +}; // Check whether the previously processed command caused the VM to become diff --git a/src/runtime.cc b/src/runtime.cc index b47cc3ab0..0c15f60f3 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -43,6 +43,7 @@ #include "global-handles.h" #include "jsregexp.h" #include "liveedit.h" +#include "liveobjectlist-inl.h" #include "parser.h" #include "platform.h" #include "runtime.h" @@ -10948,6 +10949,207 @@ static MaybeObject* Runtime_GetHeapUsage(Arguments args) { } return Smi::FromInt(usage); } + + +// Captures a live object list from the present heap. +static MaybeObject* Runtime_HasLOLEnabled(Arguments args) { +#ifdef LIVE_OBJECT_LIST + return Heap::true_value(); +#else + return Heap::false_value(); +#endif +} + + +// Captures a live object list from the present heap. +static MaybeObject* Runtime_CaptureLOL(Arguments args) { +#ifdef LIVE_OBJECT_LIST + return LiveObjectList::Capture(); +#else + return Heap::undefined_value(); +#endif +} + + +// Deletes the specified live object list. +static MaybeObject* Runtime_DeleteLOL(Arguments args) { +#ifdef LIVE_OBJECT_LIST + CONVERT_SMI_CHECKED(id, args[0]); + bool success = LiveObjectList::Delete(id); + return success ? Heap::true_value() : Heap::false_value(); +#else + return Heap::undefined_value(); +#endif +} + + +// Generates the response to a debugger request for a dump of the objects +// contained in the difference between the captured live object lists +// specified by id1 and id2. +// If id1 is 0 (i.e. not a valid lol), then the whole of lol id2 will be +// dumped. +static MaybeObject* Runtime_DumpLOL(Arguments args) { +#ifdef LIVE_OBJECT_LIST + HandleScope scope; + CONVERT_SMI_CHECKED(id1, args[0]); + CONVERT_SMI_CHECKED(id2, args[1]); + CONVERT_SMI_CHECKED(start, args[2]); + CONVERT_SMI_CHECKED(count, args[3]); + CONVERT_ARG_CHECKED(JSObject, filter_obj, 4); + EnterDebugger enter_debugger; + return LiveObjectList::Dump(id1, id2, start, count, filter_obj); +#else + return Heap::undefined_value(); +#endif +} + + +// Gets the specified object as requested by the debugger. +// This is only used for obj ids shown in live object lists. +static MaybeObject* Runtime_GetLOLObj(Arguments args) { +#ifdef LIVE_OBJECT_LIST + CONVERT_SMI_CHECKED(obj_id, args[0]); + Object* result = LiveObjectList::GetObj(obj_id); + return result; +#else + return Heap::undefined_value(); +#endif +} + + +// Gets the obj id for the specified address if valid. +// This is only used for obj ids shown in live object lists. +static MaybeObject* Runtime_GetLOLObjId(Arguments args) { +#ifdef LIVE_OBJECT_LIST + HandleScope scope; + CONVERT_ARG_CHECKED(String, address, 0); + Object* result = LiveObjectList::GetObjId(address); + return result; +#else + return Heap::undefined_value(); +#endif +} + + +// Gets the retainers that references the specified object alive. +static MaybeObject* Runtime_GetLOLObjRetainers(Arguments args) { +#ifdef LIVE_OBJECT_LIST + HandleScope scope; + CONVERT_SMI_CHECKED(obj_id, args[0]); + RUNTIME_ASSERT(args[1]->IsUndefined() || args[1]->IsJSObject()); + RUNTIME_ASSERT(args[2]->IsUndefined() || args[2]->IsBoolean()); + RUNTIME_ASSERT(args[3]->IsUndefined() || args[3]->IsSmi()); + RUNTIME_ASSERT(args[4]->IsUndefined() || args[4]->IsSmi()); + CONVERT_ARG_CHECKED(JSObject, filter_obj, 5); + + Handle instance_filter; + if (args[1]->IsJSObject()) { + instance_filter = args.at(1); + } + bool verbose = false; + if (args[2]->IsBoolean()) { + verbose = args[2]->IsTrue(); + } + int start = 0; + if (args[3]->IsSmi()) { + start = Smi::cast(args[3])->value(); + } + int limit = Smi::kMaxValue; + if (args[4]->IsSmi()) { + limit = Smi::cast(args[4])->value(); + } + + return LiveObjectList::GetObjRetainers(obj_id, + instance_filter, + verbose, + start, + limit, + filter_obj); +#else + return Heap::undefined_value(); +#endif +} + + +// Gets the reference path between 2 objects. +static MaybeObject* Runtime_GetLOLPath(Arguments args) { +#ifdef LIVE_OBJECT_LIST + HandleScope scope; + CONVERT_SMI_CHECKED(obj_id1, args[0]); + CONVERT_SMI_CHECKED(obj_id2, args[1]); + RUNTIME_ASSERT(args[2]->IsUndefined() || args[2]->IsJSObject()); + + Handle instance_filter; + if (args[2]->IsJSObject()) { + instance_filter = args.at(2); + } + + Object* result = + LiveObjectList::GetPath(obj_id1, obj_id2, instance_filter); + return result; +#else + return Heap::undefined_value(); +#endif +} + + +// Generates the response to a debugger request for a list of all +// previously captured live object lists. +static MaybeObject* Runtime_InfoLOL(Arguments args) { +#ifdef LIVE_OBJECT_LIST + CONVERT_SMI_CHECKED(start, args[0]); + CONVERT_SMI_CHECKED(count, args[1]); + return LiveObjectList::Info(start, count); +#else + return Heap::undefined_value(); +#endif +} + + +// Gets a dump of the specified object as requested by the debugger. +// This is only used for obj ids shown in live object lists. +static MaybeObject* Runtime_PrintLOLObj(Arguments args) { +#ifdef LIVE_OBJECT_LIST + HandleScope scope; + CONVERT_SMI_CHECKED(obj_id, args[0]); + Object* result = LiveObjectList::PrintObj(obj_id); + return result; +#else + return Heap::undefined_value(); +#endif +} + + +// Resets and releases all previously captured live object lists. +static MaybeObject* Runtime_ResetLOL(Arguments args) { +#ifdef LIVE_OBJECT_LIST + LiveObjectList::Reset(); + return Heap::undefined_value(); +#else + return Heap::undefined_value(); +#endif +} + + +// Generates the response to a debugger request for a summary of the types +// of objects in the difference between the captured live object lists +// specified by id1 and id2. +// If id1 is 0 (i.e. not a valid lol), then the whole of lol id2 will be +// summarized. +static MaybeObject* Runtime_SummarizeLOL(Arguments args) { +#ifdef LIVE_OBJECT_LIST + HandleScope scope; + CONVERT_SMI_CHECKED(id1, args[0]); + CONVERT_SMI_CHECKED(id2, args[1]); + CONVERT_ARG_CHECKED(JSObject, filter_obj, 2); + + EnterDebugger enter_debugger; + return LiveObjectList::Summarize(id1, id2, filter_obj); +#else + return Heap::undefined_value(); +#endif +} + #endif // ENABLE_DEBUGGER_SUPPORT diff --git a/src/runtime.h b/src/runtime.h index 52a6e1fd3..9dd6eda0a 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -376,7 +376,21 @@ namespace internal { \ F(SetFlags, 1, 1) \ F(CollectGarbage, 1, 1) \ - F(GetHeapUsage, 0, 1) + F(GetHeapUsage, 0, 1) \ + \ + /* LiveObjectList support*/ \ + F(HasLOLEnabled, 0, 1) \ + F(CaptureLOL, 0, 1) \ + F(DeleteLOL, 1, 1) \ + F(DumpLOL, 5, 1) \ + F(GetLOLObj, 1, 1) \ + F(GetLOLObjId, 1, 1) \ + F(GetLOLObjRetainers, 6, 1) \ + F(GetLOLPath, 3, 1) \ + F(InfoLOL, 2, 1) \ + F(PrintLOLObj, 1, 1) \ + F(ResetLOL, 0, 1) \ + F(SummarizeLOL, 3, 1) #else #define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F) -- 2.34.1