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

в 9:14, , рубрики: debug, 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 — попробуйте сами.

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

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

Установка

Установка дебаггера не отличается от установки любой другой 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

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

Ссылки:

Автор: divan0

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js