Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2015.09.10;
Скачать: CL | DM;

Вниз

Чудеса   Найти похожие ветки 

 
Юрий Зотов ©   (2014-11-01 09:40) [0]

Убил 2 дня на вылавливание такого глюка

Java. В одном из методов одного из классов есть такой кусок кода с тремя локальными переменными:

boolean var1 = Выражение_1;
boolean var2 = Выражение_2;
boolean var3 = Выражение_3;
if (var1 && var2 &&var3)
 что-то делаем;

Этот код работает и прекрасно делает то, что от него нужно. Причем var1 и var2 в программе живут давно и успешно, а вот var3 появилась недавно - после чего вдруг перестал работать совершенно другой функционал, с этим кодом никак не связанный. Ошибок не выдает, но и не работает как надо.

Поскольку вместе с var3 в программу было внесено еще немало изменений,  отлов плюхи был совсем неочевидным и стоил 3 дня нервов. И встретилось вот такое чудо. Комментирую кусок кода:

boolean var1 = Выражение_1;
boolean var2 = Выражение_2;
boolean var3 = Выражение_3;
if (var1 && var2) // Комментарий &&var3)
 что-то делаем;

То есть, переменная var3 из if исключена и нигде не читается (о чем Eclipse честно предупреждает). А отвалившийся в другом месте функционал при этом вдруг восстановился!!!!

В изумлении чешу репу, но объяснить это чудо не могу. Тогда делаю фантастическое предположение, что, поскольку var3 стала ненужной, то java выкинула и ее вычисление вместе с Выражением3. Дело в том, что в Выражении3 вызываются методы, которые действительно могут повлиять на другие места программы - и если это выражение было выкинуто (хотя выкидывать вызовы функций нельзя никакому оптимизатору), то вот такое чудо и произошло.

Проверяю это фантастическое предположение следующим образом - делаю переменную var3 снова используемой:

boolean var1 = Выражение_1;
boolean var2 = Выражение_2;
boolean var3 = Выражение_3;
if (var1 && var2) // Комментарий &&var3)
 что-то делаем;
System.out.println(var3);

Теперь Выражение3 снова вычисляется - то есть искомый функционал снова должен отвалиться. А он работает!!!

В общем, совершенно четко установлено, что если var3 участвует в if, то глюк есть, а если не участвует, то глюка нет. При этом неважно, вычисляется Выражение3, или нет.

Чудеса?


 
Rouse_ ©   (2014-11-01 11:08) [1]

Ха, у нас глюк был, который впрочем в последствии оказался глюком отладчика, без отладки все работало норм, так вот, представь:
Есть модуль, разрабатываемый и работающий годами, но в какой- то момент этот модуль стало нельзя комментировать, т.е. Добавляем любой коментарий в любом месте модуля и при отладке происходит крах приложения. Чудеса? :)))


 
Dimka Maslov ©   (2014-11-01 11:10) [2]

В дельфи то же такие глюки были. Я даже для написал процедурку

procedure Hole(var V);
asm
end;

Чтобы компилятор с оптимизатором думали, что переменная дальше где-то ещё используется и не выкидывали её.


 
DVM ©   (2014-11-01 11:36) [3]


> Юрий Зотов ©   (01.11.14 09:40) 


> Дело в том, что в Выражении3 вызываются методы, которые
> действительно могут повлиять на другие места программы


> Чудеса?

Почему же чудеса? Это косяк программиста. Функция (а ведь там функция раз она используется в выражении), которая кроме выполнения своей основной задачи (возвращения значения) меняет явно или неявно еще какие то внешние по отношению к ней переменные (в том числе поля класса) - это уже повод задуматься над ее рефакторингом.


 
Юрий Зотов ©   (2014-11-01 12:14) [4]

> DVM ©   (01.11.14 11:36) [3]

О побочных эффектах я где-то когда-то тоже слышал. Лет этак 40 назад.
:o)

Но там далеко не все так просто, как показано в примере. Дело в том, что функция, участвующая в Выражении3, может влиять на программу неявно, через сервер приложений. И избежать этого нельзя - так устроен сервер. Ну да и ладно, с этим мы уж как-нибудь разберемся.

А чудо совсем не в этом, а вот в чем.

Этот код дает глюк:
boolean var1 = Выражение_1;
boolean var2 = Выражение_2;
boolean var3 = Выражение_3;
if (var1 && var2 && var3)
что-то делаем;


Комментируем кусок IF"а, не трогая Выражения3. Глюк вдруг исчез:
boolean var1 = Выражение_1;
boolean var2 = Выражение_2;
boolean var3 = Выражение_3;
if (var1 && var2) // Комментарий && var3)
что-то делаем;


Предполагаем, что Выражение3 было выброшено. Делаем так, чтобы оно уж точно вычислялось. Глюка все равно нет:
boolean var1 = Выражение_1;
boolean var2 = Выражение_2;
boolean var3 = Выражение_3;
if (var1 && var2) // Комментарий && var3)
что-то делаем;
System.out.println(var3);


Получается следующее: независимо от Выражения3 глюк возникает при
if (var1 && var2 && var3)
и глюк исчезает при
if (var1 && var2) // Комментарий && var3)

А вот это уже чудо. Поскольку var3 - всего лишь локальная переменная.


 
DVM ©   (2014-11-01 12:46) [5]


> Юрий Зотов ©   (01.11.14 12:14) [4]

А в Java нет такой штуки вроде отложенных вычислений, т.е код вычисляется не сразу а по мере необходимости в нем? Т.е выражение 1 и выражение 2 вычисляются не в момент присваивания, а в момент чтения значений переменных в условии? Это могло бы объяснить странное поведение, если они зависят от выражения 3 косвенно.


 
Inovet ©   (2014-11-01 14:39) [6]

1. Последовательность вызовов функций в Выражение_1, Выражение_3, Выражение_3 может влиять на результат?

2. Если вместо
что-то делаем;
поставить
делаем что-то нейтральное;


 
Ellisium ©   (2014-11-02 18:21) [7]

Дядь Юр, в данном примере, как мне кажется, у вас порочная логика.

С одной стороны, вы намекаете, что глюк связан с вычислением переменной var3. С другой стороны, когда вы делаете вывод:

> System.out.println(var3);

то глюка по прежнему нету. Но ведь выражение вычислено, вы результат видите в консоли (или где там).
Отсюда делаем вывод, что вычисление результата var3 не влияет на глюк.

Таким образом все сводится к тому, что у вас программа глючит, а если что-то поменять в коде - то не глючит. И связи не прослеживается.

Откуда уверенность, что это глюк java - непонятно.


 
Юрий Зотов ©   (2014-11-02 19:16) [8]

> Ellisium ©   (02.11.14 18:21) [7]
> Отсюда делаем вывод, что вычисление результата var3 не влияет
> на глюк.


Я сделал такой же вывод (о чем уже говорил).

> Откуда уверенность, что это глюк java - непонятно.

Во-первых, я нигде не утверждал, что это глюк именно Джавы. Вообще, Джаву упомянул лишь в самом начале и лишь для конкретики.

Во-вторых, поскольку var1, var2 и var3 - локальные булевские переменные, вот это ошибкой программиста быть не может никак (причем на любом языке и независимо ни от чего):
if (var1 && var2) - работает без глюков.
if (var1 && var2 && var3) - работает с глюком.

PS
Просьба читать внимательнее. Очень не хочется пояснять сабж в четвертый раз.


 
sniknik ©   (2014-11-02 23:28) [9]

> А в Java нет такой штуки вроде отложенных вычислений
+
> 1. Последовательность вызовов функций в Выражение_1, Выражение_3, Выражение_3 может влиять на результат?
=
???

отложенное вычисление (/выкинуты оптимизатором переменные), вызов функции на месте сравнения, причем, сравнение идет с конца... т.к.
boolean var1 = Выражение_1;
boolean var2 = Выражение_2;
boolean var3 = Выражение_3;
if (var1 && var2) // Комментарий && var3)
что-то делаем;
System.out.println(var3);

глюка нет.

если так, по логике, то -  
boolean var1 = Выражение_1;
boolean var2 = Выражение_2;
boolean var3 = Выражение_3;
System.out.println(var3);
if (var1 && var2) // Комментарий && var3)

должен быть глюк.


 
Плохиш ©   (2014-11-03 00:14) [10]

Подозреваю, что "глюк" в выполнении и не выполнении куска "что-то делаем;"


 
Юрий Зотов ©   (2014-11-03 00:46) [11]

> Плохиш ©   (03.11.14 00:14) [10]

"Что-то делаем" - это заведомо существующий объект добавляется в заведомо существующий список. Проверено.

Да и не в этом дело. Когда не было переменной var3, все отлично работало. Потом ввели var3, которая, ясное дело, может быть false или true. То есть,  объект либо в список добавляется, либо нет - точно так  же как было и раньше. Но вдруг появился глюк.


 
Германн ©   (2014-11-03 02:28) [12]

Юра.
Код
> boolean var1 = Выражение_1;
> boolean var2 = Выражение_2;
> boolean var3 = Выражение_3;
> if (var1 && var2 &&var3)
>  что-то делаем;

и код
> boolean var1 = Выражение_1;
> boolean var2 = Выражение_2;
> boolean var3 = Выражение_3;
> if (var1 && var2) // Комментарий && var3)
> что-то делаем;
> System.out.println(var3);
>

отличаются имхо, двумя обстоятельствами.
Первое - то что в булевом выражении участвует разное количество переменных. (Но ты уже сказал, что ни в каком языке такие глюки недопустимы. Точнее никакие компиляторы такое не должны создавать).
Второе - то что в первом варианте переменная var3 используется до "что-то делаем", а во втором случае после.
Не знаю язву (слава богу, которого нет) и не знаком поэтому с компиляторами её. Так что присоединяюсь к

> DVM ©   (01.11.14 12:46) [5]
>
>
> > Юрий Зотов ©   (01.11.14 12:14) [4]
>
> А в Java нет такой штуки вроде отложенных вычислений, т.
> е код вычисляется не сразу а по мере необходимости в нем?
>

Только разумеется не в язве дело, а в её компиляторе.
Что получится, если строку System.out.println(var3);
перенести выше по коду? Т.е. до "что-то делаем".


 
sniknik ©   (2014-11-03 02:48) [13]

> Потом ввели var3, которая, ясное дело, может быть false или true.
если верно про отложенные/оптимизатор, то var3 в "объективной реальности" нету, нужно рассматривать код вида -
if (Выражение_1 && Выражение_2 && Выражение_1) и т.д.


 
Inovet ©   (2014-11-03 02:52) [14]

Всё к тому же - к последовательности вычислений. Тем более что точно известно - Выражение_3 влияет на состояние, причём хитро и, видимо, неочевидно.

Так есть глюк?
if (var3 && var2 && var1)


 
Inovet ©   (2014-11-03 02:55) [15]

> [14] Inovet ©   (03.11.14 02:52)

И ещё дальше.

А так?
if (var1 & var2 & var3)

Это если в джаве такое имеет смысл.


 
Inovet ©   (2014-11-03 02:56) [16]

> [15] Inovet ©   (03.11.14 02:55)

Было вот к этому
if (var1 && var2) // Комментарий &&var3)
что-то делаем;
System.out.println(var3);


 
Юрий Зотов ©   (2014-11-07 09:56) [17]

И что вы думаете?

Переписал Выражение_3, сохранив его логику. Примерно что-то вроде этого:
not ((not B) or (not A)) вместо нормального A and B.

И заработало. No comments.


 
Германн ©   (2014-11-08 01:44) [18]


> Юрий Зотов ©   (07.11.14 09:56) [17]
>
> И что вы думаете?
>
> Переписал Выражение_3, сохранив его логику. Примерно что-
> то вроде этого:
> not ((not B) or (not A)) вместо нормального A and B.
>
> И заработало. No comments.
>

Странно. Похоже на баг компилятора. И я с таким сталкивался. Но то действительно был недоделанный, но (имхо) единственный и в мире и во все времена компилятор Паскаля для MC51. Но что-то мне сомнительно, что и ты, Юр используешь столь же сырой компилятор. Ведь такой баг в достаточно широко используемом компиляторе был бы сразу обнаружен и исправлен.
Но раз заработало, то ...
Действительно No comments.


 
Юрий Зотов ©   (2014-11-08 09:23) [19]

> Германн ©   (08.11.14 01:44) [18]

Я и сам почти в шоке. Но делать серьезные выводы все же воздерживаюсь, потому что в процессе борьбы с багом в код были внесены еще и другие изменения. Например, были вынесены из цикла (а показанный выше код исполнялся в цикле) вычисления инвариантов, участвующих в выражениях 1-3. Конечно, инварианты должны были повлиять на скорость, но не на баг - но в итоге я не могу с уверенностью локализовать именно то мероприятие, после которого баг исчез.


 
virex(home) ©   (2014-11-08 09:31) [20]

>Юрий Зотов ©   (01.11.14 09:40) [0]

у меня еще чудесатее было: проект полностью построен как com+ приложение  (клиентская и серверная часть), этакий толстый клиент, где серверная часть работает с бд mssql через ado

последнее время полезли ошибки (точно не вспомню, но со словом influense) притом без какой либо закономерности, и единственное временное решение - перезапуск сервера com+ приложений (а это надо выгонять всех юзеров)

в интернетах написано что глюк в bde (надо увеличить размер буфера), TClientDataSet (глючная midas.dll)

притом бде мы не используем

теперь внимание! убийца - дворецкий.
оказалось, что во все модули клиентской части (еще при сталине) прокрались визуальные компоненты-скины, и модули которые достаточно давно не менялись внезапно стали глючить через несколько лет. глюк - изза криво написанного компонента  (скин формы) очень голодного на память


 
virex(home) ©   (2014-11-08 09:34) [21]

причем тот же клиент запущеный через citrix (ОЗУ 1Гб для сессии) никаких ошибок не выдавал


 
kaif ©   (2014-11-08 13:42) [22]

Этот код многопоточный или однопоточный?


 
Юрий Зотов ©   (2014-11-08 14:49) [23]

> kaif ©   (08.11.14 13:42) [22]

Вот это, в числе прочего, сейчас и выясняем. Сам по себе, код потоков не создает, но их может создавать сервер приложений. И хочется понять, что же это за "чудо" такое было и как его не допускать далее.


 
Германн ©   (2014-11-09 02:43) [24]

<offtop>
Чудеса, да и только.

> kaif ©   (08.11.14 13:42) [22]

Ашот, это действительно ты? Какими судьбами ты вновь тут? И где ты прятался всё это время?
:(
</offtop>


 
Дмитрий С ©   (2014-11-10 09:51) [25]

Спасибо за опыт, теперь буду осторожен!



Страницы: 1 вся ветка

Текущий архив: 2015.09.10;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.112 c
2-1394229671
alexdn
2014-03-08 02:01
2015.09.10
Мигает картинка


2-1394221183
leshka
2014-03-07 23:39
2015.09.10
сохранение файл dbf


15-1416776457
ВладОшин
2014-11-24 00:00
2015.09.10
Шахматы, задачка


15-1420742668
Kerk
2015-01-08 21:44
2015.09.10
О безопасности программ на Delphi


15-1411763403
Юрий
2014-09-27 00:30
2015.09.10
С днем рождения ! 27 сентября 2014 суббота