lldb_commands.py 4.6 KB

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