gdbinit 8.58 KB
Newer Older
1 2 3 4
# 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.

5
# Print tagged object.
6
define job
7
call (void) _v8_internal_Print_Object((void*)($arg0))
8 9 10 11 12 13
end
document job
Print a v8 JavaScript object
Usage: job tagged_ptr
end

14 15
# Print content of v8::internal::Handle.
define jh
16
call (void) _v8_internal_Print_Object(*((v8::internal::Object**)($arg0).location_))
17 18 19 20 21 22 23
end
document jh
Print content of a v8::internal::Handle
Usage: jh internal_handle
end

# Print content of v8::Local handle.
24
define jlh
25
call (void) _v8_internal_Print_Object(*((v8::internal::Object**)($arg0).val_))
26 27 28 29 30 31
end
document jlh
Print content of a v8::Local handle
Usage: jlh local_handle
end

32 33
# Print Code objects containing given PC.
define jco
34 35 36 37 38
  if $argc == 0
    call (void) _v8_internal_Print_Code((void*)($pc))
  else
    call (void) _v8_internal_Print_Code((void*)($arg0))
  end
39 40 41 42 43 44
end
document jco
Print a v8 Code object from an internal code address
Usage: jco pc
end

45 46
# Print TransitionTree.
define jtt
47
call (void) _v8_internal_Print_TransitionTree((void*)($arg0))
48 49 50 51 52 53
end
document jtt
Print the complete transition tree of the given v8 Map.
Usage: jtt tagged_ptr
end

54
# Print JavaScript stack trace.
55
define jst
56
call (void) _v8_internal_Print_StackTrace()
57 58 59 60 61 62
end
document jst
Print the current JavaScript stack trace
Usage: jst
end

63 64 65 66 67 68 69 70 71
# 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

72 73 74 75 76 77 78 79 80 81 82 83
# 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

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
# 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

102 103 104 105
# Print stack trace with assertion scopes.
define bta
python
import re
106
frame_re = re.compile("^#(\d+)\s*(?:0x[a-f\d]+ in )?(.+) \(.+ at (.+)")
107
assert_re = re.compile("^\s*(\S+) = .+<v8::internal::Per\w+AssertScope<v8::internal::(\S*), (false|true)>")
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
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
123
end
124 125 126 127 128
document bta
Print stack trace with assertion scopes
Usage: bta
end

129 130 131
# Search for a pointer inside all valid pages.
define space_find
  set $space = $arg0
Yang Guo's avatar
Yang Guo committed
132 133
  set $current_page = $space->first_page()
  while ($current_page != 0)
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
    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

154 155 156 157 158 159 160
# 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
161 162 163 164 165 166 167 168 169

# 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
170 171

# Install a handler whenever the debugger stops due to a signal. It walks up the
172 173 174
# 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.
175
python
176
def v8_stop_handler(event):
177
  frame = gdb.selected_frame()
178
  select_frame = None
179
  message = None
180
  count = 0
181 182
  # Limit stack scanning since the frames we look for are near the top anyway,
  # and otherwise stack overflows can be very slow.
183
  while frame is not None and count < 7:
184
    count += 1
185 186 187 188 189
    # 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

190 191 192 193 194 195
    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
196
    if frame.name() is not None and frame.name().startswith('V8_Fatal'):
197
      select_frame = frame.older()
198 199
    if frame.name() == 'v8::base::OS::DebugBreak':
      select_frame = frame.older()
200 201
    frame = frame.older()

202 203 204
  if select_frame is not None:
    select_frame.select()
    gdb.execute('frame')
205 206
    if message:
      print('DCHECK error: {}'.format(message))
207

208
gdb.events.stop.connect(v8_stop_handler)
209
end
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258

# 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)

259
gdb.execute("set environment V8_GDBINIT_SOURCED=1")
260 261

end
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313

# 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