Отладка не должна быть частью разработки, потому что она непродуктивна и отнимает много времени. В идеале код нужно сразу делать чистым, понятным и покрывать тестами. Но хотя современные подходы к разработке ПО не подразумевают дальнейшей отладки, мы каждый день продолжаем сталкиваться с унаследованным кодом, который может быть не покрыт тестами, быть сложным и запутанным. И в результате нам всё же приходится заниматься этим неблагодарным делом.
Сегодня есть множество IDE, поддерживающих работу с Go и позволяющих отлаживать приложения. На текущий момент для Go представлены два отладчика: GDB (но он не поддерживает многие фичи языка, например Go-рутины) и Delve. Многие IDE используют последний как дефолтный отладчик. И в этой статье я расскажу о возможностях Delve: о том, что умеет сам отладчик, а не что нам предоставляет IDE.

Основы работы с Delve
Для того чтобы начать работу с отладчиком, нужно скомпилировать программу на Go и выполнить в командной строке команду dlv debug, находясь в директории с исполняемым файлом. После этого мы попадём в Delve. Для начала работы требуется установить первую точку останова и выполнить команду continue.
Рассмотрим пример.
Возьмём простую программу на Go, которая читает данные из текстового файла и обновляет его, если объём данных не превышает 12 байт. А если объём равен 12 байтам, то программа просто выводит строку hello и завершает выполнение.
package main import ( "fmt" "io/ioutil" "log" "os" ) func main() { file, err := os.Open("test.txt") defer file.Close() data, err := ioutil.ReadAll(file) if err != nil { fmt.Errorf(" problem: %v", err) } fmt.Println(data) fmt.Println(len(data)) if len(data) == 12 { fmt.Println("hello") return } data = append(data, byte(len(data))) err = ioutil.WriteFile("test.txt", data, 0644) if err != nil { log.Fatal(err) } }
Так выглядит моя директория перед компиляцией:

Теперь скомпилируем программу, выполнив команду go build main.go в командной строке. В результате должно получиться вот что:

Получив бинарный файл, заходим в директорию с ним и выполняем команду dlv debug:

Далее устанавливаем в файле точку останова на строке номер 14, выполнив команду break main.go:14:

И запускаем отладку с помощью команды continue:

Исполнение программы остановилось на 14-й строке. Теперь можно посмотреть значения переменных:

Чтобы продолжить отладку, нужно в командной строке либо выполнить команду next (и тогда выполнится следующая строка кода), либо набрать continue, (и программа выполнится до следующей точки останова).

Теперь вкратце расскажу про основные команды Delve, с помощью которых вы сможете отлаживать свои приложения:
-
next — следующая строка;
-
step — вход внутрь вызываемой функции:

-
continue — следующая точка останова (breakpoint):

-
break — установка точки останова, например break m67 main.go:67;
-
cond — задаёт условия, при которых произойдёт останова на текущей команде отладки. Например, при выполнении команды cond m67 len(array) == 8 сработает останова на этой строке, если в массиве будет восемь элементов;
-
breakpoints — отображает все заданные точки останова;
-
print — распечатывает значение выражения или переменной;
-
vars— выводит значения всех загруженных переменных приложения:

-
locals — выводит значения локальных переменных функции:

Это основные команды Delve, которых будет достаточно для начала работы с отладчиком. Разумеется, инструментарий решения гораздо серьёзнее, и подробнее обо всех командах вы можете узнать из официальной документации.
Но главной фишкой Delve является возможность создавать пользовательские команды, которые позволяют гибче использовать отладчик и открывают широкие возможности для автоматизации. Давайте рассмотрим синтаксис и пример создания пользовательской команды.
Пишем свои команды на Starlark
Delve поддерживает синтаксис Starlark — это диалект Python, который позволяет писать полезные и функциональные плагины. Так как Starlark был придуман для написания небольших программ-конфигураций в отладчиках, а не программ, которые будут долго выполняться, он не содержит таких возможностей Python, как классы, исключения и рефлексия.
На Starlark, например, можно написать команду для создания дампа текущего приложения и перезапуска его отладки уже с новыми дампом и данными. Такая функциональность может пригодиться, если какая-то ошибка воспроизводится только в очень «экзотических» случаях.
Структура программ-конфигураций на языке Starlark:
def command_название команды "Комментарий, который будет выведен, если набрать help имя команды" Далее пишем код.
Синтаксис языка можно посмотреть здесь.
Давайте рассмотрим пример создания команды для Delve:
def command_flaky(args): "Repeatedly runs program until a breakpoint is hit" while True: if dlv_command("continue") == None: break dlv_command("restart")
Эта команда будет перезапускать отладку до тех пор, пока не будет достигнута точка останова. Чтобы выполнить её в Delve:
-
Сохраните команду в файл с расширением .star.
-
Запустите Delve.
-
Выполните в командной строке команду source flaky.star.
-
Расставьте точки останова.
-
Выполните команду flaky.
Для работы с flaky возьмём программу из предыдущего раздела. Пример того, что отобразится в консоли отладчика:

Как видите, программа была перезапущена семь раз, и при каждом выполнении условия срабатывала точка останова. Отлавливать такие вещи вручную в Visual Studio Code и других средах разработки не так-то просто.
Если вам интересно, что ещё можно сделать в Delve с помощью Starlark-синтаксиса, за подробностями добро пожаловать сюда. А если вы не любите использовать командную строку или не хотите разбираться в тонкостях «неродного» языка, то давайте рассмотрим, как сделать то же самое на Go.
Написание плагинов на Go
Рассмотрим этот процесс на примере удалённой отладки приложений. В Delve реализован gRPC-сервер, к которому можно обращаться по API. Для этого сначала необходимо установить Delve рядом с приложением. Если вы используете микросервисную архитектуру, то можно добавить этот инструмент в образ вашего контейнера.
Возьмём код из первого раздела и попробуем отладить его с помощью Go. Для этого нам нужно выполнить в командной строке команду:
dlv exec —continue —headless —accept-multiclient —api-version 2 —listen 0.0.0.0:50080 main
Открываем любимую IDE и пишем на Go:
package main import ( "encoding/json" "fmt" "os" "github.com/go-delve/delve/service/api" "github.com/go-delve/delve/service/rpc2" ) func main() { serverAddr := "localhost:50080" funcToTrace := "main.main" // Create a new connection to the Delve debug server. // rpc2.NewClient will log.Fatal if connection fails so there // won't be an error to handle here. client := rpc2.NewClient(serverAddr) defer client.Disconnect(true) // Stop the program we are debugging. // The act of halting the program will return it's current state. state, err := client.Halt() if err != nil { bail(err) } bp := &api.Breakpoint{ FunctionName: funcToTrace, Tracepoint: true, Line: 12, } client.Restart(false) tracepoint, err := client.CreateBreakpoint(bp) if err != nil { bail(err) } defer client.ClearBreakpoint(tracepoint.ID) for _, i := range []int{1, 2} { fmt.Printf("i:\t %d\n", i) client.Restart(false) // Continue the program. stateChan := client.Continue() // Create JSON encoder to write to stdout. enc := json.NewEncoder(os.Stdout) fmt.Println("____________________________________________") fmt.Println("state") for state = range stateChan { // Write state to stdout. enc.Encode(state) } fmt.Println("____________________________________________") } } func bail(s interface{}) { fmt.Println(s) os.Exit(1) }
Что происходит на стороне сервера, когда идёт отладка:

Тут видно, что было несколько перезапусков приложения. На стороне же приложения будет следующий вывод:

Изучим информацию, которую выдаёт Delve:
-
Pid — идентификатор приложения в Linux;
-
Running — запущено ли приложение;
-
Recording — идёт запись информации о процессе;
-
CoreDumping — идёт запись дампа приложения;
-
Threads — информация о потоках исполнения приложения;
-
breakPoint — информация о сработавшей точке останова.
Подробно про выведенную информацию можно почитать здесь.
Отладка приложения с помощью написания другого приложения позволяет создавать анализаторы поведения программы и автоматизировать проверку своих приложений. Если вам захотелось написать что-то такое, то вам поможет gRPC-клиент.
Заключение
Я только поверхностно ознакомил вас с возможностями Delve. Показал, что мы можем отлаживать код и без IDE. Можно писать анализаторы поведения программ и приложения для отладки своего приложения. Наконец, функциональность Delve можно расширять собственными командами, что делает его очень мощным инструментом.
Дополнительная литература
ссылка на оригинал статьи https://habr.com/ru/company/ozontech/blog/701198/




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