2014-07-07 16:21:29 +00:00
|
|
|
# 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.
|
|
|
|
|
2018-11-12 21:40:17 +00:00
|
|
|
# Print tagged object.
|
2014-07-07 16:21:29 +00:00
|
|
|
define job
|
2019-03-01 11:30:19 +00:00
|
|
|
call (void) _v8_internal_Print_Object((void*)($arg0))
|
2014-07-07 16:21:29 +00:00
|
|
|
end
|
|
|
|
document job
|
|
|
|
Print a v8 JavaScript object
|
|
|
|
Usage: job tagged_ptr
|
|
|
|
end
|
|
|
|
|
2018-11-12 21:40:17 +00:00
|
|
|
# Print content of v8::internal::Handle.
|
|
|
|
define jh
|
2019-03-01 11:30:19 +00:00
|
|
|
call (void) _v8_internal_Print_Object(*((v8::internal::Object**)($arg0).location_))
|
2018-11-12 21:40:17 +00:00
|
|
|
end
|
|
|
|
document jh
|
|
|
|
Print content of a v8::internal::Handle
|
|
|
|
Usage: jh internal_handle
|
|
|
|
end
|
|
|
|
|
|
|
|
# Print content of v8::Local handle.
|
2017-01-13 12:59:30 +00:00
|
|
|
define jlh
|
2019-03-01 11:30:19 +00:00
|
|
|
call (void) _v8_internal_Print_Object(*((v8::internal::Object**)($arg0).val_))
|
2017-01-13 12:59:30 +00:00
|
|
|
end
|
|
|
|
document jlh
|
|
|
|
Print content of a v8::Local handle
|
|
|
|
Usage: jlh local_handle
|
|
|
|
end
|
|
|
|
|
2014-07-07 16:21:29 +00:00
|
|
|
# Print Code objects containing given PC.
|
|
|
|
define jco
|
2021-07-02 17:04:07 +00:00
|
|
|
if $argc == 0
|
|
|
|
call (void) _v8_internal_Print_Code((void*)($pc))
|
|
|
|
else
|
|
|
|
call (void) _v8_internal_Print_Code((void*)($arg0))
|
|
|
|
end
|
2014-07-07 16:21:29 +00:00
|
|
|
end
|
|
|
|
document jco
|
|
|
|
Print a v8 Code object from an internal code address
|
|
|
|
Usage: jco pc
|
|
|
|
end
|
|
|
|
|
2017-07-06 08:17:19 +00:00
|
|
|
# Print TransitionTree.
|
|
|
|
define jtt
|
2019-03-01 11:30:19 +00:00
|
|
|
call (void) _v8_internal_Print_TransitionTree((void*)($arg0))
|
2017-07-06 08:17:19 +00:00
|
|
|
end
|
|
|
|
document jtt
|
|
|
|
Print the complete transition tree of the given v8 Map.
|
|
|
|
Usage: jtt tagged_ptr
|
|
|
|
end
|
|
|
|
|
2014-07-07 16:21:29 +00:00
|
|
|
# Print JavaScript stack trace.
|
2014-07-09 10:55:55 +00:00
|
|
|
define jst
|
2019-03-01 11:30:19 +00:00
|
|
|
call (void) _v8_internal_Print_StackTrace()
|
2014-07-07 16:21:29 +00:00
|
|
|
end
|
|
|
|
document jst
|
|
|
|
Print the current JavaScript stack trace
|
|
|
|
Usage: jst
|
|
|
|
end
|
|
|
|
|
2019-06-24 10:46:59 +00:00
|
|
|
# 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
|
|
|
|
|
Add a jss gdb macro that skips over the jitted stack in gdb
Use it like this:
Program received signal SIGTRAP, Trace/breakpoint trap.
[Switching to Thread 0x7f50d02c4700 (LWP 8152)]
base::debug::(anonymous namespace)::DebugBreak ()
at ../../base/debug/debugger_posix.cc:230
230 }
(gdb) bt
at ../../base/debug/debugger_posix.cc:230
...
args_object=0x7f50d02bfe00, isolate=0x2f001c9e2020)
at ../../v8/src/builtins/builtins-api.cc:123
...
(gdb) jss
(gdb) bt
isolate=0x2f001c9e2020, is_construct=false, target=..., receiver=...,
argc=1, args=0x7f50d02c0518, new_target=...)
at ../../v8/src/execution.cc:111
isolate=0x2f001c9e2020, callable=..., receiver=..., argc=1,
argv=0x7f50d02c0518) at ../../v8/src/execution.cc:168
...
R=yangguo@chromium.org
BUG=
Review-Url: https://codereview.chromium.org/2186013002
Cr-Commit-Position: refs/heads/master@{#38121}
2016-07-28 09:04:39 +00:00
|
|
|
# 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
|
|
|
|
|
2021-02-02 11:01:05 +00:00
|
|
|
# Execute a simulator command.
|
|
|
|
python
|
|
|
|
import gdb
|
|
|
|
|
|
|
|
class SimCommand(gdb.Command):
|
|
|
|
"""Sim the current program."""
|
|
|
|
|
|
|
|
def __init__ (self):
|
|
|
|
super (SimCommand, self).__init__ ("sim", gdb.COMMAND_SUPPORT)
|
|
|
|
|
|
|
|
def invoke (self, arg, from_tty):
|
|
|
|
arg_c_string = gdb.Value(arg)
|
|
|
|
cmd_func = gdb.selected_frame().read_var("_v8_internal_Simulator_ExecDebugCommand")
|
|
|
|
cmd_func(arg_c_string)
|
|
|
|
|
|
|
|
SimCommand()
|
|
|
|
end
|
|
|
|
|
2016-11-04 07:27:39 +00:00
|
|
|
# Print stack trace with assertion scopes.
|
|
|
|
define bta
|
|
|
|
python
|
|
|
|
import re
|
2016-11-04 08:23:16 +00:00
|
|
|
frame_re = re.compile("^#(\d+)\s*(?:0x[a-f\d]+ in )?(.+) \(.+ at (.+)")
|
2019-02-18 16:45:02 +00:00
|
|
|
assert_re = re.compile("^\s*(\S+) = .+<v8::internal::Per\w+AssertScope<v8::internal::(\S*), (false|true)>")
|
2016-11-04 07:27:39 +00:00
|
|
|
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
|
2016-11-04 08:23:16 +00:00
|
|
|
end
|
2016-11-04 07:27:39 +00:00
|
|
|
document bta
|
|
|
|
Print stack trace with assertion scopes
|
|
|
|
Usage: bta
|
|
|
|
end
|
|
|
|
|
2017-05-09 12:11:27 +00:00
|
|
|
# Search for a pointer inside all valid pages.
|
|
|
|
define space_find
|
|
|
|
set $space = $arg0
|
2019-03-05 19:53:41 +00:00
|
|
|
set $current_page = $space->first_page()
|
|
|
|
while ($current_page != 0)
|
2017-05-09 12:11:27 +00:00
|
|
|
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
|
|
|
|
|
2019-10-09 10:55:16 +00:00
|
|
|
# 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
|
2022-04-22 17:51:33 +00:00
|
|
|
|
|
|
|
# Configuring ASLR may not be possible on some platforms, such running via the
|
|
|
|
# `rr` debuggger.
|
|
|
|
python
|
|
|
|
try:
|
|
|
|
gdb.execute("set disable-randomization off")
|
|
|
|
except gdb.error:
|
|
|
|
pass
|
|
|
|
end
|
2018-08-13 16:14:06 +00:00
|
|
|
|
|
|
|
# Install a handler whenever the debugger stops due to a signal. It walks up the
|
2022-01-04 13:39:33 +00:00
|
|
|
# stack looking for V8_Dcheck / V8_Fatal / OS::DebugBreak frame and moves the
|
|
|
|
# frame to the one above it so it's immediately at the line of code that
|
|
|
|
# triggered the stop condition.
|
2018-08-13 16:14:06 +00:00
|
|
|
python
|
2022-01-04 13:39:33 +00:00
|
|
|
def v8_stop_handler(event):
|
2018-12-14 10:16:01 +00:00
|
|
|
frame = gdb.selected_frame()
|
2018-08-21 11:45:52 +00:00
|
|
|
select_frame = None
|
2018-12-14 10:16:01 +00:00
|
|
|
message = None
|
2019-01-08 10:07:20 +00:00
|
|
|
count = 0
|
2022-01-04 13:39:33 +00:00
|
|
|
# Limit stack scanning since the frames we look for are near the top anyway,
|
|
|
|
# and otherwise stack overflows can be very slow.
|
2020-01-23 11:13:53 +00:00
|
|
|
while frame is not None and count < 7:
|
2019-01-08 10:07:20 +00:00
|
|
|
count += 1
|
2021-10-18 14:07:01 +00:00
|
|
|
# If we are in a frame created by gdb (e.g. for `(gdb) call foo()`), gdb
|
|
|
|
# emits a dummy frame between its stack and the program's stack. Abort the
|
|
|
|
# walk if we see this frame.
|
|
|
|
if frame.type() == gdb.DUMMY_FRAME: break
|
|
|
|
|
2018-12-14 10:16:01 +00:00
|
|
|
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
|
2018-12-18 09:53:49 +00:00
|
|
|
if frame.name() is not None and frame.name().startswith('V8_Fatal'):
|
2018-08-13 16:14:06 +00:00
|
|
|
select_frame = frame.older()
|
2022-01-04 13:39:33 +00:00
|
|
|
if frame.name() == 'v8::base::OS::DebugBreak':
|
|
|
|
select_frame = frame.older()
|
2018-08-13 16:14:06 +00:00
|
|
|
frame = frame.older()
|
|
|
|
|
2018-08-21 11:45:52 +00:00
|
|
|
if select_frame is not None:
|
|
|
|
select_frame.select()
|
|
|
|
gdb.execute('frame')
|
2018-12-14 10:16:01 +00:00
|
|
|
if message:
|
|
|
|
print('DCHECK error: {}'.format(message))
|
2018-08-21 11:45:52 +00:00
|
|
|
|
2022-01-04 13:39:33 +00:00
|
|
|
gdb.events.stop.connect(v8_stop_handler)
|
2018-08-13 16:14:06 +00:00
|
|
|
end
|
2019-05-14 11:30:42 +00:00
|
|
|
|
|
|
|
# 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)
|
|
|
|
|
2019-06-17 10:35:18 +00:00
|
|
|
gdb.execute("set environment V8_GDBINIT_SOURCED=1")
|
2019-05-14 11:30:42 +00:00
|
|
|
|
|
|
|
end
|
2022-04-26 15:53:36 +00:00
|
|
|
|
|
|
|
# Add a simple unwinder which, on x64, walks frame pointers when there
|
|
|
|
# is no source information available.
|
|
|
|
python
|
|
|
|
|
|
|
|
from gdb.unwinder import Unwinder
|
|
|
|
|
|
|
|
class V8UnwinderFrameId(object):
|
|
|
|
def __init__(self, sp, pc):
|
|
|
|
self.sp = sp
|
|
|
|
self.pc = pc
|
|
|
|
|
|
|
|
class V8Unwinder(Unwinder):
|
|
|
|
def __init__(self):
|
|
|
|
super(V8Unwinder, self).__init__("V8Unwinder")
|
|
|
|
self.enabled = True
|
|
|
|
|
|
|
|
def __call__(self, pending_frame):
|
|
|
|
try:
|
|
|
|
# Only supported on x64.
|
|
|
|
if gdb.selected_inferior().architecture().name() != "i386:x86-64":
|
|
|
|
return None
|
|
|
|
|
|
|
|
pc = pending_frame.read_register("rip")
|
|
|
|
sym_and_line = gdb.current_progspace().find_pc_line(int(pc))
|
|
|
|
|
|
|
|
if sym_and_line.symtab is not None:
|
|
|
|
return None
|
|
|
|
fp = pending_frame.read_register("rbp").reinterpret_cast(
|
|
|
|
gdb.lookup_type("void").pointer().pointer())
|
|
|
|
|
|
|
|
next_sp = fp
|
|
|
|
next_fp = fp.dereference()
|
|
|
|
next_pc = (fp+1).dereference()
|
|
|
|
|
|
|
|
frame_info = V8UnwinderFrameId(next_sp, next_pc)
|
|
|
|
|
|
|
|
# create_unwind_info seems to sometimes have issues accessing
|
|
|
|
# the frame_info if it's not first accessed in Python.
|
|
|
|
_lol_gdb_workaround = frame_info.pc + 1
|
|
|
|
|
|
|
|
unwind_info = pending_frame.create_unwind_info(frame_info)
|
|
|
|
unwind_info.add_saved_register("rsp", next_sp)
|
|
|
|
unwind_info.add_saved_register("rip", next_pc)
|
|
|
|
unwind_info.add_saved_register("rbp", next_fp)
|
|
|
|
return unwind_info
|
|
|
|
except Exception as e:
|
|
|
|
return None
|
|
|
|
|
|
|
|
gdb.unwinder.register_unwinder(None, V8Unwinder(), replace=True)
|
|
|
|
|
|
|
|
end
|