godebug — кроссплатформенный дебаггер для Go

Ребята из компании Mailgun презентовали новый кроссплатформенный дебаггер для Go, который использует оригинальную технологию, в корне отличающуюся от стандартных подходов. Забегая наперед — с помощью Gopherjs этот дебаггер работает даже в браузере.

image

Интро

All that stands in the way [of a good Go debugger] is the writing of a lot of non-portable low-level code talking to buggy undocumented interfaces.

— Rob Pike

Поддержка Go была в дебаггере gdb давно, но оставалось много мелких, но неприятных проблем, которые не давали возможности полноценно им пользоваться. Также есть проект delve, но о его популярности пока сложно сделать выводы. В любом случае, «классические» подходы к написанию дебаггера для Go неизменно сталкивались со сложностью реализации, особенно когда речь заходит о спецефичных для Go вещах вроде дебага горутин.

Абсолютно другой подход выбрал Jeremy Schlatter из компании Mailgun, которая использует Go достаточно активно — он взял за основу ту же идею, которая лежит в основе go coverage tool, встроенной системы тестов и некоторых других: изменение кода на лету перед компиляцией. Такой подход вообще стал возможен благодаря двум основным моментам — огромной скорости компиляции и простой грамматике языка. Встроенные в stdlib инструменты для разбора Go-грамматики (go/ast и go/parser) позволяют достаточно просто на лету модифицировать код компилируемой программы, а это открывает целый новый пласт возможностей.

Пример

К примеру, возьмем простую программу на Go:

package main  import "fmt"  func hello(text string) { 	fmt.Println(text) }  func main() { 	fmt.Printf("Hello,") 	hello("world") }  

И запустим godebug с командой output, чтобы посмотреть сгенерированный код:

package main  import ( 	"fmt" 	"github.com/mailgun/godebug/lib" )  var main_go_scope = godebug.EnteringNewScope(main_go_contents)  func hello(text string) { 	ctx, ok := godebug.EnterFunc(func() { 		hello(text) 	}) 	if !ok { 		return 	} 	defer godebug.ExitFunc(ctx) 	scope := main_go_scope.EnteringNewChildScope() 	scope.Declare("text", &text) 	godebug.Line(ctx, scope, 6) 	fmt.Println(text) }  func main() { 	ctx, ok := godebug.EnterFunc(main) 	if !ok { 		return 	} 	godebug.Line(ctx, main_go_scope, 10) 	fmt.Printf("Hello,") 	godebug.Line(ctx, main_go_scope, 11) 	hello("world") }  var main_go_contents = `package main  import "fmt"  func hello(text string) { 	fmt.Println(text) }  func main() { 	fmt.Printf("Hello,") 	hello("world") } ` 

Не вдаваясь пока в подробности каждой функции, видно, что на каждой строчке расставлены функции-идентификаторы строк, входа и выхода из функций, декларации переменных и так далее. Рантайм дебаггера уже работает с этими, добавленными функциями, имея полную информацию о программе.

Демо

Этот подход, конечно же, не универсален и имеет свои ограничения и недостатки, но его относительная простота и преимущества очевидны. Одной из демонстраций сильных сторон такого подхода является возможность делать отладку прямо в браузере, с помощью gopherjs: focused-sprite-91100.appspot.com/playground/blog-post — попробуйте сами.

Есть некоторые нюансы с тем, что нарушается нативная нумерация строк — это может повлиять на, скажем, логгеры/трейсеры, которые репортят номер строки. Но в целом для большинства случаев этот подход работает на ура.

Установка

Установка дебаггера не отличается от установки любой другой Go-программы:

go install github.com/mailgun/godebug

Использование

Пользоваться дебаггером очень просто:

$ godebug godebug is a tool for debugging Go programs. Usage:          godebug command [arguments]  The commands are:      run       compile, run, and debug a Go program     test      compile, run, and debug Go package tests     output    generate debug source code, but do not build or run it  Use "godebug help [command]" for more information about a command.

godebug run *.go — компилирует с поддержкой дебага все исходники в текущей директории. Все дополнительные пакеты, используемые в коде, как свои, так и внешние — компилируются нативно, без дебага — это сделано специально, чтобы не замедлять код там, где это не нужно. Если нужно отлаживать и код в каких-то включенных пакетах, то они должны быть указаны с помощью флага -instrument:

godebug run -instrument github.com/go-sql-driver/mysql main.go

Брекпоинты в программе ставятся следующей конструкцией:

_ = "breakpoint"

При обычной сборке этот код ничего не делает, а godebug его распознает перед компиляцией и вставит инструкцию для остановки.

Статус

Проект уже готов для практического использования, хотя поддерживаются пока только самые базовые функции:

  • list
  • step
  • next
  • continue
  • print

В планах поддержка остановки/инспекции горутин и другие вещи, которые коммьюнити сочтет нужными/полезными.

Ссылки:

Подробная статья — анонс дебаггера: blog.mailgun.com/introducing-a-new-cross-platform-debugger-for-go
Слайды с доклада Jeremy на GoSF’15: go-talks.appspot.com/github.com/jeremyschlatter/godebug-talk/godebug.slide
Github: github.com/mailgun/godebug

ссылка на оригинал статьи http://habrahabr.ru/post/256355/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *