-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Rudimentary debug support is here. It allows LLDB to show Crystal variables properly in C lang style. To work properly some LLDB patching is needed due to the dereferencing bug in DWARF Expression Evaluator. * Rolled back enforced NoInline in debug mode. * Added comment to explain why we have to use C++ Language ID * Added debug information for union and nullable types. * Fixed some of the debug type glitches (MixedUnionType notoriously) as well as fixed line linfo for blocks. * Refactored debug information logging as per bcardiff comments * Added array types debugging with DWARF support. * Some extra features was added to llvm module so now it is possible to access to any block or any instruction in the LLVM function. Also some refactoring as done to show properly locations of block variables. * Added debug support for Tuples, NamedTuples, TypeDefs and partial support for Symbols. * Removed unnecessary debug logs and did some format tidy up. * More of code tidy up. * Moving LLVM functions to the class they belong to. * Neating up the code as per comments of reviewers. * Code cleanup as per Sija's comments. * Moved lldb crystal formatter into etc/lldb Added get_or_create_array_subrange_variable method to LLVM module. * Rolled back previously set_current_debug_location as they are breaking unit tests and not really beneficial. * Fixes as per Sija's suggestions as well as fix for failed unit test due to the missed filename of the unit test * autoformated source code * Removed location code that is not needed and was redundant and also was breaking the unit tests. * Code clean up as per Sija's comments. * Typo fix! Thanks a lot, Sija! * Some clean up to trigger the build to re-test Alpine Linux. * Re-trigger build wth comment commit. * Fixed formatting issues (who knew that ### is not allowed! ) * initial refactor to use proper debug alloca * fixing of alloca to be on alloca block * formatted code * Bug fixing for proper debug implementation. It should work properly now for most of variables and "self" variable. * Fix of code formatting. * Code fixes and avoid generating debug info for naked functions * Fixed function name typo that @Sija found * Addressed PR comments and refactored back to Type for debug_type_hash. * changed the order of 'if' branch execution for setup_fun when --debug flag is used as per PR comments from @RX14 * Removed method attributes irrelevant for debugging as per @RX14 * Refactored code as per suggestions from @Asterlite and @Sija * Code refactoring as per @Sija suggestions * Removed all unused methods that were implemented during debug support implementeation. * Initial debug specs Generate a lldb script from magic comments and check the output using FileCheck * Cherrypicked debug driver from @bcardiff Co-authored-by: Sergey Kuznetsov <[email protected]> Co-authored-by: Brian J. Cardiff <[email protected]>
- Loading branch information
1 parent
965afe2
commit 642ae96
Showing
19 changed files
with
649 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import lldb | ||
|
||
class CrystalArraySyntheticProvider: | ||
def __init__(self, valobj, internal_dict): | ||
self.valobj = valobj | ||
self.buffer = None | ||
self.size = 0 | ||
|
||
def update(self): | ||
if self.valobj.type.is_pointer: | ||
self.valobj = self.valobj.Dereference() | ||
self.size = int(self.valobj.child[0].value) | ||
self.type = self.valobj.type | ||
self.buffer = self.valobj.child[2] | ||
|
||
def num_children(self): | ||
size = 0 if self.size is None else self.size | ||
return size | ||
|
||
def get_child_index(self, name): | ||
try: | ||
return int(name.lstrip('[').rstrip(']')) | ||
except: | ||
return -1 | ||
|
||
def get_child_at_index(self,index): | ||
if index >= self.size: | ||
return None | ||
try: | ||
elementType = self.buffer.type.GetPointeeType() | ||
offset = elementType.size * index | ||
return self.buffer.CreateChildAtOffset('[' + str(index) + ']', offset, elementType) | ||
except Exception as e: | ||
print('Got exception %s' % (str(e))) | ||
return None | ||
|
||
def findType(name, module): | ||
cachedTypes = module.GetTypes() | ||
for idx in range(cachedTypes.GetSize()): | ||
type = cachedTypes.GetTypeAtIndex(idx) | ||
if type.name == name: | ||
return type | ||
return None | ||
|
||
|
||
def CrystalString_SummaryProvider(value, dict): | ||
error = lldb.SBError() | ||
if value.TypeIsPointerType(): | ||
value = value.Dereference() | ||
process = value.GetTarget().GetProcess() | ||
byteSize = int(value.child[0].value) | ||
len = int(value.child[1].value) | ||
len = byteSize or len | ||
strAddr = value.child[2].load_addr | ||
val = process.ReadCStringFromMemory(strAddr, len + 1, error) | ||
return '"%s"' % val | ||
|
||
|
||
def __lldb_init_module(debugger, dict): | ||
debugger.HandleCommand('type synthetic add -l crystal_formatters.CrystalArraySyntheticProvider -x "^Array\(.+\)(\s*\**)?" -w Crystal') | ||
debugger.HandleCommand('type summary add -F crystal_formatters.CrystalString_SummaryProvider -x "^(String|\(String \| Nil\))(\s*\**)?$" -w Crystal') | ||
debugger.HandleCommand('type category enable Crystal') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
def each_line1(file) | ||
File.read_lines(file).each_with_index do |line, index0| | ||
yield line, index0 + 1 | ||
end | ||
end | ||
|
||
repo_base_dir = "#{__DIR__}/../../" | ||
tmp_build_dir = File.join(repo_base_dir, ".build") | ||
Dir.mkdir_p(tmp_build_dir) | ||
|
||
input = ARGV[0] | ||
|
||
bin = File.join(tmp_build_dir, "debug_test_case") | ||
debugger_script = File.join(tmp_build_dir, "./debugger.script") | ||
|
||
`#{repo_base_dir}/bin/crystal build --debug #{input} -o #{bin}` | ||
|
||
File.open(debugger_script, "w") do |script| | ||
lldb_crystal_formatters = File.expand_path(File.join(repo_base_dir, "etc", "lldb", "crystal_formatters.py")) | ||
script.puts "version" | ||
script.puts "command script import #{lldb_crystal_formatters}" | ||
|
||
each_line1(input) do |line, line_number| | ||
if line.match(/# break\b/) | ||
script.puts "breakpoint set --file #{input} --line #{line_number}" | ||
end | ||
end | ||
|
||
script.puts "run" | ||
|
||
each_line1(input) do |line| | ||
if md = line.match(/# lldb-command: (.*)/) | ||
script.puts md[1] | ||
end | ||
end | ||
end | ||
|
||
session_output_dir = File.join(repo_base_dir, "tmp", "debug") | ||
Dir.mkdir_p(session_output_dir) | ||
|
||
session_log = File.join(session_output_dir, File.basename(input, File.extname(input)) + ".lldb-session") | ||
session_assert = File.join(session_output_dir, File.basename(input, File.extname(input)) + ".lldb-assert") | ||
|
||
File.open(session_assert, "w") do |assert| | ||
each_line1(input) do |line| | ||
if md = line.match(/# lldb-command: (.*)/) | ||
assert.puts "CHECK: (lldb) #{md[1]}" | ||
elsif md = line.match(/# lldb-check: (.*)/) | ||
assert.puts "CHECK-NEXT: #{md[1]}" | ||
end | ||
end | ||
end | ||
|
||
`/usr/bin/lldb -b --source #{debugger_script} #{bin} > #{session_log}` | ||
|
||
`FileCheck #{session_assert} < #{session_log}` | ||
|
||
exit $?.exit_code |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# NOTE: breakpoint on line 1 + next does not work | ||
a = "hello world" # break | ||
# lldb-command: n | ||
# lldb-command: print a | ||
# lldb-check: (String *) $0 = {{0x[0-9a-f]+}} "hello world" | ||
b = 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#!/bin/sh | ||
|
||
# This file can be executed from the root of the working copy | ||
# | ||
# $ ./spec/debug/test.sh | ||
# | ||
# It will use the ./spec/debug/driver.cr program to execute | ||
# the files explicitly listed at the end of this file. | ||
# | ||
# Those files have magic comments to build a script for an lldb session | ||
# and a FileCheck file with assertions over that session. | ||
# | ||
# In ./tmp/debug you can find a dump of the session and the assertion file. | ||
# | ||
# The magic comments interpreted by the driver are: | ||
# | ||
# * # break | ||
# * # lldb-command: | ||
# * # lldb-check: | ||
# | ||
|
||
set -euo pipefail | ||
|
||
SCRIPT_PATH="$(realpath "$0")" | ||
SCRIPT_ROOT="$(dirname "$SCRIPT_PATH")" | ||
|
||
BUILD_DIR=$SCRIPT_ROOT/../../.build | ||
crystal=$SCRIPT_ROOT/../../bin/crystal | ||
driver=$BUILD_DIR/debug_driver | ||
mkdir -p $BUILD_DIR | ||
$crystal build $SCRIPT_ROOT/driver.cr -o $driver | ||
|
||
$driver $SCRIPT_ROOT/top_level.cr | ||
$driver $SCRIPT_ROOT/strings.cr |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# NOTE: breakpoint on line 1 + next does not work | ||
a = 42 # break | ||
# lldb-command: print a | ||
# lldb-check: (int) $0 = 0 | ||
# lldb-command: n | ||
# lldb-command: print a | ||
# lldb-check: (int) $1 = 42 | ||
b = 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.