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