{"title":"Crystal Debug","description":"","section":"examples","version":"v1.5","path":"examples/crystal-debug","canonical_url":"https://amberframework.org/docs/v1.5/examples/crystal-debug","markdown_url":"https://amberframework.org/docs/v1.5/examples/crystal-debug.md","inherited":true,"content_markdown":"# Crystal Debug\n\nThis tutorial has tips and tricks on how to debug Crystal projects. It shows how to leverage tools like GDB or LLDB using debugger clients like [Native Debug](https://marketplace.visualstudio.com/items?itemName=webfreak.debug) for VSCode.\n\n## Prerequisites\n\n* Crystal - See installation guide [here](https://crystal-lang.org/docs/installation/)\n* Crystal project - See installation guide [here](https://crystal-lang.org/docs/using_the_compiler/#creating-a-project-or-library) or [here \\(Amber\\)](../guides/installation.md)\n* VSCode with [Crystal Lang](https://marketplace.visualstudio.com/items?itemName=faustinoaq.crystal-lang) and [Native Debug](https://marketplace.visualstudio.com/items?itemName=webfreak.debug) or [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb) extensions\n* GNU debugger \\(GDB\\) or LLVM debugger \\(LLDB\\) - See installation guide below\n\nInstall `gdb` or `lldb` accordingly to your OS.\n\n{% hint style=\"info\" %}\nConfirm that the above prerequisites are installed before setting up the debugger. These settings have been verified for mac OS and Linux environments.\n{% endhint %}\n\n## Debug on VSCode\n\n{% hint style=\"warning\" %}\nBy convention the project directory name is the same as your application name, if you have changed it, please update `${workspaceFolderBasename}` with the name configured inside `shards.yml`\n{% endhint %}\n\n### 1. `tasks.json` configuration to compile a crystal project\n\n```javascript\n{\n  \"version\": \"2.0.0\",\n  \"tasks\": [\n    {\n      \"label\": \"Compile\",\n      \"command\": \"shards build --debug ${workspaceFolderBasename}\",\n      \"type\": \"shell\"\n    }\n  ]\n}\n```\n\n### 2. `launch.json` configuration to debug a binary\n\n#### Using GDB\n\n```javascript\n{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"name\": \"Debug\",\n      \"type\": \"gdb\",\n      \"request\": \"launch\",\n      \"target\": \"./bin/${workspaceFolderBasename}\",\n      \"cwd\": \"${workspaceRoot}\",\n      \"preLaunchTask\": \"Compile\"\n    }\n  ]\n}\n```\n\n#### Using LLDB\n\n```javascript\n{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"name\": \"Debug\",\n      \"type\": \"lldb-mi\",\n      \"request\": \"launch\",\n      \"target\": \"./bin/${workspaceFolderBasename}\",\n      \"cwd\": \"${workspaceRoot}\",\n      \"preLaunchTask\": \"Compile\"\n    }\n  ]\n}\n```\n\n### 3. Then hit the DEBUG green play button\n\n![debugging](https://i.imgur.com/GsGT1h0.png)\n\n## Tips and Tricks for debugging Crystal applications\n\n{% hint style=\"danger\" %}\n`lldb` does not show data for variables in crystal yet, see issue [\\#4457](https://github.com/crystal-lang/crystal/issues/4457)\n{% endhint %}\n\nFully debugging Crystal applications is not supported yet. You can use some of the techniques below to improve the debugging experience.\n\n### 1. Use debugger keyword\n\nInstead of putting breakpoints using commands inside GDB or LLDB you can try to set a breakpoint using `debugger` keyword.\n\n```crystal\ni = 0\nwhile i < 3\n  i += 1\n  debugger # => breakpoint\nend\n```\n\n### 2. Avoid breakpoints inside blocks\n\nCurrently, Crystal lacks support for debugging inside of blocks. If you put a breakpoint inside a block, it will be ignored.\n\nAs a workaround, use `pp` to pretty print objects inside of blocks.\n\n```crystal\n3.times do |i|\n  pp i\nend\n# i => 1\n# i => 2\n# i => 3\n```\n\n### 3. Try `@[NoInline]` to debug arguments data\n\nSometimes crystal will optimize argument data, so the debugger will show `<optimized output>` instead of the arguments. To avoid this behavior use the `@[NoInline]` attribute before your function implementation.\n\n```crystal\n@[NoInline]\ndef foo(bar)\n  debugger\nend\n```\n\n### 4. Printing strings objects \\(GDB\\)\n\nTo print string objects in the debugger:\n\nFirst, setup the debugger with the `debugger` statement:\n\n```crystal\nfoo = \"Hello World!\"\ndebugger\n```\n\nThen use `print` in the debugging console.\n\n```bash\n(gdb) print &foo.c\n$1 = (UInt8 *) 0x10008e6c4 \"Hello World!\"\n```\n\nOr add `&foo.c` using a new variable entry on watch section in VSCode debugger\n\n![Using VSCode GUI](https://i.imgur.com/EpQinL7.png)\n\n### 5. Printing array variables\n\nTo print array items in the debugger:\n\nFirst, setup the debugger with the `debugger` statement:\n\n```crystal\nfoo = [\"item 0\", \"item 1\", \"item 2\"]\ndebugger\n```\n\nThen use `print` in the debugging console:\n\n```bash\n(gdb) print &foo.buffer[0].c\n$19 = (UInt8 *) 0x10008e7f4 \"item 0\"\n```\n\nChange the buffer index for each item you want to print.\n\n### 6. Printing instance variables\n\nFor printing `@foo` var in this code:\n\n```crystal\nclass Bar\n  @foo = 0\n  def baz\n    debugger\n  end\nend\n\nBar.new\n```\n\nYou can use `self.foo` in the debugger terminal or VSCode GUI.\n\n### 7. Print hidden objects\n\nSome objects do not show at all. You can unhide them using the `.to_s` method and a temporary debugging variable, like this:\n\n```crystal\ndef bar(hello)\n  \"#{hello} World!\"\nend\n\ndef foo(hello)\n  bar_hello_to_s = bar(hello).to_s\n  debugger\nend\n\nfoo(\"Hello\")\n```\n\nThis trick allows showing the `bar_hello_to_s` variable inside the debugger tool."}