diff options
Diffstat (limited to 'deps/v8/tools/windbg.js')
-rw-r--r-- | deps/v8/tools/windbg.js | 315 |
1 files changed, 218 insertions, 97 deletions
diff --git a/deps/v8/tools/windbg.js b/deps/v8/tools/windbg.js index 91877b4c61..bcf45a496b 100644 --- a/deps/v8/tools/windbg.js +++ b/deps/v8/tools/windbg.js @@ -20,9 +20,6 @@ function help() { print(" e.g. !jlh(\"key\") or !jlh(\"this->receiver_\")"); print(" !job(address_or_taggedint)"); print(" prints object at the address, e.g. !job(0x235cb869f9)"); - print(" !jobs(start_address, count)"); - print(" prints 'count' objects from a continuous range of Object"); - print(" pointers, e.g. !jobs(0x5f7270, 42)"); print(" !jst() or !jst"); print(" prints javascript stack (output goes into the console)"); print(" !jsbp() or !jsbp"); @@ -53,6 +50,11 @@ function help() { print(" !where(address)"); print(" prints name of the space and address of the MemoryChunk the"); print(" 'address' is from, e.g. !where(0x235cb869f9)"); + print(" !rs(chunk_address, set_id = 0)"); + print(" prints slots from the remembered set in the MemoryChunk. If"); + print(" 'chunk_address' isn't specified, prints for all chunks in the"); + print(" old space; 'set_id' should match RememberedSetType enum,"); + print(" e.g. !rs, !rs 0x2fb14780000, !rs(0x2fb14780000, 1)"); print(""); print("--------------------------------------------------------------------"); @@ -114,14 +116,6 @@ function print(s) { host.diagnostics.debugLog(s + "\n"); } -function print_filtered(obj, filter) { - for (let line of obj) { - if (!filter || line.indexOf(filter) != -1) { - print(line); - } - } -} - function inspect(s) { for (let k of Reflect.ownKeys(s)) { // Attempting to print either of: @@ -140,10 +134,23 @@ function hex(number) { /*============================================================================= Utils (postmortem and live) =============================================================================*/ -// WinDbg wraps large integers into objects that fail isInteger test (and, -// consequently fail isSafeInteger test even if the original value was a safe -// integer). I cannot figure out how to extract the original value from the -// wrapper object so doing it via conversion to a string. Brrr. Ugly. +// WinDbg wraps large integers (0x80000000+) into an object of library type that +// fails isInteger test (and, consequently fail isSafeInteger test even if the +// original value was a safe integer). +// However, that library type does have a set of methods on it which you can use +// to force conversion: +// .asNumber() / .valueOf(): Performs conversion to JavaScript number. +// Throws if the ordinal part of the 64-bit number does not pack into JavaScript +// number without loss of precision. +// .convertToNumber(): Performs conversion to JavaScript number. +// Does NOT throw if the ordinal part of the 64-bit number does not pack into +// JavaScript number. This will simply result in loss of precision. +// The library will also add these methods to the prototype for the standard +// number prototype. Meaning you can always .asNumber() / .convertToNumber() to +// get either JavaScript number or the private Int64 type into a JavaScript +// number. +// We could use the conversion functions but it seems that doing the conversion +// via toString is just as good and slightly more generic... function int(val) { if (typeof val === 'number') { return Number.isInteger(val) ? val : undefined; @@ -192,6 +199,26 @@ function get_register(name) { .Registers.User[name]; } +// JS doesn't do bitwise operations on large integers, so let's do it ourselves +// using hex string representation. +function bitwise_and(l, r) { + l = hex(l); + let l_length = l.length; + r = hex(r); + let r_length = r.length; + let res = ""; + let length = Math.min(l_length, r_length) - 2; // to account for "0x" + for (let i = 1; i <= length; i++) { + res = (parseInt(l[l_length - i], 16) & parseInt(r[r_length - i], 16)) + .toString(16) + res; + } + return parseInt(res, 16); +} + + +/*============================================================================= + Script setup +=============================================================================*/ // In debug builds v8 code is compiled into v8.dll, and in release builds // the code is compiled directly into the executable. If you are debugging some // other embedder, run !set_module and provide the module name to use. @@ -209,9 +236,18 @@ function module_name(use_this_module) { return m.Name.indexOf("\\v8.dll") !== -1; }); - if (v8) { + let v8_test = host.namespace.Debugger.State.DebuggerVariables.curprocess + .Modules.Where( + function(m) { + return m.Name.indexOf("\\v8_for_testing.dll") !== -1; + }); + + if (v8.Count() > 0) { module_name_cache = "v8"; } + else if (v8_test.Count() > 0) { + module_name_cache = "v8_for_testing"; + } else { for (let exe_name in known_exes) { let exe = host.namespace.Debugger.State.DebuggerVariables.curprocess @@ -219,7 +255,7 @@ function module_name(use_this_module) { function(m) { return m.Name.indexOf(`\\${exe_name}.exe`) !== -1; }); - if (exe) { + if (exe.Count() > 0) { module_name_cache = exe_name; break; } @@ -234,6 +270,25 @@ function module_name(use_this_module) { return module_name_cache; }; +let using_ptr_compr = false; +let isolate_address = 0; +function set_isolate_address(addr, ptr_compr) { + isolate_address = addr; + + if (typeof ptr_compr === 'undefined') { + ptr_compr = (bitwise_and(isolate_address, 0xffffffff) == 0); + } + using_ptr_compr = ptr_compr; + + if (using_ptr_compr) { + print("The target is using pointer compression."); + } +} + + +/*============================================================================= + Wrappers around V8's printing functions and other utils for live-debugging +=============================================================================*/ function make_call(fn) { if (!supports_call_command()) { print("ERROR: This command is supported in live sessions only!"); @@ -249,16 +304,8 @@ function make_call(fn) { return output; } - -/*============================================================================= - Wrappers around V8's printing functions and other utils for live-debugging -=============================================================================*/ - -/*----------------------------------------------------------------------------- - 'address' should be an int (so in hex must include '0x' prefix). ------------------------------------------------------------------------------*/ function print_object(address) { - let output = make_call(`_v8_internal_Print_Object(${address})`); + let output = make_call(`_v8_internal_Print_Object(${decomp(address)})`); // skip the first few lines with meta info of .call command let skip_line = true; @@ -273,43 +320,13 @@ function print_object(address) { } } -/*----------------------------------------------------------------------------- - 'handle_to_object' should be a name of a Handle which can be a local - variable or it can be a member variable like "this->receiver_". ------------------------------------------------------------------------------*/ function print_object_from_handle(handle_to_object) { let handle = host.evaluateExpression(handle_to_object); let location = handle.location_; - let pobj = poi(location.address); + let pobj = poi(location.address); // handles use uncompressed pointers print_object(pobj); } -/*----------------------------------------------------------------------------- - 'start_address' should be an int (so in hex must include '0x' prefix), it can - point at any continuous memory that contains Object pointers. ------------------------------------------------------------------------------*/ -function print_objects_array(start_address, count) { - const ptr_size = pointer_size(); - let ctl = host.namespace.Debugger.Utility.Control; - let addr_int = start_address; - for (let i = 0; i < count; i++) { - const addr_hex = hex(addr_int); - - // TODO: Tried using createPointerObject but it throws unknown exception - // from ChakraCore. Why? - //let obj = host.createPointerObject(addr_hex, module, "void*"); - - let output = ctl.ExecuteCommand(`dp ${addr_hex} l1`); - let item = ""; - for (item of output) {} // 005f7270 34604101 - let deref = `0x${item.split(" ").pop()}`; - print(`${addr_hex} -> ${deref}`); - print_object(deref); - - addr_int += ptr_size; - } -} - function print_js_stack() { make_call("_v8_internal_Print_StackTrace()"); } @@ -323,21 +340,47 @@ function set_user_js_bp() { /*============================================================================= Managed heap related functions (live and post-mortem debugging) =============================================================================*/ -let isolate_address = 0; -function set_isolate_address(addr) { - isolate_address = addr; +/*----------------------------------------------------------------------------- + Pointer compression +-----------------------------------------------------------------------------*/ +function tagged_size() { + return using_ptr_compr ? 4 : pointer_size(); } +function get_compressed_ptr_base() { + if (!using_ptr_compr) return 0; + + return isolate_address; +} + +function decomp(value) { + if (value > 0xffffffff) return value; + return get_compressed_ptr_base() + value; +} + +// Adjust for possible pointer compression ('address' is assumed to be on the +// managed heap). +function poim(address) { + try { + // readMemoryValues throws if cannot read from 'address'. + return host.memory.readMemoryValues(decomp(address), 1, tagged_size())[0]; + } + catch (e){} +} + +/*----------------------------------------------------------------------------- + Exploring objects +-----------------------------------------------------------------------------*/ function is_map(addr) { let address = int(addr); if (!Number.isSafeInteger(address) || address % 2 == 0) return false; // the first field in all objects, including maps, is a map pointer, but for // maps the pointer is always the same - the meta map that points to itself. - const map_addr = int(poi(address - 1)); + const map_addr = int(poim(address - 1)); if (!Number.isSafeInteger(map_addr)) return false; - const map_map_addr = int(poi(map_addr - 1)); + const map_map_addr = int(poim(map_addr - 1)); if (!Number.isSafeInteger(map_map_addr)) return false; return (map_addr === map_map_addr); @@ -348,12 +391,12 @@ function is_likely_object(addr) { if (!Number.isSafeInteger(address) || address % 2 == 0) return false; // the first field in all objects must be a map pointer - return is_map(poi(address - 1)); + return is_map(poim(address - 1)); } function find_object_near(aligned_addr, max_distance, step_op) { if (!step_op) { - const step = pointer_size(); + const step = tagged_size(); const prev = find_object_near(aligned_addr, max_distance, x => x - step); const next = @@ -364,14 +407,14 @@ function find_object_near(aligned_addr, max_distance, step_op) { return (addr - prev <= next - addr) ? prev : next; } - let maybe_map_addr = poi(aligned_addr); + let maybe_map_addr = poim(aligned_addr); let iters = 0; while (maybe_map_addr && iters < max_distance) { if (is_map(maybe_map_addr)) { return aligned_addr; } aligned_addr = step_op(aligned_addr); - maybe_map_addr = poi(aligned_addr); + maybe_map_addr = poim(aligned_addr); iters++; } } @@ -379,7 +422,7 @@ function find_object_near(aligned_addr, max_distance, step_op) { function find_object_prev(addr, max_distance) { if (!Number.isSafeInteger(int(addr))) return; - const ptr_size = pointer_size(); + const ptr_size = tagged_size(); const aligned_addr = addr - (addr % ptr_size); return find_object_near(aligned_addr, max_distance, x => x - ptr_size); } @@ -387,7 +430,7 @@ function find_object_prev(addr, max_distance) { function find_object_next(addr, max_distance) { if (!Number.isSafeInteger(int(addr))) return; - const ptr_size = pointer_size(); + const ptr_size = tagged_size(); const aligned_addr = addr - (addr % ptr_size) + ptr_size; return find_object_near(aligned_addr, max_distance, x => x + ptr_size); } @@ -400,7 +443,7 @@ function print_object_prev(addr, max_slots = 100) { } else { print( - `found object: ${hex(obj_addr + 1)} : ${hex(poi(obj_addr))}`); + `found object: ${hex(obj_addr + 1)} : ${hex(poim(obj_addr))}`); } } @@ -412,7 +455,7 @@ function print_object_next(addr, max_slots = 100) { } else { print( - `found object: ${hex(obj_addr + 1)} : ${hex(poi(obj_addr))}`); + `found object: ${hex(obj_addr + 1)} : ${hex(poim(obj_addr))}`); } } @@ -422,10 +465,11 @@ function print_objects_in_range(start, end){ if (!Number.isSafeInteger(int(start)) || !Number.isSafeInteger(int(end))) { return; } - const ptr_size = pointer_size(); + if (start < ptr_size || end <= start) return; + let iters = (end - start) / ptr_size; - let cur = start; + let cur = start - ptr_size; print(`===============================================`); print(`objects in range ${hex(start)} - ${hex(end)}`); print(`===============================================`); @@ -434,7 +478,7 @@ function print_objects_in_range(start, end){ let obj = find_object_next(cur, iters); if (obj) { count++; - print(`${hex(obj + 1)} : ${hex(poi(obj))}`); + print(`${hex(obj + 1)} : ${hex(poim(obj))}`); iters = (end - cur) / ptr_size; } cur = obj + ptr_size; @@ -454,10 +498,10 @@ function print_objects_tree(root, depth_limit) { let path = []; function impl(obj, depth, depth_limit) { - const ptr_size = pointer_size(); + const ptr_size = tagged_size(); // print the current object and its map pointer const this_obj = - `${" ".repeat(2 * depth)}${hex(obj)} : ${hex(poi(obj - 1))}`; + `${" ".repeat(2 * depth)}${hex(obj)} : ${hex(poim(obj - 1))}`; const cutoff = depth_limit && depth == depth_limit - 1; print(`${this_obj}${cutoff ? " (...)" : ""}`); if (cutoff) return; @@ -472,7 +516,7 @@ function print_objects_tree(root, depth_limit) { let seen = new Set(path); while (!is_likely_object(cur + 1) && iter < 100) { iter++; - let field = poi(cur); + let field = poim(cur); if (is_likely_object(field)) { if (seen.has(field)) { print( @@ -491,7 +535,7 @@ function print_objects_tree(root, depth_limit) { } /*----------------------------------------------------------------------------- - Memory in each Space is organized into a linked list of memory chunks + Memory spaces -----------------------------------------------------------------------------*/ const NEVER_EVACUATE = 1 << 7; // see src\heap\spaces.h @@ -564,12 +608,6 @@ function find_chunk(address) { return undefined; } -/*----------------------------------------------------------------------------- - Print memory chunks from spaces in the current Heap - 'isolate_address' should be an int (so in hex must include '0x' prefix). - 'space': space separated string containing "all", "old", "new", "map", - "code", "ro [readonly]", "lo [large]", "nlo [newlarge]" ------------------------------------------------------------------------------*/ function print_memory(space = "all") { if (isolate_address == 0) { print("Please call !set_iso(isolate_address) first."); @@ -622,16 +660,13 @@ function print_memory(space = "all") { } } -/*----------------------------------------------------------------------------- - 'isolate_address' and 'address' should be ints (so in hex must include '0x' - prefix). ------------------------------------------------------------------------------*/ function print_owning_space(address) { if (isolate_address == 0) { print("Please call !set_iso(isolate_address) first."); return; } + address = decomp(address); let c = find_chunk(address); if (c) { print(`${hex(address)} is in ${c.space} (chunk: ${hex(c.address)})`); @@ -642,7 +677,7 @@ function print_owning_space(address) { } /*----------------------------------------------------------------------------- - + Handles -----------------------------------------------------------------------------*/ function print_handles_data(print_handles = false) { if (isolate_address == 0) { @@ -705,6 +740,9 @@ function print_handles_data(print_handles = false) { } } +/*----------------------------------------------------------------------------- + dp +-----------------------------------------------------------------------------*/ function pad_right(addr) { let addr_hex = hex(addr); return `${addr_hex}${" ".repeat(pointer_size() * 2 + 2 - addr_hex.length)}`; @@ -721,26 +759,109 @@ function dp(addr, count = 10) { return; } - const ptr_size = pointer_size(); + const ptr_size = tagged_size(); let aligned_addr = addr - (addr % ptr_size); - let val = poi(aligned_addr); + let val = poim(aligned_addr); let iter = 0; while (val && iter < count) { - const augm_map = is_map(val) ? "map" : ""; - const augm_obj = is_likely_object(val) && !is_map(val) ? "obj" : ""; - const augm_other = !is_map(val) && !is_likely_object(val) ? "val" : ""; - let c = find_chunk(val); + const map = is_map(val); + const obj = is_likely_object(val) && !map; + + const augm_map = map ? "map" : ""; + const augm_obj = obj ? "obj" : ""; + const augm_other = !map && !obj ? "val" : ""; + + let c = find_chunk(decomp(val)); const augm_space = c ? ` in ${c.space}` : ""; const augm = `${augm_map}${augm_obj}${augm_other}${augm_space}`; - print(`${pad_right(aligned_addr)} ${pad_right(val)} ${augm}`); + const full_ptr = using_ptr_compr ? + pad_right((map || obj) ? decomp(val) : val) : ""; + print(`${pad_right(aligned_addr)} ${pad_right(val)} ${full_ptr} ${augm}`); aligned_addr += ptr_size; - val = poi(aligned_addr); + val = poim(aligned_addr); iter++; } } +/*----------------------------------------------------------------------------- + Remembered Sets +-----------------------------------------------------------------------------*/ +// set ids: 0 = OLD_TO_NEW, 1 = 0 = OLD_TO_OLD +function print_remembered_set(chunk_addr, set_id = 0) { + if (!chunk_addr) { + if (isolate_address == 0) { + print("Please call !set_iso(isolate_address) or provide chunk address."); + return; + } + + let iso = cast(isolate_address, "v8::internal::Isolate"); + let h = iso.heap_; + let chunks = []; + get_chunks_space('old', h.old_space_.memory_chunk_list_.front_, chunks); + get_chunks_space('lo', h.lo_space_.memory_chunk_list_.front_, chunks); + for (let c of chunks) { + try { + print_remembered_set(c.address); + } + catch (e) { + print(`failed to process chunk ${hex(c.address)} due to ${e.message}`); + } + } + return; + } + + print(`Remembered set in chunk ${hex(chunk_addr)}`); + let chunk = cast(chunk_addr, "v8::internal::MemoryChunk"); + + // chunk.slot_set_ is an array of SlotSet's. For standard pages there is 0 or + // 1 item in the array, but for large pages there will be more. + const page_size = 256 * 1024; + const sets_count = Math.floor((chunk.size_ + page_size - 1) / page_size); + let rs = chunk.slot_set_[set_id]; + if (rs.isNull) { + print(` <empty>`); + return; + } + if (rs[0].page_start_ != chunk_addr) { + print(`page_start_ [${hex(rs.page_start_)}] doesn't match chunk_addr!`); + return; + } + + const ptr_size = tagged_size(); + let count = 0; + for (let s = 0; s < sets_count; s++){ + const buckets_count = rs[s].buckets_.Count(); + for (let b = 0; b < buckets_count; b++) { + let bucket = rs[s].buckets_[b]; + if (bucket.isNull) continue; + // there are 32 cells in each bucket, cell's size is 32 bits + print(` bucket ${hex(bucket.address.asNumber())}:`); + const first_cell = bucket.address.asNumber(); + for (let c = 0; c < 32; c++) { + let cell = host.memory.readMemoryValues( + first_cell + c * 4, 1, 4 /*size to read*/)[0]; + if (cell == 0) continue; + let mask = 1; + for (let bit = 0; bit < 32; bit++){ + if (cell & mask) { + count++; + const slot_offset = (b * 32 * 32 + c * 32 + bit) * ptr_size; + const slot = rs[s].page_start_ + slot_offset; + print(` ${hex(slot)} -> ${hex(poim(slot))}`); + } + mask = mask << 1; + } + } + } + } + + if (count == 0) print(` <empty>`); + else print(` ${count} remembered pointers in chunk ${hex(chunk_addr)}`); +} + + /*============================================================================= Initialize short aliased names for the most common commands =============================================================================*/ @@ -749,7 +870,6 @@ function initializeScript() { new host.functionAlias(help, "help"), new host.functionAlias(print_object_from_handle, "jlh"), new host.functionAlias(print_object, "job"), - new host.functionAlias(print_objects_array, "jobs"), new host.functionAlias(print_js_stack, "jst"), new host.functionAlias(set_isolate_address, "set_iso"), @@ -757,6 +877,7 @@ function initializeScript() { new host.functionAlias(print_memory, "mem"), new host.functionAlias(print_owning_space, "where"), new host.functionAlias(print_handles_data, "handles"), + new host.functionAlias(print_remembered_set, "rs"), new host.functionAlias(print_object_prev, "jo_prev"), new host.functionAlias(print_object_next, "jo_next"), |