GoCPG vs Joern: Сравнительный анализ

GoCPG vs Joern: Сравнительный анализ

Дата: 2026-02-25 Joern: 4.0.426 (flatgraph) GoCPG: 0.1.0 (DuckDB) Кодовые базы: GoCPG (Go, 215 файлов), CodeGraph (Python, 1183 файла)


Резюме

GoCPG в 3.9x быстрее Joern при генерации CPG и создаёт более богатые графы с дополнительными типами рёбер (DDG, PDG, eval_type, binds_to). Joern генерирует в 2.6x больше CDG-рёбер из-за моделирования вызовов функций как точек ветвления (пути исключений). Оба инструмента используют идентичные базовые алгоритмы (доминаторы Cooper-Harvey-Kennedy, CDG по Ferrante-Ottenstein-Warren). Ключевое архитектурное различие: GoCPG — нативный Go-бинарник с записью напрямую в DuckDB, Joern — JVM/Scala-система с in-memory flatgraph.


1. Производительность

Парсинг + полный конвейер анализа

Кодовая база GoCPG Joern Ускорение
GoCPG (Go, 215 файлов) 13.1с 51.8с 3.9x
CodeGraph (Python, 1183 файла) 85.5с 97.7с 1.1x

Время по пассам (Go)

Фаза GoCPG (параллельный DAG) Joern (последовательный)
CFG в рамках 4.57с 638мс
Доминаторы в рамках 4.57с 615мс
CDG в рамках 4.57с 129мс
Reaching Defs в рамках 4.57с 2,827мс
Граф вызовов в рамках 4.57с 74мс
Все пассы 4.57с (30 пассов, параллельно) ~6.9с (последовательно)

Преимущество GoCPG: конвейер на основе DAG выполняет все 30 пассов параллельно с разрешением зависимостей. Joern выполняет пассы последовательно.

Память и хранение

Метрика GoCPG Joern
Формат вывода DuckDB (SQL-запросы) flatgraph (проприетарный бинарный формат)
Среда выполнения Нативный бинарник (без JVM) JVM + Scala
Инкрементальные обновления Git diff, отслеживание веток Не поддерживается

2. Сравнение узлов

Go кодовая база (исходники GoCPG, 215 файлов)

Тип узла GoCPG Joern Разница Пояснение
method 10,549 5,762 +83% GoCPG создаёт внешние заглушки методов
call 83,908 95,061 -12% Joern инлайнит операторы как вызовы
file 430 375 +15% GoCPG включает testdata
type_decl 2,060 879 +134% GoCPG резолвит интерфейсные типы
identifier 79,217 87,603 -10%
literal 24,537 31,874 -23% Joern разбивает составные литералы
local 11,200 12,897 -13%
param 14,730 10,333 +43% GoCPG включает method_parameter_out
return 5,262 4,620 +14%
block 37,449 28,449 +32%
control_structure 11,368 13,332 -15%
member 3,169 3,108 +2%
comment 12,752 0 Joern отбрасывает все комментарии
method_return 9,303 5,762 +61%
type 2,732 0 Joern не сохраняет TYPE-узлы для Go
namespace 216 49 +341% GoCPG: один на пакетный путь
method_ref 191 180 +6%
annotation 237 0 Joern игнорирует Go build tags
ИТОГО 349,710 332,652 +5%

Уникальные узлы GoCPG (отсутствуют в Joern): - finding (1,199) — результаты анализа шаблонов - binding (6,253) — привязки тип-метод - field_identifier (19,876) — доступ к полям структур - modifier (670) — модификаторы видимости - import (1,097) — объявления импортов - method_parameter_out (8,900) — выходные параметры - type_ref (2,137) — ссылки на типы

Python кодовая база (CodeGraph, 1183 файла)

Тип узла GoCPG Joern Разница Пояснение
method 200,790 61,700 +225% GoCPG создаёт заглушки для всех внешних методов
call 1,431,271 661,422 +116% GoCPG моделирует декораторы/comprehensions как вызовы
file 4,515 1,540 +193% GoCPG включает init.py, конфиги, тесты
type_decl 92,998 56,569 +64%
identifier 1,286,815 612,328 +110%
literal 615,354 255,001 +141% GoCPG извлекает части f-строк
local 428,845 218,693 +96%
param 292,805 133,839 +119%
block 390,614 143,495 +172%
comment 105,730 0 Joern отбрасывает все комментарии
ИТОГО 6,474,877 2,894,558 +124%

3. Сравнение рёбер

Go кодовая база

Тип ребра GoCPG Joern Разница Пояснение
cfg 270,587 268,470 +0.8% Почти идентично
cdg 59,123 153,152 -61% См. раздел 4
dominate 192,710 253,562 -24%
post_dominate 259,891 251,657 +3%
reaching_def 821,197 671,111 +22% GoCPG: межпроцедурный анализ
call 90,844 95,426 -5%
contains 305,724 285,865 +7%
argument 170,722 188,583 -9%
ast 195,808 332,152 -41% Joern материализует полное AST
ref 92,320 66,165 +40% GoCPG резолвит больше ссылок
condition 11,039 10,345 +7%
receiver 13,857 16,705 -17%
source_file 335,713 5,429 +6,084% GoCPG связывает все узлы с исходным файлом
parameter_link 8,731 10,333 -16%
ddg 243,723 Только GoCPG: граф зависимостей данных
pdg 722,704 Только GoCPG: граф программных зависимостей
eval_type 277,592 Только GoCPG: типизация выражений
binds_to 50,739 Только GoCPG: привязки параметров типов
inherits_from 2,156 Только GoCPG для Go
capture 383 Только GoCPG: захват замыканий
ИТОГО 4,132,053 2,608,955 +58%

Python кодовая база

Тип ребра GoCPG Joern Разница
cfg 4,170,935 1,889,935 +121%
cdg 1,786,888 668,084 +167%
reaching_def 8,200,984 4,205,949 +95%
call 1,733,711 15,906,040 -89%
ast 5,851,652 2,678,327 +118%
ddg 1,927,830 Только GoCPG
pdg 7,716,042 Только GoCPG
eval_type 4,191,411 2,160,473 +94%
ИТОГО 65,874,890 35,928,055 +83%

Примечание: 15.9M call-рёбер Joern для Python — результат NaiveCallLinker, который агрессивно связывает все вызовы по имени (~10x ложных срабатываний).


4. Анализ расхождения CDG: 59K vs 153K

Ключевая находка

При почти идентичном числе CFG-рёбер (270K vs 268K), Joern создаёт в 14.2x больше ветвящихся узлов:

Метрика CFG GoCPG Joern Соотношение
Линейные узлы (1 преемник) 268,401 239,514
Ветвящиеся узлы (2+ преемников) 970 13,734 14.2x
— из них CALL ~841 13,031 15.5x
— из них IDENTIFIER ~129 703 5.4x

Причина

Joern моделирует каждый вызов функции как точку ветвления — вызов может вернуть результат нормально или выбросить исключение (два пути). В Go, где ошибки обрабатываются явными возвратами (if err != nil), а не исключениями, это создаёт 13,031 ложное ветвление.

Цепочка влияния

Больше ветвящихся узлов → Другие деревья постдоминаторов →
Большие постдоминаторные фронтиры → Больше CDG-рёбер

GoCPG:    970 ветвящихся →  59K CDG
Joern: 13,734 ветвящихся → 153K CDG

Корректность для Go

Для Go CDG GoCPG точнее: Go не использует try/catch исключения. Моделирование путей исключений Joern создаёт ложные зависимости управления, которых нет в реальном потоке.

Алгоритмы идентичны

Шаг Joern GoCPG
Доминаторы Cooper-Harvey-Kennedy Cooper-Harvey-Kennedy
Постдоминаторы На обратном CFG На обратном CFG
CDG Постдоминаторный фронтир IPDOM chain walk

5. Архитектурное сравнение

Проектирование конвейера

Аспект GoCPG Joern
Язык Go (нативный бинарник) Scala (JVM)
Планирование пассов DAG с топосортировкой, параллельно Последовательный список
Хранилище DuckDB (SQL-запросы) flatgraph (проприетарный формат)
Инкрементальные обновления Git diff + отслеживание веток Не поддерживается

Конвейер анализа

Фаза GoCPG (30 пассов) Joern (~25 пассов)
Парсинг Frontend → DiffGraph → CPGGraph Frontend → flatgraph
Типы TypeNode, TypeDecl, Inheritance, TypeRecovery TypeDeclStub, MethodStub, TypeEval
Поток управления CFG, Dominator, PostDominator, CDG CfgCreation, CfgDominator, CdgPass
Граф вызовов CallGraph, CallResolution, ImportResolver StaticCallLinker, DynamicCallLinker
Поток данных Ref, AliasAnalysis, ReachingDef, InterproceduralReachingDef ReachingDefPass
Комбинированные PDG (CDG + данные), DDG (не вычисляется)
Обогащение Metrics, Comments, Findings, PatternMatch ContainsEdge
Домен Annotation, Tags, UseCasePropagation, VCS (нет)

Уникальные возможности GoCPG

  1. DDG/PDG — рёбра зависимостей данных и программных зависимостей
  2. AliasAnalysisPass — внутрипроцедурный анализ указателей (p = &x → pts[p]={x}, копирование указателей, разрешение p->fieldobj.field)
  3. Межпроцедурный Reaching Def — анализ потока данных между функциями + распространение побочных эффектов глобальных переменных
  4. Тернарные косвенные вызовы(cond ? f1 : f2)(args) создаёт отдельные CallNode для каждой ветви (C/C++)
  5. PatternMatchPass — структурный поиск шаблонов в конвейере
  6. FindingGenerationPass — находки качества/безопасности как узлы CPG
  7. DomainAnnotationPass — доменная разметка (PostgreSQL, Django и др.)
  8. VCSTagPass — теги автора, частоты изменений из git
  9. Watch mode — наблюдение за файловой системой с live-обновлением CPG
  10. Инкрементальные обновления — на основе git diff с отслеживанием веток и поддержкой слияний
  11. DuckDB — SQL-запросы без JVM

Уникальные возможности Joern

  1. Интерактивный REPL — Scala-консоль для ad-hoc запросов
  2. 13 модулей анализа (vs 11 у GoCPG) — Ruby, Swift
  3. GHIDRA — анализ бинарников
  4. NaiveCallLinker — агрессивное связывание вызовов по имени
  5. PythonTypeRecovery — итеративный вывод типов (2 прохода)

6. Сравнение потоков данных

Reaching Definitions

Аспект GoCPG Joern
Гранулярность Внутрипроцедурный + межпроцедурный Только внутрипроцедурный
Анализ указателей Анализ points-to для цепочек разыменования Отсутствует (Joern #5580, #5668)
Глобальные побочные эффекты Межпроцедурное распространение записей в глобальные переменные Отсутствует (Joern #5581)
Семантика стандартной библиотеки YAML-конфигурации для 11 языков Жёстко закодировано в Scala
Тайм-аут 15с на метод Настраиваемый max-num-def
Рёбра (Go) 821,197 671,111 (+22%)
Рёбра (Python) 8,200,984 4,205,949 (+95%)

Граф вызовов

Аспект GoCPG Joern
Стратегия FQN с 4-уровневым откатом Static + Dynamic + Naive
Go (рёбра) 90,844 95,426
Python (рёбра) 1,733,711 15,906,040 (~10x ложных срабатываний)

7. Паритет по Python (с коррекцией дупликации)

Ошибка: дупликация путей к файлам (~2x раздувание)

GoCPG имеет ошибку нормализации путей: каждый исходный файл сохраняется с абсолютным (D:\work\codegraph\src\...) и относительным (..\src\...) путём, создавая ~2x FILE-узлов и дублируя все содержащиеся узлы/рёбра.

Метрика Значение
Файлов с абсолютным путём 2,345
Файлов с относительным путём 2,170
Пересекающихся (один файл, два пути) 2,336
Уникальных файлов 2,345
Файлов в Joern 1,540

Причина: orchestrator.go:280 использует абсолютные пути из filepath.WalkDir(). Резолвинг импортов/типов в Python-модуль анализае создаёт записи с относительными путями. CPGGraph.filesByName использует путь как ключ — оба варианта сосуществуют. full_name метода включает путь к файлу (filepath:ClassName.method), поэтому дедупликация по full_name не работает.

Сравнение узлов (дедупликация)

Используя только абсолютные пути как истинные дедуплицированные значения:

Тип узла GoCPG (дедупл.) Joern Разница Пояснение
Внутренние методы 42,923 47,749 -10% Близкий паритет
Внешние заглушки 117,118 13,951 +740% GoCPG агрессивнее создаёт заглушки
call 740,272 661,422 +12% GoCPG: декораторы, comprehensions как вызовы
identifier 664,721 612,328 +9% Близкий паритет
literal 317,917 255,001 +25% GoCPG: части f-строк
type_decl 47,789 56,569 -16% Joern: PythonTypeRecovery добавляет больше
comment ~52,865 0 Joern отбрасывает комментарии

Сравнение рёбер (оценка /2)

Тип ребра GoCPG (сырое) GoCPG /2 (оценка) Joern Разница
cfg 4,170,935 ~2,085K 1,890K +10%
cdg 1,786,888 ~893K 668K +34%
reaching_def 8,200,984 ~4,100K 4,206K -3%
call 1,733,711 ~867K 15,906K -95%
ast 5,851,652 ~2,926K 2,678K +9%
ddg 1,927,830 ~964K Только GoCPG
pdg 7,716,042 ~3,858K Только GoCPG

Примечание: 15.9M call-рёбер Joern — результат NaiveCallLinker (~18x ложных срабатываний). FQN-резолвинг GoCPG значительно точнее.

Вердикт по Python

Паритет достигнут на ключевых метриках после коррекции дупликации: - Внутренние методы: -10% — незначительное расхождение в генерации синтетических методов - CFG: +10%, Reaching defs: -3%, AST: +9% — всё близко к паритету - CDG: +34% — ожидаемо: GoCPG правильно моделирует ветвление исключений Python - Граф вызовов: GoCPG в 18x точнее NaiveCallLinker Joern - Эксклюзивно: DDG, PDG, eval_type, binds_to — только GoCPG


8. Покрытие языков

Язык GoCPG Joern
C/C++ Да Да
Go Да (нативный go/parser) Да
Python Да Да
JavaScript/TypeScript Да Да
Java Да Да
Kotlin Да Да
C# Да Да
PHP Да Да
1C:Предприятие Да Нет
Ruby Нет Да
Swift Нет Да
Бинарники (GHIDRA) Нет Да

9. Итоговая таблица

Измерение GoCPG Joern Лидер
Скорость парсинга 13с 52с GoCPG (3.9x)
Без JVM Да Нет GoCPG
SQL-запросы к результату Да Нет GoCPG
Типы узлов 22 16 GoCPG
Типы рёбер 22 (DDG, PDG) 14 GoCPG
CDG для Go 59K (точный) 153K (пути исключений) GoCPG
Паритет Python -10% методов, +10% CFG Базовый Ничья (паритет)
Граф вызовов Python 867K (точный) 15.9M (~18x FP) GoCPG
Глубина анализа данных Межпроцедурный Внутрипроцедурный GoCPG
Инкрементальные обновления Git-based Нет GoCPG
Поиск шаблонов YAML + tree-sitter Scala DSL Ничья
Интерактивный REPL Нет Да Joern
Число языков 11 13 + GHIDRA Joern
Экосистема Проприетарный Open source Joern

Создано: 2026-02-25 | GoCPG v0.1.0 vs Joern v4.0.426