| # Copyright 2014 the V8 project authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| # Print tagged object. | |
| define job | |
| call (void) _v8_internal_Print_Object((void*)($arg0)) | |
| end | |
| document job | |
| Print a v8 JavaScript object | |
| Usage: job tagged_ptr | |
| end | |
| # Print content of v8::internal::Handle. | |
| define jh | |
| call (void) _v8_internal_Print_Object(*((v8::internal::Object**)($arg0).location_)) | |
| end | |
| document jh | |
| Print content of a v8::internal::Handle | |
| Usage: jh internal_handle | |
| end | |
| # Print content of v8::Local handle. | |
| define jlh | |
| call (void) _v8_internal_Print_Object(*((v8::internal::Object**)($arg0).val_)) | |
| end | |
| document jlh | |
| Print content of a v8::Local handle | |
| Usage: jlh local_handle | |
| end | |
| # Print Code objects containing given PC. | |
| define jco | |
| call (void) _v8_internal_Print_Code((void*)($arg0)) | |
| end | |
| document jco | |
| Print a v8 Code object from an internal code address | |
| Usage: jco pc | |
| end | |
| # Print LayoutDescriptor. | |
| define jld | |
| call (void) _v8_internal_Print_LayoutDescriptor((void*)($arg0)) | |
| end | |
| document jld | |
| Print a v8 LayoutDescriptor object | |
| Usage: jld tagged_ptr | |
| end | |
| # Print TransitionTree. | |
| define jtt | |
| call (void) _v8_internal_Print_TransitionTree((void*)($arg0)) | |
| end | |
| document jtt | |
| Print the complete transition tree of the given v8 Map. | |
| Usage: jtt tagged_ptr | |
| end | |
| # Print JavaScript stack trace. | |
| define jst | |
| call (void) _v8_internal_Print_StackTrace() | |
| end | |
| document jst | |
| Print the current JavaScript stack trace | |
| Usage: jst | |
| end | |
| # Print TurboFan graph node. | |
| define pn | |
| call _v8_internal_Node_Print((void*)($arg0)) | |
| end | |
| document pn | |
| Print a v8 TurboFan graph node | |
| Usage: pn node_address | |
| end | |
| # Skip the JavaScript stack. | |
| define jss | |
| set $js_entry_sp=v8::internal::Isolate::Current()->thread_local_top()->js_entry_sp_ | |
| set $rbp=*(void**)$js_entry_sp | |
| set $rsp=$js_entry_sp + 2*sizeof(void*) | |
| set $pc=*(void**)($js_entry_sp+sizeof(void*)) | |
| end | |
| document jss | |
| Skip the jitted stack on x64 to where we entered JS last. | |
| Usage: jss | |
| end | |
| # Print stack trace with assertion scopes. | |
| define bta | |
| python | |
| import re | |
| frame_re = re.compile("^#(\d+)\s*(?:0x[a-f\d]+ in )?(.+) \(.+ at (.+)") | |
| assert_re = re.compile("^\s*(\S+) = .+<v8::internal::Per\w+AssertScope<v8::internal::(\S*), (false|true)>") | |
| btl = gdb.execute("backtrace full", to_string = True).splitlines() | |
| for l in btl: | |
| match = frame_re.match(l) | |
| if match: | |
| print("[%-2s] %-60s %-40s" % (match.group(1), match.group(2), match.group(3))) | |
| match = assert_re.match(l) | |
| if match: | |
| if match.group(3) == "false": | |
| prefix = "Disallow" | |
| color = "\033[91m" | |
| else: | |
| prefix = "Allow" | |
| color = "\033[92m" | |
| print("%s -> %s %s (%s)\033[0m" % (color, prefix, match.group(2), match.group(1))) | |
| end | |
| end | |
| document bta | |
| Print stack trace with assertion scopes | |
| Usage: bta | |
| end | |
| # Search for a pointer inside all valid pages. | |
| define space_find | |
| set $space = $arg0 | |
| set $current_page = $space->first_page() | |
| while ($current_page != 0) | |
| printf "# Searching in %p - %p\n", $current_page->area_start(), $current_page->area_end()-1 | |
| find $current_page->area_start(), $current_page->area_end()-1, $arg1 | |
| set $current_page = $current_page->next_page() | |
| end | |
| end | |
| define heap_find | |
| set $heap = v8::internal::Isolate::Current()->heap() | |
| printf "# Searching for %p in old_space ===============================\n", $arg0 | |
| space_find $heap->old_space() ($arg0) | |
| printf "# Searching for %p in map_space ===============================\n", $arg0 | |
| space_find $heap->map_space() $arg0 | |
| printf "# Searching for %p in code_space ===============================\n", $arg0 | |
| space_find $heap->code_space() $arg0 | |
| end | |
| document heap_find | |
| Find the location of a given address in V8 pages. | |
| Usage: heap_find address | |
| end | |
| # The 'disassembly-flavor' command is only available on i386 and x84_64. | |
| python | |
| try: | |
| gdb.execute("set disassembly-flavor intel") | |
| except gdb.error: | |
| pass | |
| end | |
| set disable-randomization off | |
| # Install a handler whenever the debugger stops due to a signal. It walks up the | |
| # stack looking for V8_Dcheck and moves the frame to the one above it so it's | |
| # immediately at the line of code that triggered the DCHECK. | |
| python | |
| def dcheck_stop_handler(event): | |
| frame = gdb.selected_frame() | |
| select_frame = None | |
| message = None | |
| count = 0 | |
| # limit stack scanning since they're usually shallow and otherwise stack | |
| # overflows can be very slow. | |
| while frame is not None and count < 7: | |
| count += 1 | |
| if frame.name() == 'V8_Dcheck': | |
| frame_message = gdb.lookup_symbol('message', frame.block())[0] | |
| if frame_message: | |
| message = frame_message.value(frame).string() | |
| select_frame = frame.older() | |
| break | |
| if frame.name() is not None and frame.name().startswith('V8_Fatal'): | |
| select_frame = frame.older() | |
| frame = frame.older() | |
| if select_frame is not None: | |
| select_frame.select() | |
| gdb.execute('frame') | |
| if message: | |
| print('DCHECK error: {}'.format(message)) | |
| gdb.events.stop.connect(dcheck_stop_handler) | |
| end | |
| # Code imported from chromium/src/tools/gdb/gdbinit | |
| python | |
| import os | |
| import subprocess | |
| import sys | |
| compile_dirs = set() | |
| def get_current_debug_file_directories(): | |
| dir = gdb.execute("show debug-file-directory", to_string=True) | |
| dir = dir[ | |
| len('The directory where separate debug symbols are searched for is "' | |
| ):-len('".') - 1] | |
| return set(dir.split(":")) | |
| def add_debug_file_directory(dir): | |
| # gdb has no function to add debug-file-directory, simulates that by using | |
| # `show debug-file-directory` and `set debug-file-directory <directories>`. | |
| current_dirs = get_current_debug_file_directories() | |
| current_dirs.add(dir) | |
| gdb.execute( | |
| "set debug-file-directory %s" % ":".join(current_dirs), to_string=True) | |
| def newobj_handler(event): | |
| global compile_dirs | |
| compile_dir = os.path.dirname(event.new_objfile.filename) | |
| if not compile_dir: | |
| return | |
| if compile_dir in compile_dirs: | |
| return | |
| compile_dirs.add(compile_dir) | |
| # Add source path | |
| gdb.execute("dir %s" % compile_dir) | |
| # Need to tell the location of .dwo files. | |
| # https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html | |
| # https://crbug.com/603286#c35 | |
| add_debug_file_directory(compile_dir) | |
| # Event hook for newly loaded objfiles. | |
| # https://sourceware.org/gdb/onlinedocs/gdb/Events-In-Python.html | |
| gdb.events.new_objfile.connect(newobj_handler) | |
| gdb.execute("set environment V8_GDBINIT_SOURCED=1") | |
| end |