CME is Bad
Модификация для отладки исключений, связанных с одновременным изменением данных (Concurrent Modification Exceptions) и выходом за пределы допустимого диапазона индексов (Index Out of Bounds Exceptions).
Быстрое задание
Как далеко вы сможете убежать, прежде чем вас поймают толпы?
Проверка Minecraft
Подтвердите свой запуск
Пройдите быструю проверку, чтобы получить свой код.
CME is Bad
Резюме
Игроки и разработчики модпаков иногда сталкиваются с вылетами игры из-за `ConcurrentModificationExceptions` (CME) и `IndexOutOfBoundsExceptions` (IOOBE), которые предоставляют только трассировку стека текущего потока, и сложно определить, какой мод их вызывает. Вот один из сбоев, вызванный SimpleReloadInstance в Forge версии 1.20.1:

Вот для чего и разработан этот мод. Игроки, которые установят этот мод и правильно добавят javaagent в аргумент JVM, получат полный лог истории изменений определенной коллекции:

Применение
Сначала добавьте этот jar-файл в папку mods.
Во-вторых, отредактируйте аргумент виртуальной машины Java в вашем лаунчере. Добавьте `-javaagent:mods/CMESuckMyDuck-`. .jar= ; ; ; `.
Наконец, запустите игру, поиграйте и дождитесь сбоя.
Использование в других проектах на Java
Этот мод можно использовать не только для отладки Minecraft, но и для отладки других Java-проектов. Аналогично Minecraft. Единственное отличие заключается в том, что вам нужно добавить gson и asm.jar в classpath (`-cp`) перед javaagent, а также добавить наш `CMESuckMyDuck- Добавить `.jar` в classpath после javaagent.
аргументы
Полное имя класса, содержащего контейнер, который вы хотите отслеживать. Используйте `` вместо `.` (также известный как внутреннее имя класса).
Имя поля контейнера в целевом классе, который вы хотите отслеживать. Для Forge используйте имя SRG. Для Fabric используйте имя промежуточного контейнера. Для NeoForge используйте официальное имя.
В настоящее время мы поддерживаем только три контейнера: `List`, `Set`, `Map`. Этот аргумент указывает тип отслеживаемого контейнера.
`static` или `nonstatic`. Этот аргумент указывает, является ли контейнер статическим или нестатическим полем.
Как получить и
Начнём с примера.
Для начала взгляните на трассировку стека CME/IOOBE:

Во-вторых, изучите исходный код SoundEngine и определите, в каком контейнере возникла эта проблема:

Теперь мы знаем, что нам следует отслеживать карту `field_217942_m` (`instanceToChannel` в `SoundEngine`). Продолжайте.
В-третьих, установите этот мод и добавьте "`-javaagent:mods/CMESuckMyDuck-1.0.0.jar=net/minecraft/client/audio/SoundEngine;field_217942_m;Map;nonstatic`" в аргумент виртуальной машины Java. Перезапустите игру и дождитесь следующего сбоя.
Наконец, откройте файл `CMESuckMyDuck.log`, и вы увидите, какой поток и какой модификатор одновременно внесли изменения в контейнер.
Примеры (аргументы JVM)
Исключение ConcurrentModificationException от SoundEngine в среде Forge 1.16.5
`-javaagent:mods/CMESuckMyDuck-1.0.0.jar=net/minecraft/client/audio/SoundEngine;field_217942_m;Map;nonstatic`
Исключение ConcurrentModificationException при использовании PotionBrewing в среде Forge 1.20.1
`-javaagent:mods/CMESuckMyDuck-1.0.0.jar=net/minecraft/world/item/alchemy/PotionBrewing;f_43494_;List;static`
Исключение ArrayIndexOutOfBoundsException из Zeta mod
`-javaagent:CMESuckMyDuck-1.0.0.jar=org/violetmoon/zetaimplforge/event/ForgeZetaEventBus;convertedHandlers;Map;nonstatic`
Другие варианты
Уровень журнала
Используйте системное свойство `-Dcme_suck_my_duck.log_level=` ` для установки пользовательского уровня логирования.
Значение по умолчанию — 1, что означает отсутствие отладочных сообщений в журнале.
Пользователи могут установить значение 0 для вывода отладочных сообщений, что не рекомендуется — функции запросов, такие как Map#get, Set#containsAll, также будут записываться в лог при значении 0, что значительно увеличит размер файла.
Версия API ASM (v1.0.2+)
Для обеспечения совместимости с последней версией Minecraft этот мод скомпилирован с использованием ассемблерного кода версии 9.7. Для более старых версий Minecraft (например, 1.12.2) операции на уровне API, такие как ASM_9, не могут быть применены, поэтому игрокам необходимо использовать `-Dcme_suck_my_duck.asm_api_version=`. ` для изменения совместимости версий ASM API. Например, для версии игры 1.12.2: `-Dcme_suck_my_duck.asm_api_version=5`.
Максимальное количество записей в файле (v1.0.3+)
Поскольку мы заметили, что при сбое последние несколько операций в журнале гораздо важнее предыдущих, наш мод после версии 1.0.3 использует стратегию постраничной навигации, и в конце сохраняются только две последние страницы. Количество записей в журнале на каждой странице (то есть количество стеков вызовов операций) фиксировано, значение по умолчанию — 500. Игроки могут использовать `-Dcme_suck_my_duck.file_max_entries=` ` для изменения максимального количества элементов на странице.
Белый список конструкторов (v1.0.3+)
Иногда контейнер определенного класса используется во многих местах (например, CompoundTag#tags). Прямое использование этого модификатора приведет к слишком длинному логу или замене допустимого содержимого последующими операциями. Поэтому игроки могут указать дополнительный белый список конструкторов для отслеживания соответствующего контейнера в соответствующем классе конкретного модуля. По умолчанию он пуст, то есть белого списка нет. Игроки могут использовать `-Dcme_suck_my_duck.whitelist_constructor_stacktrace=` ` для его указания. Если какая-либо строка в трассировке стека, где создается контейнер, содержит содержимое строки белого списка, контейнер будет отслеживаться; в противном случае, контейнер не будет отслеживаться, что значительно упрощает информацию в логах.
Преобразование в потокобезопасный режим (v1.0.4+)
Используйте системное свойство `-Dcme_suck_my_duck.transform_to_thread_safe=true`, чтобы преобразовать поле в потокобезопасный контейнер.
Это НЕ рекомендуется, если только вам не нравится медленная работа и вы не хотите исправлять проблему.
Метод инъекции (v1.0.4+)
Используйте системное свойство `-Dcme_suck_my_duck.inject_method=true` для переключения в режим инъекции.
Если задано, следует использовать `-javaagent:CMESuckMyDuck-`. .jar= ; И всякий раз, когда вызывается этот метод, вы будете получать трассировку стека в лог-файлах.
Игнорировать потоки (v1.0.5+)
Используйте системное свойство `-Dcme_suck_my_duck.ignore_threads=` ; ; ;...` для игнорирования потока, который, как ожидается, будет изменять данный контейнер (или вызывать данный метод). Например, `-Dcme_suck_my_duck.ignore_threads="Server thread"`.
Прекратите запись логов на ранней стадии (v1.0.7+)
Используйте системное свойство `-Dcme_suck_my_duck.stop_logging_if_exception_created=false`, чтобы остановить запись логов в случае возникновения критической ошибки.
Программа обновления идентификаторов трассировки (v1.0.8+)
Используйте системное свойство `-Dcme_suck_my_duck.trace_id_updater=` ; ` для обновления идентификатора трассировки при вызове указанного метода. Это может быть полезно в режиме внедрения.
Мониторинг локальных переменных (версия 1.1.0+)
Используйте системное свойство `-Dcme_suck_my_duck.local_var_index=` ` для мониторинга локальной переменной по заданному индексу в данном методе.
Если задано, следует использовать `-javaagent:CMESuckMyDuck-`. .jar= ; ; Кстати, индекс может быть повторно использован различными локальными переменными. Поэтому вам, возможно, придётся использовать `-Dcme_suck_my_duck.match_local_index=`. ` для указания порядкового номера ASTORE операция, указывающая, какую локальную переменную следует отслеживать.
Заключение
Мне нравится есть утку по-пекински. Это так вкусно!
