1# Copyright 2017 the V8 project authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5# Load this file by adding this to your ~/.lldbinit: 6# command script import <this_dir>/lldb_commands.py 7 8# for py2/py3 compatibility 9from __future__ import print_function 10 11import os 12import re 13 14import lldb 15 16##################### 17# Helper functions. # 18##################### 19def current_thread(debugger): 20 return debugger.GetSelectedTarget().GetProcess().GetSelectedThread() 21 22def current_frame(debugger): 23 return current_thread(debugger).GetSelectedFrame() 24 25def no_arg_cmd(debugger, cmd): 26 cast_to_void_expr = '(void) {}'.format(cmd) 27 evaluate_result = current_frame(debugger).EvaluateExpression(cast_to_void_expr) 28 # When a void function is called the return value type is 0x1001 which 29 # is specified in http://tiny.cc/bigskz. This does not indicate 30 # an error so we check for that value below. 31 kNoResult = 0x1001 32 error = evaluate_result.GetError() 33 if error.fail and error.value != kNoResult: 34 print("Failed to evaluate command {} :".format(cmd)) 35 print(error.description) 36 else: 37 print("") 38 39def ptr_arg_cmd(debugger, name, param, cmd): 40 if not param: 41 print("'{}' requires an argument".format(name)) 42 return 43 param = '(void*)({})'.format(param) 44 no_arg_cmd(debugger, cmd.format(param)) 45 46##################### 47# lldb commands. # 48##################### 49def job(debugger, param, *args): 50 """Print a v8 heap object""" 51 ptr_arg_cmd(debugger, 'job', param, "_v8_internal_Print_Object({})") 52 53def jlh(debugger, param, *args): 54 """Print v8::Local handle value""" 55 ptr_arg_cmd(debugger, 'jlh', param, 56 "_v8_internal_Print_Object(*(v8::internal::Object**)({}.val_))") 57 58def jco(debugger, param, *args): 59 """Print the code object at the given pc (default: current pc)""" 60 if not param: 61 param = str(current_frame(debugger).FindRegister("pc").value) 62 ptr_arg_cmd(debugger, 'jco', param, "_v8_internal_Print_Code({})") 63 64def jtt(debugger, param, *args): 65 """Print the transition tree of a v8 Map""" 66 ptr_arg_cmd(debugger, 'jtt', param, "_v8_internal_Print_TransitionTree({})") 67 68def jst(debugger, *args): 69 """Print the current JavaScript stack trace""" 70 no_arg_cmd(debugger, "_v8_internal_Print_StackTrace()") 71 72def jss(debugger, *args): 73 """Skip the jitted stack on x64 to where we entered JS last""" 74 frame = current_frame(debugger) 75 js_entry_sp = frame.EvaluateExpression( 76 "v8::internal::Isolate::Current()->thread_local_top()->js_entry_sp_;") \ 77 .GetValue() 78 sizeof_void = frame.EvaluateExpression("sizeof(void*)").GetValue() 79 rbp = frame.FindRegister("rbp") 80 rsp = frame.FindRegister("rsp") 81 pc = frame.FindRegister("pc") 82 rbp = js_entry_sp 83 rsp = js_entry_sp + 2 *sizeof_void 84 pc.value = js_entry_sp + sizeof_void 85 86def bta(debugger, *args): 87 """Print stack trace with assertion scopes""" 88 func_name_re = re.compile("([^(<]+)(?:\(.+\))?") 89 assert_re = re.compile( 90 "^v8::internal::Per\w+AssertType::(\w+)_ASSERT, (false|true)>") 91 thread = current_thread(debugger) 92 for frame in thread: 93 functionSignature = frame.GetDisplayFunctionName() 94 if functionSignature is None: 95 continue 96 functionName = func_name_re.match(functionSignature) 97 line = frame.GetLineEntry().GetLine() 98 sourceFile = frame.GetLineEntry().GetFileSpec().GetFilename() 99 if line: 100 sourceFile = sourceFile + ":" + str(line) 101 102 if sourceFile is None: 103 sourceFile = "" 104 print("[%-2s] %-60s %-40s" % (frame.GetFrameID(), 105 functionName.group(1), 106 sourceFile)) 107 match = assert_re.match(str(functionSignature)) 108 if match: 109 if match.group(3) == "false": 110 prefix = "Disallow" 111 color = "\033[91m" 112 else: 113 prefix = "Allow" 114 color = "\033[92m" 115 print("%s -> %s %s (%s)\033[0m" % ( 116 color, prefix, match.group(2), match.group(1))) 117 118def setup_source_map_for_relative_paths(debugger): 119 # Copied from Chromium's tools/lldb/lldbinit.py. 120 # When relative paths are used for debug symbols, lldb cannot find source 121 # files. Set up a source map to point to V8's root. 122 this_dir = os.path.dirname(os.path.abspath(__file__)) 123 source_dir = os.path.join(this_dir, os.pardir) 124 125 debugger.HandleCommand( 126 'settings set target.source-map ../.. ' + source_dir) 127 128 129def __lldb_init_module(debugger, dict): 130 setup_source_map_for_relative_paths(debugger) 131 debugger.HandleCommand('settings set target.x86-disassembly-flavor intel') 132 for cmd in ('job', 'jlh', 'jco', 'jld', 'jtt', 'jst', 'jss', 'bta'): 133 debugger.HandleCommand( 134 'command script add -f lldb_commands.{} {}'.format(cmd, cmd)) 135