# Copyright 2017 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.
# Load this file by adding this to your ~/.lldbinit:
# command script import <this_dir>/lldb_commands.py
import lldb
import re
#####################
# Helper functions. #
#####################
def current_thread(debugger):
return debugger.GetSelectedTarget().GetProcess().GetSelectedThread()
def current_frame(debugger):
return current_thread(debugger).GetSelectedFrame()
def no_arg_cmd(debugger, cmd):
current_frame(debugger).EvaluateExpression(cmd)
print("")
def ptr_arg_cmd(debugger, name, param, cmd):
if not param:
print("'{}' requires an argument".format(name))
return
param = '(void*)({})'.format(param)
no_arg_cmd(debugger, cmd.format(param))
#####################
# lldb commands. #
#####################
def job(debugger, param, *args):
"""Print a v8 heap object"""
ptr_arg_cmd(debugger, 'job', param, "_v8_internal_Print_Object({})")
def jlh(debugger, param, *args):
"""Print v8::Local handle value"""
ptr_arg_cmd(debugger, 'jlh', param,
"_v8_internal_Print_Object(*(v8::internal::Object**)(*{}))")
def jco(debugger, param, *args):
"""Print the code object at the given pc (default: current pc)"""
if not param:
param = str(current_frame(debugger).FindRegister("pc").value)
ptr_arg_cmd(debugger, 'jco', param, "_v8_internal_Print_Code({})")
def jld(debugger, param, *args):
"""Print a v8 LayoutDescriptor object"""
ptr_arg_cmd(debugger, 'jld', param,
"_v8_internal_Print_LayoutDescriptor({})")
def jtt(debugger, param, *args):
"""Print the transition tree of a v8 Map"""
ptr_arg_cmd(debugger, 'jtt', param, "_v8_internal_Print_TransitionTree({})")
def jst(debugger, *args):
"""Print the current JavaScript stack trace"""
no_arg_cmd(debugger, "_v8_internal_Print_StackTrace()")
def jss(debugger, *args):
"""Skip the jitted stack on x64 to where we entered JS last"""
frame = current_frame(debugger)
js_entry_sp = frame.EvaluateExpression(
"v8::internal::Isolate::Current()->thread_local_top()->js_entry_sp_;") \
.GetValue()
sizeof_void = frame.EvaluateExpression("sizeof(void*)").GetValue()
rbp = frame.FindRegister("rbp")
rsp = frame.FindRegister("rsp")
pc = frame.FindRegister("pc")
rbp = js_entry_sp
rsp = js_entry_sp + 2 *sizeof_void
pc.value = js_entry_sp + sizeof_void
def bta(debugger, *args):
"""Print stack trace with assertion scopes"""
func_name_re = re.compile("([^(<]+)(?:\(.+\))?")
assert_re = re.compile(
"^v8::internal::Per\w+AssertType::(\w+)_ASSERT, (false|true)>")
thread = current_thread(debugger)
for frame in thread:
functionSignature = frame.GetDisplayFunctionName()
if functionSignature is None:
continue
functionName = func_name_re.match(functionSignature)
line = frame.GetLineEntry().GetLine()
sourceFile = frame.GetLineEntry().GetFileSpec().GetFilename()
if line:
sourceFile = sourceFile + ":" + str(line)
if sourceFile is None:
sourceFile = ""
print("[%-2s] %-60s %-40s" % (frame.GetFrameID(),
functionName.group(1),
sourceFile))
match = assert_re.match(str(functionSignature))
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)))
def __lldb_init_module(debugger, dict):
debugger.HandleCommand('settings set target.x86-disassembly-flavor intel')
for cmd in ('job', 'jlh', 'jco', 'jld', 'jtt', 'jst', 'jss', 'bta'):
debugger.HandleCommand(
'command script add -f lldb_commands.{} {}'.format(cmd, cmd))