PDA

Просмотр полной версии : необъяснимый стектрейс


undefined
14.09.2016, 14:49
из прода последнее время начал приходить такой стектрейс:
TypeError: Error #1009
at com.rhyboo.puzzles.game::Game2/onMU()
Пытаясь, найти на какой строке в методе он вылазит, расставил запись в лог после каждой строки.И какого же было мое удивление когда в логе пришло все что должно написаться в этом методе и после последнего лога этот эрор.Т.е. метод выполняется без ошибок, а на выходе из него вылазит пакость.Cобираю с sdk Flex 4.6.0,AIR 22.0

Bletraut
14.09.2016, 18:36
Чудес не бывает. Трейсить нужно тщательнее.

undefined
14.09.2016, 19:06
Чудес не бывает. Трейсить нужно тщательнее.
Тем не менее при возникновении стектрейса выполнение должно прерываться, а тут доходит до конца последняя строка в методе такая

LoggerBox.addLog("onMU log 6");

Так вот в логе приходит такое

...
onMU log 6
TypeError: Error #1009
at com.rhyboo.puzzles.game::Game2/onMU()

А насчет чудес советую почитать вот этот (http://www.flasher.ru/forum/showthread.php?t=210441&page=2&highlight=1502) топик.У меня до сих пор стоит условие чтоб эрор-репорт не слался от ошибки #1502 т.к. вылазит она достаточно часто в самых разных местах и никто не жалуется т.к. внешне эти ошибки никак себя не проявляют.

in4core
14.09.2016, 23:23
Ну и ? Так ошибка очевидна вообще то : *Так вот в логе приходит такое* - значит в вашем LoggerBox и летит все к чертям после трейса

undefined
14.09.2016, 23:41
Ну и ? Так ошибка очевидна вообще то : *Так вот в логе приходит такое* - значит в вашем LoggerBox и летит все к чертям после трейса

Причем тут логер бокс, если ошибка в методе onMU класса Game2?
Без ошибки логербокс вообщее спит и факт пришедшего репорта как раз говорит что отработал он на ура.

caseyryan
15.09.2016, 05:54
Возможны такие варианты:
1) Используется сторонняя либа, в которой зашит блок try / catch, а в нем с блоке catch сделано так:

trace(error.getStackTrace());
или так
trace(error.message);

2) Используются инлайны, и все по факту летит вообще в другом месте. За этим механизмом замечено, что полностью отваливаются брейкпоинты и выбросы ошибок происходят в другом месте (в смысле не на той строке, где указано. Ну, по факту конечно на той, в скомпилированном коде, но в сорсе это другая строка).

А вообще, покажи ка лучше свой метод. А то тут пальцем в небо гадания

undefined
15.09.2016, 09:17
А вообще, покажи ка лучше свой метод. А то тут пальцем в небо гадания

private function onMU(e:MouseEvent):void {
if (e.target is VScroller) return;
if (tray)
addChild(tray);
if (!sel_group || tween) return;
var i:int;
for (i = 0; i < sel_group.length; i++) {
if (groupCont.contains(sel_group[i]))
groupCont.removeChild(sel_group[i]);
if (sel_group[i])
pieceCont.addChild(sel_group[i]);
}
if (Main.special_users.indexOf("fb_" + fbm.fbId) != -1) {
writeToDebugPanel("left mouse up. group size:"+sel_group.length);
LoggerBox.addLog("group released " + sel_group.length);
groupCont.graphics.clear();
}
if (sel_group.length == 1) (sel_group[0] as PieceDO).stopBlink();
if (blinkingPiece>=0 && singlePieces[blinkingPiece]==sel_group[0])
blinkingPiece = -1;
var n_snd:uint = Math.floor(Math.random() * 3) + 1;
SoundManager.playSound("piece_put" + n_snd + "_snd");
stateChanged = true;
//alignConnected(sel_group[0]);
//ExternalInterface.call("console.log('before connect:')");
//showStats();
var p:Point;
if (sel_group.length>1) {
for (i = 0; i < sel_group.length; i++) {
alignToGrid(sel_group[i] as PieceDO,false);
}
alignToGrid(sel_group[0] as PieceDO, true);
} else {
alignToGrid(sel_group[0] as PieceDO, true);
}
for (i = 0; i < sel_group.length; i++) {
connect(sel_group[i]);
}
//ExternalInterface.call("console.log('after connect:')");
//showStats();
var n:int = sel_group.length;
var firstPiece:PieceDO = sel_group[0] as PieceDO;
var old_group:Array = sel_group;
sel_group = getConnected(firstPiece);
if (sel_group.length>old_group.length) {
// mark pieces
for (i = 0; i < sel_group.length; i++)
sel_group[i].checked = false;
// mark dropped pieces
for (i = 0; i < old_group.length; i++)
sel_group[i].checked = true;
//alignConnected(firstPiece);
// allign newly connected pieces to already connected part
for (i = 0; i < sel_group.length; i++) {
if (!sel_group[i].checked) {
alignConnected(sel_group[i]);
break;
}
}
} else {
alignConnected(firstPiece);
}
old_group = null;
findSinglePieces();
dispatchEvent(new AppEvent("search_panel_state_changed", getSearchPanelState()));
var ev:AppEvent;
fromTray = false;
//dispatchEvent(new AppEvent("piece released"));
if (n != sel_group.length) {
ev = new AppEvent("connected");
dispatchEvent(ev);
SoundManager.playSound("merge_snd",false);
lastMoveData = null;
}
LoggerBox.addLog("onMU log 1:"+n+","+sel_group.length+","+blink);
if (n != sel_group.length && blink) {
for (i = 0; i < sel_group.length; i++) {
if (sel_group[i].parent != pieceCont) {
sel_group[i].parent.removeChild(sel_group[i]);
pieceCont.addChild(sel_group[i]);
}
}
BlinkManager.blink(sel_group);
}
LoggerBox.addLog("onMU log 2,event:" + e+",this:" + this + ",sel_group:" + sel_group.length);
if (e)
LoggerBox.addLog("onMU log 3,target==stage?:" +(e.currentTarget==stage)+",n*m:"+(this.n * this.m)+"skipCompleted:"+skipCompleted);
if (sel_group.length == (this.n * this.m) && this.mouseEnabled && e.currentTarget != stage && !skipCompleted) {
LoggerBox.addLog("last piece connected");
ev = new AppEvent("gameover");
dispatchEvent(ev);
//e.stopImmediatePropagation();
skipCompleted = true;
if (zoomTween) zoomTween.fforward();
zoomParams = { startZoom:zoom, endZoom:field_scale};
scaleFactor = 1;
zoom = field_scale;
zoomTween=new Tween(null,"",None.easeNone,0,1,0.3,true);
zoomTween.addEventListener(TweenEvent.MOTION_CHANGE,zoomAnim);
zoomTween.start();
}
LoggerBox.addLog("onMU log 4");
sel_group = null;
LoggerBox.addLog("onMU log 5");
this.removeEventListener(Event.ENTER_FRAME, onEF);
LoggerBox.addLog("onMU log 6");
}

Ни инлайнов, ни сторонних либ тут нет

wvxvw
15.09.2016, 09:39
Очень сложная функция (высокая цикломатическая сложность, т.е. много блоков и переходов между ними). Компилятор мог ошибочно сгенерировать код в котором неправильно указал исходную позицию в коде. Нужно смотреть дизассемблер этой функции. А еще лучше - разбить функцию на более короткие функции.

caseyryan
15.09.2016, 10:35
if (groupCont.contains(sel_group[i]))
groupCont.removeChild(sel_group[i]);
if (sel_group[i])
pieceCont.addChild(sel_group[i]);
Очень плохая привычка. У тебя тут отступ, как-будто второй if вложен в первый, а по факту это два независимых блока. Но это вообще относится ко всему коду. Тут простор для багов просто огромный.
Кстати, проверка contains тоже не самый лучший вариант. Лучше проверяй поле parent.
И еще, за массивами замечено, то если удаляешь что-то из них в цикле, то это что-то может не удалиться сразу. После этого добавив что-то, ты задублируешь ссылку, а потом одна из них удалится. Какая именно не известно.

А вот это что за фигня?

for (i = 0; i < sel_group.length; i++)
sel_group[i].checked = false;
// mark dropped pieces
for (i = 0; i < old_group.length; i++)
sel_group[i].checked = true;

Во втором цикле ты задаешь checked = true объекту, находящемуся в sel_group, а перебираешь old_group.
Это так задумано что ли? Или это опечатка? Уверен, что тут может падать) Вряд ли sel_group всегда совпадает с old_group до последнего индекса

И еще. Что ты пытаешься здесь сделать? Зачем там проверка if (sel_group[i]) ?
То есть ты пытаешься сначала этот объект откудато удалить (хотя по твоей же логике он может быть null) и только после этого проверяешь а не null ли он?
И зачем его удалть откуда-то, а потом снова добавлять? Метод addChild уже и так подразумевает удаление из любого другого контейнера

for (i = 0; i < sel_group.length; i++) {
if (groupCont.contains(sel_group[i]))
groupCont.removeChild(sel_group[i]);
if (sel_group[i])
pieceCont.addChild(sel_group[i]);
}

Tails
15.09.2016, 10:59
undefined,
Была похожая проблема когда то, в чём причина так и не понял, да и не искал прям сильно, так как на работу приложения это не влияло. Выглядело так, словно любой рандомный блок кода генерировал исключение в любом месте выполнения программы. Это были и просто обработчики гуи, и циклы и загрузчики, при этом блок кода не прерывался и выполнялся как положено. Компилировалось это тогда старым sdk, который не поддерживал инлайны.

undefined
15.09.2016, 13:31
А вот это что за фигня?
Там смысл такой:
Юзер тащит кусок пазла из n кусочков (массив sel_group)
этот массив кешируется в old_group
Дальше после отпускания группы, она может слипнуться с другой группой кусков(т.е. в sel_group добавятся еще куски из присоединенной группы) и код

for (i = 0; i < sel_group.length; i++)
sel_group[i].checked = false;
// mark dropped pieces
for (i = 0; i < old_group.length; i++)
sel_group[i].checked = true;

сначала выставляет false всем кускам новой объединенной группе и потом у той группы, что юзер отпустил выставляется true.Т.е. false остается у кусков, которые присоединились к группе после отпускания мыша.Дальше эти свежеприсоединенные куски выравниваются относительно отпущенной группы.Я знаю это достаточно костыльно, но зато не надо лишний раз дергать рекурсивную функцию getConnected
И зачем его удалть откуда-то, а потом снова добавлять? Метод addChild уже и так подразумевает удаление из любого другого контейнера
А потому что были замечены артефакты в виде непонятных черных прямоугольников на месте откуда DO по идеи должен был удалиться бы.Ручное удаление исправило это.Лишнее усливие надо удалить.
Вообщем еще раз повторяю этот код выполняется от начала и до конца т.к. последняя строка
LoggerBox.addLog("onMU log 6");
Не зависит ни от каких условий и исполняется, значит и все что выше тоже исполняется без косяков.
Выглядело так, словно любой рандомный блок кода генерировал исключение в любом месте выполнения программы.
А тут как раз всегда один и тот же метод и один и тот же эрор.Вообщем хз,видимо придется смириться.

maxkar
15.09.2016, 23:52
Да нет тут ничего загадочного. Ошибка в первой же строке функции, когда e===null. Скорее всего, метод где-то явно вызывается (может даже и в кадре) передавая null. Хотя и через всякие onMU.bind(null, [null]) в качестве слушателя тоже можно добиться подобного.

Вывод
Т.е. метод выполняется без ошибок, а на выходе из него вылазит пакость.
неверен. Аккуратность расстановки логирования не позволяет его сделать. Первая запись в лог - в середине метода. До него уже много чего может сломаться. За факт многократного вызова метода - его название (on*) и ваше дальнейшее описание (обработчик событий). Пакость происходит не "на выходе из него", а на "в процессе выполнения следующего вызова метода".

В пользу версии говорит и улика в виде загадочного

if (e)
LoggerBox.addLog(some.long.message);

Загадочный if потому, что в этой строчке e уже точно не null. Ведь в начале метода идет доступ к e.target. И этот доступ падает с ошибкой в слуае e===null (что мы собственно и наблюдаем в рассматриваемой ситуации). Зато улика говорит нам, что раз есть проверка, значит автор кода явно подозревал наличие вызова onMU(null), а может, и сам написал этот вызов.

Всё сходится. Где-то есть вызов onMU(null), который и дает ошибку в логе. Лог успешного выполнения ни при чем и демонстрирует успешное выполнение функции при e!==null.

undefined
16.09.2016, 01:49
Пакость происходит не "на выходе из него", а на "в процессе выполнения следующего вызова метода".

Согласен, это объясняет лог,вот только я прошелся поиском по всему классу единственное,где встречается onMU - подписка/отписка на маусивенты,наружу ссылка на метод тоже не передается. Проверку if(e) была поставлена на автомате,не глядя на сигнатуру метода, чтоб точно знать что запись в лог не сломает все(это же все в проде).Такое ощущение, что где-то внутрях avm между созданием ивента и передачей его в обработчик, ивент грохает мусорщик.

Хотя и через всякие onMU.bind(null, [null]) в качестве слушателя тоже можно добиться подобного.

А можно по подробнее что это за конструкция и что она делает?

caseyryan
16.09.2016, 13:57
Что будет если вызов этого метода обернуть в try / catch?
Если поймает исключение, можно будет посмотреть весь стек вызовов и проверить каждый объект null.

Второй вариант - расставь по всему методу, на каждой строчке брейкпоинты и отследи вызов каждой строчки до того момента, как вылетит этот баг.
Вообще, не знаю как получилось, что ты так долго в одном методе ищешь ошибку. Есть же отладчики / профайлеры.
Я бы понял, если бы баг происходил где-то в непонятном месте во всем приложении. Но тут он локализован до одного метода. Поиск сводится к простым "брякам"

undefined
16.09.2016, 14:02
Что будет если вызов этого метода обернуть в try / catch?
В том то и дело что вызова метода нет
единственное,где встречается onMU - подписка/отписка на маусивенты,наружу ссылка на метод тоже не передается
Вообще, не знаю как получилось, что ты так долго в одном методе ищешь ошибку
из прода последнее время начал приходить такой стектрейс:
У меня то это не вылазит никогда

maxkar
21.09.2016, 22:30
Согласен, это объясняет лог,вот только я прошелся поиском по всему классу единственное,где встречается onMU - подписка/отписка на маусивенты,наружу ссылка на метод тоже не передается.
Значит я не угадал. Но это не отменяет факта, что у вас логирование с середины метода начинается. Там и промежуточные методы вызвыаются (теоретически могут вернуть null). И к каким-то глобальным переменным доступ идет. Начните логирование с самой первой строчки метода, мало ли что выяснится. Ну или хотя бы подтвердится, что метод ниоткуда не вызывается, а ошибка выводится.

А можно по подробнее что это за конструкция и что она делает?
Если я с синтаксисом не напутал, то конструкция называется вызов метода.Она вызывает метод. В аргументах там передается литерал массива (array literal), он создает массив :). Семантика метода bind - частичное применение функции (partial application), в данном случае применение к одному аргументу null. Данный вызов bind вернет функцию, которая при вызове будет вызывать onMU(null).

undefined
21.09.2016, 22:37
Там и промежуточные методы вызвыаются
ну тогда и стектрейс в этих методах был бы,а судя по логу, глубина стектрейса =1

Добавлено через 7 минут
хотя да,надо добавить логов

caseyryan
22.09.2016, 06:01
Кстати, вот только что пришло в голову. У меня как-то раз, по неопытности, вылетал почти такой же странный баг, который я долго не мог отловить. Так же вылетало обращение к свойству null, хотя проверки показывали, что в методе все ок.
Как потом оказалось, дело было в том, что ошибка вылетала в экземпляре, который уже был удален, но не достаточно хорошо зачищен от ссылок. Он продолжал существовать в памяти и работать. Но так как часть ссылок там была null, это и вызывало сбои.
Что у тебя это за класс? Уж не создается ли он случаем при каждом новом уровне?
И если создается, как ты его зачищаешь от ссылок?

undefined
22.09.2016, 14:05
Что у тебя это за класс? Уж не создается ли он случаем при каждом новом уровне?
Игровой класс, создается на каждой новой игре.Сейчас проверил - отписки все стоят.Ладно посмотрим что новые логи покажут.

undefined
25.09.2016, 20:52
действительно, стектрейс вылетает где-то до первой записи в лог.Всем спасибо, пойду копать.

caseyryan
26.09.2016, 07:21
действительно, стектрейс вылетает где-то до первой записи в лог.Всем спасибо, пойду копать.

100% уверен, что у тебя там не все зачищается