Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stream debugger redesign #6

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
Draft

Conversation

Soarex16
Copy link
Owner

@Soarex16 Soarex16 commented Apr 30, 2023

This should be translated to English before submiting PR to upstream.

Breakpoint-based stream debugger engine

Текущая реализация стрим дебаггера основана на генерации кода, похожего на исходный стрим, и его исполнении с помощью механизма evaluate expression.
Такой подход имеет ряд проблем:

  • если код содержит сайд-эффекты, то они будут выполнены два раза
  • и т.д.

TODO:

  • В qualifier-е стоит всегда делать sequential потому что мы не знаем какой стрим может быть на входе
    • добавить на этот кейс тесты
  • exception handling
    • при исключениях правильно выходить из стрима
      • учесть случай rethrow потому что мы удаляем обработчик при первом exception breakpoint
    • пробрасывать ошибки из jdi (сейчас они глотаются)
      можно в команды дебаггера передавать колбек, который будет вызываться при исключении. То есть внутри команды у нас будет try/catch, который будет ловить все исключения из команды и пробрасывать ошибки
  • пропускать user брейкпоинты при трассировке
  • все типы операций
    • parallel intermediate operation
    • Типы форматтеров результата:
      • Optional (min, max, findAny, findFirst)
      • Matching (anyMatch, allMatch, noneMatch)
      • SimpleScalar (count, sum, etc)
      • ToCollection (toArray, collect)
  • поддержка библиотек:
    • java stdlib
    • kotlin stdlib (sequences/collection operations)
    • jb iterables
    • stream ex
  • parallel streams
  • рассмотреть случай, когда много стримов в одном месте
  • Взять Lupa и собрать с гитхаба статистику по использованию Stream Api (и прочие либы)
    • Попробовать выделить общую структуру стримов
    • Посчитать статы (какие методы используются чаще, длина стрима, терминальные операции)
  • Переписать ui на новый dsl
  • Баги
    • Если стрим записать в одну строчку и протрассировать, то step out выйдет на ту же строчку, где мы были. Т. е. пользователь сможет нажать кнопку трассировку еще раз. Мне кажется это не очень хорошее поведение. Пример:
      final int[] ints = IntStream.of(1, 2, 3, 4).toArray();
    • Если пользователь остановился в середине стрима (т.е. когда часть промежуточных операций уже создала свои стейджи), то не надо ему позволять трассировать (сейчас это можно, потому что evaluate expression tracer-у это не важно).
    • LinkedChains - третий стрим (.stream().sorted().collect(Collectors.toList())) срабатывает корректно только если потрассировать предыдущие. Проблема похожа на double use, но тут проблема в том, что брейкпоинт становится на первый вызов stream().
    final List<Integer> result = Stream
           .of(1, 2, 3, 4, 5, 6).map(x -> x * x).collect(Collectors.toList())
           .stream()/*actual*/.filter(x -> x % 2 == 0).collect(Collectors.toList())
           .stream()/*expect*/.sorted().collect(Collectors.toList())
           .stream().distinct().collect(Collectors.toList());
    Здесь курсивом выделен метод, на который мы хотим поставить брейпоинт, жирным выделен метод, на котором брейкпоинт срабатывает по факту. Можно попробовать фильтровать по location (но возможно location будет работать только для строк, тогда это сломается в случае, если стрим будет записан в одну длинную строчку)
    По той же причине скорее всего не работает последний стрим.
  • визуализация того как работают параллельные стримы https://stackoverflow.com/a/34381806. Т.е. навешиваем свой коллектор, который возвращает стрим, но попутно снифит как данные делятся.
  • Запросить у кого-нибудь архитектурное ревью (Тагир/Виталий/Егор Ушаков?)

(cherry picked from commit 1aa6bd8)

Refactor all breakpoint tracer related logic

Add ability to load compiled classes into target VM

(cherry picked from commit d29c75f)

Add errors reporting

Fix value keeping

(cherry picked from commit 299d39b)

Add ability to intercept qualifier expression stream

Unify breakpoint resolving mechanism
Add draft for breakpoint based library support
Replace redundant @throws annotations with doc comments

(cherry picked from commit 8ad03fe)

Code cleanup based on code review

(cherry picked from commit a04d577)

Experiments with value formatting

(cherry picked from commit 4668757)

Replace helper class compilation with precompiled class loading

Remove runInDebuggerThread helper method because it causes misunderstanding on which thread the action will be dispatched

(cherry picked from commit 06a6931)

Add ability to choose stream tracing engine in registry on the fly

Remove unused code
Remove redundant runInEdt, because TraceStreamAction does is by itself

(cherry picked from commit f512118)

Refactor value manager and dependent classes to provide fresh suspend context.

Move ValueContext into separate implementation
Fix bug in value interception when stream chain contains multiple calls of the same stream operator
Move code for disabling object collection into separate entity named ObjectStorage

Move value interception logic from JavaMethodBreakpointFactory to ValueCollector

Fix problem with step out

Add test case for breakpoint based stream tracer

Add forEach support

Keep return value of called methods

Remove useless comments

Add more tests for breakpoint based engine

Fix null pointer exception when keeping method return value from being collected by GC

New stream debugger architecture prototype

Make method invocation inside value manager context more convenient

Better naming

Fix result formatting

Move Stream Debugger support lib into separate artifact

Now all helper classes are built into a separate jar file, which is built during the idea compilation

Proof-of-concept of method exit value sniffing

(cherry picked from commit 1aa6bd8)

Refactor all breakpoint tracer related logic

Add ability to load compiled classes into target VM

(cherry picked from commit d29c75f)

Add errors reporting

Fix value keeping

(cherry picked from commit 299d39b)

Add ability to intercept qualifier expression stream

Unify breakpoint resolving mechanism
Add draft for breakpoint based library support
Replace redundant @throws annotations with doc comments

(cherry picked from commit 8ad03fe)

Code cleanup based on code review

(cherry picked from commit a04d577)

Experiments with value formatting

(cherry picked from commit 4668757)

Replace helper class compilation with precompiled class loading

Remove runInDebuggerThread helper method because it causes misunderstanding on which thread the action will be dispatched

(cherry picked from commit 06a6931)

Add ability to choose stream tracing engine in registry on the fly

Remove unused code
Remove redundant runInEdt, because TraceStreamAction does is by itself

(cherry picked from commit f512118)

Refactor value manager and dependent classes to provide fresh suspend context.

Move ValueContext into separate implementation
Fix bug in value interception when stream chain contains multiple calls of the same stream operator
Move code for disabling object collection into separate entity named ObjectStorage

Move value interception logic from JavaMethodBreakpointFactory to ValueCollector

Fix problem with step out

Add test case for breakpoint based stream tracer

Add forEach support

Keep return value of called methods

Remove useless comments

Add more tests for breakpoint based engine

Fix null pointer exception when keeping method return value from being collected by GC

New stream debugger architecture prototype

Make method invocation inside value manager context more convenient

Better naming

Fix result formatting

Move Stream Debugger support lib into separate artifact

Now all helper classes are built into a separate jar file, which is built during the idea compilation

Fix firEach(Ordered) test for breakpoint based engine
Add initial library support
Fix StreamDebuggerUtils class loading
…tream tracing

Change breakpoint requestors behaviour, so they are able to pause execution
Time ticking now optional in collectors (made for compatibility with result interpreters)
Fix tests
@Soarex16 Soarex16 force-pushed the breakpoint-debugger-redesign branch from 5e020f1 to 46f259b Compare May 26, 2023 21:46
Extract before/after formatting to separate utils class
Extend call transformers interface with time parameter to be able to use time in source call transformations
Fix exception with multiple step requests caused by exception handling logic
Merge all collectors to UniversalCollector
Add matchers to support lib
@Soarex16 Soarex16 changed the title Breakpoint debugger redesign Stream debugger redesign Jun 1, 2023
@Soarex16
Copy link
Owner Author

Soarex16 commented Jun 1, 2023

Обработка вложенных/связанных стримов

Use cases:

  • Вложенные стримы

    final List<Integer> result = IntStream
          .iterate(2, x -> x + 1)
          .filter(x -> {
            long divisors = IntStream
              .range(2, x)
              .filter(y -> x % y == 0)
              .count();
            return divisors == 0;
          })
          .limit(10)
          .boxed()
          .collect(Collectors.toList());
  • Стрим, являющийся результатом другого стрима

    final List<Integer> result = Stream
          .of(1, 2, 3, 4, 5, 6).map(x -> x * x).collect(Collectors.toList())
          .stream().filter(x -> x % 2 == 0).collect(Collectors.toList())
          .stream().sorted().collect(Collectors.toList())
          .stream().distinct().collect(Collectors.toList());
  • Стримы в рекурсивных методах

     public static int recursiveStream(int n) {
        return IntStream
          .range(0, n)
          .map(x -> recursiveStream(x))
          .filter(x -> x % 100 != 0)
          .sum();
     }

Необходимо научить новый стрим дебаггер обрабатывать эти случаи корректно.

Идеи

  1. Добавить в breakpoint requestor-ы логику, которая будет проверять, что текущий оператор вызывается из нужного нам метода.
    1.1. Мы уже так делаем, чтобы обрабатывать исключения
    1.2. Это поможет обрабатывать вложенные стримы, и рекурсию (для рекурсии еще нужно будет запоминать глубину стека), но не будет работать для связанных стримов потому что они все вызываются в одном и том же методе.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant