Сборка Go проектов с Bazel

В данном посте расскажем как анализировать проекты на языке Go, как это работает и как проанализировать проекты с системой сборки Bazel.

Что нового

Мы поддержали в Svace сборку проектов для последней версии go 1.20. Также была поддержана базовая версия сборки проектов на Go для системы Bazel (только для дистрибутивов Linux). Данные изменения будут доступны в ближайшем релизе серии Svace 3.4.

Стандартный сценарий использования

При сборке проекта с помощью стандартной системы сборки go build схема использования будет выглядеть следующим образом:

svace init
svace build [команда сборки или скрипт сборки]

На данном этапе будет построено промежуточное представление для всего проекта и его зависимостей. Чтобы запустить анализ, необходимо использовать команду svace analyze.

Например, пройдем все шаги работы со Svace на проекте Xray-core. На момент написания статьи коммит 46d8bb5. В данном проекте используется версия go 1.20.

cd Xray-core
svace init
svace build go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main

Получим следующий вывод:

Now Svace will preprocess captured data.
Processing source code markup data...
[**********************************************************************] 100%
Assembled build object: [BUILD]9078355afe76cf71d39d75ae3e47737a240fb4c4

Далее запустим анализ на данном проекте:

svace analyze

По окончании анализа будет выведен список и количество найденных предупреждений:

...
Analysis results:
[BUFFER_OVERFLOW]
  DYNAMIC_OVERFLOW: 3
  OVERFLOW_UNDER_CHECK: 10
[DEAD_CODE]
  REDUNDANT_COMPARISON.ALWAYS_FALSE: 51
  UNREACHABLE_CODE: 479
  UNREACHABLE_CODE.RET: 29
[DEREF_OF_NULL]
  DEREF_AFTER_NULL: 9
  DEREF_AFTER_NULL.EX: 22
  DEREF_OF_NULL.ASSIGN: 2
  DEREF_OF_NULL.COND: 1
...
Total warnings: 4263
...

Далее все предупреждения можно будет увидеть в веб-интерфейсе Svacer.

Как это работает

При сборке с помощью встроенной системы go build, Svace будет перехватывать вызов исполняемого файла go. Далее исходя из аргументов go [build/install] с помощью go list будут найдены все зависимости для проекта.

Сборка Bazel

В случае сборки с Bazel, внутренний процесс сборки проекта на Go происходит по-другому. Во-первых, Bazel не использует вызов go build напрямую, а самостоятельно компилирует и линкует проект с помощью go tool [compile/link и т.д.] команд. Во-вторых, информация о зависимостях передается с помощью правил (go-rules), а именно go_repository, которая с помощью go mod download скачивает исходные коды библиотек во внутреннюю GOPATH вашего проекта, которую Bazel создает сам. Детальное описание работы сборки Go в Bazel можно посмотреть здесь.

Мы поддержали базовую версию анализа проектов, использующих Bazel, которая также будет доступна в ближайшем релизе серии Svace 3.4.

Чтобы проанализировать проект, использующий Bazel, предварительно необходимо запустить следующие команды (иначе не все компиляции будут перехвачены):

bazel clean --expunge   # для очистки кэша
bazel shutdown          # для остановки сервера компиляций bazel

Далее svace build должен содержать следующий флаг: --enable-ptrace-all, без него соответствующие команды компиляции go tool [command] перехвачены не будут.

Если вы хотите проанализировать только Go компиляции, то дополнительно добавьте флаги --disable-language all, --enable-language go, благодаря которым будут перехватываться/анализироваться только Go компиляции. Можно использовать как с svace build, так и с svace analyze.

Например, команда сборки вашего проекта: bazel build //..., то svace build будет выглядеть так:

svace build --disable-language all --enable-language go --enable-ptrace-all bazel build //...

Также для корректной работы Svace папка проекта .svace-dir должна быть создана внутри корня анализируемого проекта (актуально только для проектов Bazel).

svace build будет также работать при использовании bazelisk.

Для примера рассмотрим проект bazel-ethereum. Версия Go в go.mod: go 1.15:

cd bazel-go-ethereum
bazel clean --expunge
bazel shutdown
svace init
svace build --enable-ptrace bazelisk build --spawn_strategy=local --define version="local" //... 

Запускаем анализ:

svace analyze --disable-language all --enable-language go // т.к. также перехватились C/C++ компиляции

По окончании анализа будет выведен список и количество найденных предупреждений:

...
Goa import time:  6s
Warning suppression...
Suppression time: 684ms

Analysis results:
[BUFFER_OVERFLOW]
  DYNAMIC_OVERFLOW: 5
  OVERFLOW_UNDER_CHECK: 2
  OVERFLOW_UNDER_CHECK.PROC: 1
...
Total warnings: 4415
...

Далее все предупреждения можно будет увидеть в веб-интерфейсе Svacer.

Как это работает

Наш компонент перехвата сборки отслеживает низкоуровневую команду go tool compile; в режиме модулей, также перехватываются команды go mod download. Внутри папки .svace-dir/bitcode создается GOPATH (.gopath). Данная GOPATH будет использована при построении промежуточного представления необходимого для анализа, куда будут скачаны нужные версии зависимостей, заданные для установки с помощью go mod download через правило go_repository в таком формате:

https://github.com/bazelbuild/bazel-gazelle/blob/master/repository.md#go_repository

# Download using "go mod download"
go_repository(
    name = "com_github_pkg_errors",
    importpath = "github.com/pkg/errors",
    sum = "h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=",
    version = "v0.8.1",
)

В другом случае, будет проанализирована последняя версия зависимости, исходный код которой будет скачан в Svace .gopath.


Варвара Дворцова