[問題] 有沒有人碰過所謂的靈異現象

作者: HuangJC (吹笛牧童)   2016-06-21 12:58:02
抱歉,"靈異現象"一詞是敝公司內部在用的
主要是講各種 執行不如預期
通常,有很大機率,會被人說"那是你自己寫程式不小心"
比如,程式在不該當的地方當了
但怎麼查也查不到原因
其實原因是更早之前使用了不存在或已刪除的變數
(這狀況在 java 還沒碰過;我是舉 C 的例子)
因為無效指標會讓程式流程跑到亂碼
也可能破壞堆疊;而且不一定"馬上"當
不過這次的例子比較奇怪
主管已經追一個禮拜了
(幸好不是發生在我身上,不然一定罵死我又不相信我
發生在他身上,他則說"解決了有賞"XD)
public void run() {
long __lCurrentTime = 1466154837;
long __lTimeout = __lCurrentTime + 6;
boolean __result = false;
// 中斷點1:請在下一行放置一個中斷點
int __count = 0;
for ( ; __lCurrentTime<=__lTimeout ; __lCurrentTime++) {
__result = __lCurrentTime<=__lTimeout;
__count++;
}
// 中斷點2:請在下一行放置一個中斷點
}
這次的例子是上面的小程式
中斷點 2 永遠執行不到
而檢查 __result, 也竟然永遠為 true
自己直接把程式放入自己的小專案當然沒問題
我們這個是放在敝公司的專案中
而奇怪的是,它還挑 build machine
這段 code 要在特定機器上 build 出 APK
才會執行不完
其他機器去 build, 則不會執行出問題
(而我們信任自己的 build machine 啊,那是裝好後只用來 build 程式的一台專用機器)
重灌 build machine 嗎?也不對,因為不只一台會出問題
(會出問題的就一直 build 出問題)
程式的邏輯很簡單(雖然不實用;因為它是專為了重製問題,簡化出來的 code)
同事因此做了個猜測:堆疊炸了
如果堆疊炸了,那當然程式就不必談邏輯了
但這段程式是位在另一個 thread, 同事加大 thread stack
new Thread(null, null, TAG, 2*1024*1024)
沒有用
主管現在用另一個方法迴避問題
把原來的 long, 故意 cast 成 double
這樣是很無聊啦,但 compare 結果會正確了!!
(如果是堆疊炸了,應該要減少使用自動變數才對
奇奇怪怪的迴避方法未必能解決問題吧!)
同事說,這種奇怪的問題,未必能用以前 C/C++ 時的邏輯去猜想
可能要更深入 byte code 去推斷
請問有沒有人解過類似的問題
(這不像 java 語法問題,而是 java 環境使用上的問題)
謝謝
作者: KekeMonster (KekeMonster)   2016-06-21 18:18:00
是我木眼嗎,result 本來就該是 true,false 才是靈異現象吧?
作者: qrtt1 (有些事,有時候。。。)   2016-06-21 19:08:00
不要把找不出 root cause 當靈異現象啊 @o@
作者: KekeMonster (KekeMonster)   2016-06-22 00:21:00
result 已經在 loop 裡被更新 6 次 true 了怎麽會是false
作者: dennisxkimo (Dennis(一上B就糟糕))   2016-06-22 11:44:00
我怎麼看都不覺得結果是flase
作者: HuangJC (吹笛牧童)   2016-06-22 11:58:00
不會是false,但也不該被執行到我們是因為奇怪它為什麼被執行 第七次以上大家還是在正常邏輯裡打轉,那不可能看懂我這篇啊...
作者: dennisxkimo (Dennis(一上B就糟糕))   2016-06-22 12:06:00
我看起來是0> 1> 2> 3> 4> 5> 6 七次
作者: KekeMonster (KekeMonster)   2016-06-22 12:09:00
哈 對是 7 次我覺得你為了呈現現象,把範例程式碼過度簡化到不會有錯了吧推 qrtt1 大所說的,我覺得比較像是邏輯性的錯誤造成無窮迴圈,找出 root cause 吧
作者: bitlife (BIT一生)   2016-06-22 12:30:00
java method的max code size是有64KB的上限,但compiler多半會警告,4000行過了沒有難以確定,但寫這麼長確定不太好除錯及維護
作者: HuangJC (吹笛牧童)   2016-06-22 13:05:00
那我再說一次,不是跑了七次,是 endless,大家還是覺得邏輯性錯誤?當跑了二十次,我們就懷疑其布林值,這時還為true就怪了程式有簡化,但簡化的版本足以重製問題…我不是在表達邏輯而已,而是就這樣的code就會有endless loop
作者: dennisxkimo (Dennis(一上B就糟糕))   2016-06-22 14:08:00
"不結束,__result永遠為true",for loop 無關resut吧
作者: ssccg (23)   2016-06-22 16:00:00
有錯和沒錯的版本bytecode長一樣? 以你的說法看起來跟build環境有關,跟執行環境沒關?那去研究stack memory好像方向錯誤
作者: HuangJC (吹笛牧童)   2016-06-22 16:17:00
For loop 是否結束,決定於那個布林算式,那是和result一模一樣的算式
作者: bitlife (BIT一生)   2016-06-22 16:37:00
作業系統,CPU,JDK版本等都一模一樣嗎? 這當然有可能是遇compiler本身的錯誤
作者: ssccg (23)   2016-06-22 16:45:00
所以bytecode到底一不一樣...
作者: HuangJC (吹笛牧童)   2016-06-22 16:45:00
bytecode 是指 build 出來的結果嗎?不一樣
作者: HuangJC (吹笛牧童)   2016-06-22 16:46:00
android compiler 可以做到一模一樣嗎?我試試
作者: ssccg (23)   2016-06-22 16:50:00
android build過程有幾個step,至少先看一下是從.class就不一樣,還是dex不一樣,還是哪邊...才知道問題在哪步吧
作者: HuangJC (吹笛牧童)   2016-06-22 16:53:00
我自己的電腦 build 兩次,就不會一模一樣了所以這種 compiler 是無法這樣驗證的
作者: kiwatami (悠游自在)   2016-06-22 16:58:00
我覺得問題不在機器 而是程式裡面有不夠嚴謹的判斷而機器的差異導致這個 bug 產生有沒有試著在迴圈內 print 相關的值?看看值的變化在兩台機器上有什麼不同?並且確定每次值的異動是不是都有執行到感覺你的判斷式跟時間有關 如果加上 thread.sleep有沒有可能導致結果不如預期?例如時間差太大跑進了其他判斷式 導致 r 或 c 沒被更新然後這個迴圈本來就會執行七次 r 也會是 true如果 r 不是 true 就根本進不去迴圈了你仔細看 迴圈的條件跟你的 r 值條件是一樣的
作者: HuangJC (吹笛牧童)   2016-06-22 17:07:00
為什麼和時間有關?又沒有 time api..
作者: ssccg (23)   2016-06-22 17:09:00
最後的apk當然會不一樣,但是.class和.dex我試過同電腦相同
作者: dennisxkimo (Dennis(一上B就糟糕))   2016-06-22 17:11:00
for結束跳出後 {}內result還是true很正常
作者: HuangJC (吹笛牧童)   2016-06-22 17:11:00
... 謝謝大家,因為問題已解,所以我也重製不了了...
作者: dennisxkimo (Dennis(一上B就糟糕))   2016-06-22 17:12:00
__result不能拿來判斷迴圈結束條件的狀態
作者: HuangJC (吹笛牧童)   2016-06-22 17:14:00
但如果迴圈死不結束,它就有意義了
作者: dennisxkimo (Dennis(一上B就糟糕))   2016-06-22 17:19:00
死不結束 要檢查你結束條件設的變數 不是resultfor條件內的A<=B 跟{}內的C=A<=B的C不同
作者: HuangJC (吹笛牧童)   2016-06-22 17:28:00
還有一點,因為"一定要特定機器 build 才會出錯"所以,無法在自己機器上步進執行..只能不斷的設計 log;所有變數,我們都監看了當我們 trace 到,current 值'遠'大於 timeout值時真的,無法解釋..unsigned 的溢位我也假設過;所以我們才把布林值記下來不然不必加上那一行的..
作者: dennisxkimo (Dennis(一上B就糟糕))   2016-06-22 17:33:00
不要被改結束條件就for(int i=0;i<=6;i++){},不行嗎?不然當curtime某原因不小心大於long.max就...
作者: KekeMonster (KekeMonster)   2016-06-22 17:36:00
" 分成兩行,就要小心有別的機會被修改變數" 寫成一行就沒機會? 你程式最後被編譯成 bytecode 後變成幾個指令?
作者: HuangJC (吹笛牧童)   2016-06-22 17:37:00
盡人事而已,我沒更好的工具了..
作者: dennisxkimo (Dennis(一上B就糟糕))   2016-06-22 17:37:00
假設Timeout保證不變,應該是觀察__lCurrentTime值
作者: dennisxkimo (Dennis(一上B就糟糕))   2016-06-22 17:59:00
你是用debug 一一觀察每次迴圈 各變數變化嗎?還是在迴圈內外插入print之類來看數值?
作者: kiwatami (悠游自在)   2016-06-22 18:15:00
喔 因為你特別用 time millis 當範例 我才以為你很直覺的使用你原本的原始碼使用的判斷式當例子 才會猜你原本的判斷式是不是跟時間有關不過既然你知道是一樣的怎麼會認為他跑不了第七次?是跑不了第八次才對如果是你說的那個什麼堆疊爆了...這範例才 10 行不到 不太可能吧print currenttime 會超過 timeout? 還是永遠等於?
作者: ssccg (23)   2016-06-22 18:35:00
這次沒得試了,下次遇到再說吧,我是覺得比較可能是編出來執行檔就是錯的,不然stack爆了再把long cast成double反而會對不太合理
作者: HuangJC (吹笛牧童)   2016-06-22 18:38:00
我多少表達個參戰的決心,唉講話要先大聲才有機會我很有想法,我覺得log太少了我一向不喜歡同樣的事做兩次,然後預設它們結果一樣我喜歡的 code style,就是只做一次,把它存起來用兩次這樣爭議小很多..比如 multi-thread 變數瞬變,我踢的鐵板也夠多了..
作者: kiwatami (悠游自在)   2016-06-22 19:51:00
我沒爬完整 我錯了 我自己去角落玩沙...
作者: HuangJC (吹笛牧童)   2016-06-23 00:14:00
:P 我自己安全過關就好..
作者: realmeat (真肉)   2016-06-23 11:45:00
c compile出問題並不是啥新鮮事通常都要追bytecode才看的出來
作者: HuangJC (吹笛牧童)   2016-06-23 12:41:00
byte code 要怎樣追?以前寫 VC 時,可以輕易調出組合語言,以及監看所有暫存器;現在在 Android Studio 下,也可以輕易看到 bytecode 嗎?
作者: Chikei ( )   2016-06-24 14:56:00
拿artifact出來直接看編出來的bytecode不同機器是不是一樣
作者: eieio (好多目標)   2016-07-01 12:41:00
1446154850 本來就比 1466154837+6 要小啊

Links booklink

Contact Us: admin [ a t ] ucptt.com