Мы создаем ПО для разработки и поддержки баз данных Oracle, и статический анализатор PL/SQL является одной из основных фич наших приложений. Кто знаком с Oracle, тот хорошо знает что такое PL/SQL.

Известная поговорка гласит: «Лучше один раз увидеть, чем сто раз услышать». Поэтому мы решили заимпрувить статический анализатор таким образом, чтобы он визуализировал код в виде блок-схем (Flowcharts) и диаграмм вызовов (Call Trees). Хоть и нарисовать блоки и их связи несложно, оптимизировать их расположение на «листе» представлялось задачей, требующей значительных усилий. Чтобы стрелки минимально пересекались и обтекали блоки, блоки объединялись в группы, и диаграмма при этом не превращалась в «кашу», нужно было потратить много сил и времени.

И тогда мы решили поискать готовое решение, дабы не изобретать велосипед. Наше внимание сразу привлек Graphviz – open source решение по визуализации диаграмм. Первые его версии были разработаны компанией AT&T, а теперь он доступен как набор утилит и библиотек, а также в исходниках под лицензией Eclipse Public License (EPL).

Его движок диаграмм использует язык описания графов DOT, который представляет собой текстовое описание структуры графа: вершины, их связи, группы и атрибуты для их визуального оформления.

Описание простейшего графа:

digraph graphname {
  a -> b;
}

Если необходимо добавить узлам атрибуты, например подписи, то необходимо отдельно описать узлы:

digraph graphname {
  a [label="source"]
  b [label="destination"]
  a -> b;
}

Далее покажу на примере простой процедуры на PL/SQL:

CREATE PROCEDURE WRITE_STRING(
    P_MESSAGE_TYPE IN NUMBER
  , P_MESSAGE IN VARCHAR2
)
IS
BEGIN
  IF P_MESSAGE_TYPE >= LOG_LEVEL THEN
    ADD_LOG_RECORD(P_MESSAGE_TYPE, P_MESSAGE);
  END IF;
END WRITE_STRING;

Код понятен, даже если вы не знакомы с синтаксисом PL/SQL.

А теперь опишем этот код на языке DOT. Пояснения снова излишни:

digraph G {
  N0 [label="”]
  N1_COND [label="P_MESSAGE_TYPE \>= LOG_LEVEL ?"];
  N2 [label="ADD_LOG_RECORD(P_MESSAGE_TYPE, P_MESSAGE);"];
  EXIT_LABEL [label="END"];
  
  N0 -> N1_COND
  N1_COND -> N2
  N1_COND -> EXIT_LABEL
  N2 -> EXIT_LABEL 
}

Теперь можно «скормить» этот файл Graphviz, подставив соответственно вместо %PNGFILE% и %DOTFILE% имена выходного (png) и входного (dot) файлов:

dot -v -Tpng –o%PNGFILE% %DOTFILE%

При помощи Graphviz из описания выше получается такая картина:



Наглядно, но весьма аскетично.

Можно сделать диаграмму привлекательнее, добавив атрибуты для определения формы блоков (shape=diamond), подписи стрелок (label="Yes") и цвета color, fontcolor.

Также мы можем объединить в подграф блоки, которые являются исполняемыми. Это реализуется конструкцией subgraph {}, внутри которой мы можем перечислить имена блоков, включенных в подграф, и указать атрибуты для визуального оформления (цвет рамки).

digraph G {
  comment="PL/SQL flowchart generated by ClearSQL";
  node [fontname="Arial", fontsize=8, height=.2, width=.25, color="#000000", fontcolor="#000000"];
  edge [fontname="Arial", fontsize=8, arrowsize=.8, color="#000000", fontcolor="#000000"];
  
  N0 [shape=plaintext, label="", style=invis];
  N1_COND [shape=diamond, conditional=true, label="P_MESSAGE_TYPE \>= LOG_LEVEL ?"];
  N2 [shape=box, label="ADD_LOG_RECORD(P_MESSAGE_TYPE, P_MESSAGE);"];
  EXIT_LABEL [shape=Msquare, label="END"];
  
  N0 -> N1_COND [color=darkgoldenrod];
  N1_COND -> N2 [label="  Yes " color="#228b22" fontcolor="#228b22"];
  N1_COND -> EXIT_LABEL [label="  No " color="#ff7e40" fontcolor="#ff7e40"];
  N2 -> EXIT_LABEL  
  
  subgraph "cluster_#1" {
    color="#0000ff"; 
    N1_COND; N2
  }  
}

Получается вполне симпатичная блок-схема, которую и людям показать не стыдно.



Чтобы код могли читать не только программисты, но и «простые смертные», мы придумали такую фишку: приделали распознавание специальных тегов в комментариях, которые бы разработчик писал как псевдокод – язык описания алгоритмов. Это помогло увеличить ценность блок-схемы, которая теперь может отображать бизнес-логику, а не только визуализировать листинг.

CREATE PROCEDURE WRITE_STRING(
      P_MESSAGE_TYPE IN NUMBER
    , P_MESSAGE IN VARCHAR2
)
IS
BEGIN
  --## Уровень данного сообщения по отношению к порогу журналирования
  --##@true Выше
  --##@false Ниже
  IF P_MESSAGE_TYPE >= LOG_LEVEL THEN
    --## Добавляем запись в журнал
    ADD_LOG_RECORD(P_MESSAGE_TYPE, P_MESSAGE);
  END IF;
END WRITE_STRING;

Вуаля! Graphviz дает возможность делать потрясающие визуализации с минимальными затратами.


Комментарии (8)


  1. MetaDone
    04.09.2017 12:17

    Graphviz еще используется в https://symfony.com/doc/current/components/workflow.html
    Удобно и наглядно получаются графики возможных переходов состояний у объекта
    image


  1. AlexVist
    04.09.2017 12:27

    Я в своем проекте сппр так же использую Graphviz. Это позвояет визуализировать логическую структуру. Так же следует упомянуть о других возможностях. Это использование svg формата. Что позволяет визуализировть в браузерах (более удобно) и возможностях гипер-ссылок. Что позволяет сделать отображение еще и интерактивным.


  1. raydac
    04.09.2017 12:33

    решение интересное, правда риски всё теже — разъедется содержимое псевдокода с реализацией при активной разработке


    1. kspksp Автор
      04.09.2017 12:34

      Согласен. Актуальность комментариев в коде тоже надо поддерживать.


      1. AlexVist
        04.09.2017 12:56

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


        1. kspksp Автор
          04.09.2017 14:44

          Мне кажется это не совсем так. Есть два типа блока, с помощью которых можно выводить данные в виде таблицы: shape=record|Mrecord. Отличие второго от первого — скругленные наружные углы. Больше ни в каких типах блоков сделать столбцы/строки нельзя.


  1. flatscode
    04.09.2017 16:33
    +1

    Часто блок-схемы (Activity Diagram) составляю с помощью plantuml, который для некоторых случаев использует graphviz под капотом.

    У plantuml, по-моему, язык попроще.

    Такой текст:

    @startuml
    start
    
    if (Graphviz installed?) then (yes)
      :process all\ndiagrams;
    else (no)
      :process only
      __sequence__ and __activity__ diagrams;
    endif
    
    stop
    
    @enduml


    Отрисует такую диаграмму:

    image

    Так же есть плагины для использования в IDE от Jetbrains.

    Еще пара-тройка примеров
    image
    image
    image
    image
    image


  1. habradante
    04.09.2017 16:55

    Используем Graphviz + PlantUML для авто генерации диаграмм классов и для составления документации (Sequence Diagram). Очень удобно для составления документации по проекту.