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

看板java作者時間7年前 (2016/06/21 12:58), 7年前編輯推噓8(8096)
留言104則, 10人參與, 最新討論串1/1
抱歉,"靈異現象"一詞是敝公司內部在用的 主要是講各種 執行不如預期 通常,有很大機率,會被人說"那是你自己寫程式不小心" 比如,程式在不該當的地方當了 但怎麼查也查不到原因 其實原因是更早之前使用了不存在或已刪除的變數 (這狀況在 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 環境使用上的問題) 謝謝 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 60.251.197.55 ※ 文章網址: https://www.ptt.cc/bbs/java/M.1466485086.A.EF4.html

06/21 18:18, , 1F
是我木眼嗎,result 本來就該是 true,false 才是靈
06/21 18:18, 1F

06/21 18:18, , 2F
異現象吧?
06/21 18:18, 2F
__lCurrentTime 會漸漸變大,而它比起另一個變數,只大6 因此這個 forloop 執行六次左右就該結束 結束時 __lCurrentTime<=__lTimeout 這個判斷條件應為 false 而 __result 應該不會被更新到 目前的問題就是不結束,__result 永遠為 true 我應該沒寫錯

06/21 19:08, , 3F
不要把找不出 root cause 當靈異現象啊 @o@
06/21 19:08, 3F
我常在板上挨罵就算了,他們可資深得不得了 工研院出來的,Trace 過整個 Unix, 我會懷疑他們在 java 上的功力,畢竟碰沒多久 但不會懷疑他們在 c/c++, multi-thread 的功力 (因為也曾是 multithread 講師) 因此比如變數被另一個 thread 干擾,這個也排除了 我自己前陣子是解掉一個,原因就是 multi-thread 變數 a 的值,我一直 trace 都是我要的,突然一轉眼就變了 但是同事避這個問題的方法很簡單:他沒必要共享變數,他的是 local 變數 既然變數沒被另一個 thread 參考到,那值就沒有變的理由 那為什麼會出現像 boolean a = (5 <= 3); 結果 a 為 true, 這樣很根本上的錯誤呢? ------------------- 問題可能解掉了 方法是把一個四千多行的函式縮小,拆成數個 因此目前我們只能認為,這也是 java 的極限 (我不是說不能寫四千多行喔,別自己去寫一個四千多行的來打我臉,不是這樣喔) 麻煩的就是,既然炸了也好歹打聲招呼,丟個 fatal 出來 都沒有啊,默默的... 那我們就有一堆不確定的猜測,大量的測試 操死一堆同事... ※ 編輯: HuangJC (60.251.197.55), 06/21/2016 19:26:31

06/22 00:21, , 4F
result 已經在 loop 裡被更新 6 次 true 了怎麽會是
06/22 00:21, 4F

06/22 00:21, , 5F
false
06/22 00:21, 5F

06/22 11:44, , 6F
我怎麼看都不覺得結果是flase
06/22 11:44, 6F

06/22 11:58, , 7F
不會是false,但也不該被執行到
06/22 11:58, 7F

06/22 11:59, , 8F
我們是因為奇怪它為什麼被執行 第七次以上
06/22 11:59, 8F

06/22 12:00, , 9F
大家還是在正常邏輯裡打轉,那不可能看懂我這篇啊...
06/22 12:00, 9F

06/22 12:06, , 10F
我看起來是0> 1> 2> 3> 4> 5> 6 七次
06/22 12:06, 10F

06/22 12:09, , 11F
哈 對是 7 次
06/22 12:09, 11F

06/22 12:11, , 12F
我覺得你為了呈現現象,把範例程式碼過度簡化到不會
06/22 12:11, 12F

06/22 12:11, , 13F
有錯了吧
06/22 12:11, 13F

06/22 12:16, , 14F
推 qrtt1 大所說的,我覺得比較像是邏輯性的錯誤造
06/22 12:16, 14F

06/22 12:16, , 15F
成無窮迴圈,找出 root cause 吧
06/22 12:16, 15F

06/22 12:30, , 16F
java method的max code size是有64KB的上限,但compiler多
06/22 12:30, 16F

06/22 12:30, , 17F
半會警告,4000行過了沒有難以確定,但寫這麼長確定不太好
06/22 12:30, 17F

06/22 12:30, , 18F
除錯及維護
06/22 12:30, 18F

06/22 13:05, , 19F
那我再說一次,不是跑了七次,是 endless,大家還是覺得
06/22 13:05, 19F

06/22 13:05, , 20F
邏輯性錯誤?
06/22 13:05, 20F

06/22 13:08, , 21F
當跑了二十次,我們就懷疑其布林值,這時還為true就怪了
06/22 13:08, 21F

06/22 13:09, , 22F
程式有簡化,但簡化的版本足以重製問題…
06/22 13:09, 22F

06/22 13:11, , 23F
我不是在表達邏輯而已,而是就這樣的code就會有endless l
06/22 13:11, 23F

06/22 13:11, , 24F
oop
06/22 13:11, 24F

06/22 14:08, , 25F
"不結束,__result永遠為true",for loop 無關resut吧
06/22 14:08, 25F

06/22 16:00, , 26F
有錯和沒錯的版本bytecode長一樣? 以你的說法看起來跟build
06/22 16:00, 26F

06/22 16:01, , 27F
環境有關,跟執行環境沒關?
06/22 16:01, 27F

06/22 16:03, , 28F
那去研究stack memory好像方向錯誤
06/22 16:03, 28F

06/22 16:17, , 29F
For loop 是否結束,決定於那個布林算式,那是和result一
06/22 16:17, 29F

06/22 16:17, , 30F
模一樣的算式
06/22 16:17, 30F
我文章從頭到尾都說和build machine 有關啊,換一台就不會 從前我做 embedded system 時 用的 cross compiler 很讓人懷念 那是一個只要一模一樣的 source code 進去 build 出來的 bin 就會一模一樣的 compiler 因此,每當我拿到一台新的電腦,重架起 compiler 時 第一件事,就是 build 一下我們最新的 release 拿到結果後做 file compare, 一定要一模一樣 若沒一模一樣,就一定有錯 又比如,加入新 model, 新功能 而同一個 code 要 build 舊 model code 已經有變了,要證明沒影響舊 model 也是 build 一版出來做 file compare 像現在這個問題,特定 build machine 有問題 其實我是很想說"就那台 build machine 自己有問題啦" 但不只一台會 因此如果沒找到問題,有個合理假設 換台機器 build 仍然只會被認為是頭痛醫頭腳痛醫腳而已 ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 16:34:28

06/22 16:37, , 31F
作業系統,CPU,JDK版本等都一模一樣嗎? 這當然有可能是遇
06/22 16:37, 31F

06/22 16:38, , 32F
compiler本身的錯誤
06/22 16:38, 32F
__lCurrentTime = 1466154837; __lTimeout = __lCurrentTime + 6 跑數次後, __lTimeout 沒變 __lCurrentTime 變成 1446154850 然後 __result = __lCurrentTime<=__lTimeout; 這個 __result 依然是 true 這樣大家感覺到問題沒有? 當然我自己不會這樣寫程式 我認為如果是記憶體錯亂,那麼 for loop 裡的布林值和算式裡的布林值,就未必結果相同 真的要龜毛,必需這樣寫 for ( ; __result = (__lCurrentTime<=__lTimeout) ; __lCurrentTime++) { Log.v(TAG, __result); __count++; } 也就是一定要和 forloop 的判斷式,用同一個 看到底是 boolean 不如預期 還是明明為 false 了但卻不往外跳 不管哪一個,其實都是執行不如預期 但我覺得這樣會更精準點 這已經不是語言邏輯層次的問題,我其實見過很多次 文章前面也說了:當記憶體配置出問題,那就見怪不怪了 java 用的是 GC,而不像 C 要由工程師自己去管理記憶體,new & delete 因此在 GC 極強的記憶體管理下 我其實還不太清楚怎麼造出以前的狀況 在 C,這種問題可以說是層出不窮,都是要去檢討有沒有做了違犯規定的存取.. ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 16:45:12

06/22 16:45, , 33F
所以bytecode到底一不一樣...
06/22 16:45, 33F

06/22 16:45, , 34F
bytecode 是指 build 出來的結果嗎?不一樣
06/22 16:45, 34F

06/22 16:45, , 35F
compiler有可能就真的有bug啊...
06/22 16:45, 35F
還有 35 則推文
還有 6 段內文
06/22 17:36, , 71F
個指令?
06/22 17:36, 71F

06/22 17:37, , 72F
盡人事而已,我沒更好的工具了..
06/22 17:37, 72F

06/22 17:37, , 73F
假設Timeout保證不變,應該是觀察__lCurrentTime值
06/22 17:37, 73F
有 log 它沒錯

06/22 17:37, , 74F
我們對 bytecode 會有的現象真的不熟..
06/22 17:37, 74F
※ 編輯: HuangJC (60.251.197.55), 06/22/2016 17:38:02 題外話,在以前寫 C 時,我碰過另一個靈異現象 void F2() { a = 5; } void F1() { F2(); a = 6; a = 7; . . . } 如上,F2 是幫忙設一些初始值 接下去 F1 的片段是開始修改那些值 可是我發現那些值一直跳回來 比如上例的 a, 明明後續設為 6, 設為 7 但監控它會發現它一直是 5 我全域搜尋 a 何時被改為 5,只有 F2 裡有做這件事 後來忍不住了,在 F2 開頭下一個中斷點 結果發現,F1 "每執行一行",就會跳去執行 F2 一次 程式裡完全看不出來,但執行結果就是這樣 那次我加班到凌晨都解不掉 幸好第二天,主管說那個案子不做了 我真的感覺莫名其妙 不管重開機,clean build 種種能把環境弄乾淨的方法我都試了 但我就是不知道,誰奪取了控制權,硬去執行 F2 的 那種感覺就像陷入了單步中斷 debugger 就會做這種事 但那是 VC 的特權,誰搶去做了? ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 17:49:37

06/22 17:59, , 75F
你是用debug 一一觀察每次迴圈 各變數變化嗎?
06/22 17:59, 75F

06/22 18:00, , 76F
還是在迴圈內外插入print之類來看數值?
06/22 18:00, 76F
這要講兩支程式,一支是線上實際出貨的 這個有看所有變數 另外一個是為了縮小測試範圍,所以簡化(但仍然可以重製 bug) 公開在板上的是這支程式 這支程式裡已經沒有 log 了 我也不可能用步進執行 因為,一但步進執行,就是在我的機器上重 build (不重 build 但又可以步進執行,以前 code view 玩過,現在在 android 我還沒試過) 而我們強調是'那一台特定 build machine 才會有問題' 公開的這一支,同事只說,永遠執行不到第二個中斷點 endless loop 這句話,和實際出貨的程式倒是一樣 QA 測到的就是迴圈跑不完;但比較值已經遞增到極大了,為什麼還不結束 loop? (比較值及被比較值,兩者都有 log) 我是很想參戰啦,但我沒重製過問題 我參戰的必要條件是:讓我可以操作 build machine,任意 build 我改的 code 這點,沒法子 大家煩得要死,不給我玩 -------------- 我們出貨的程式,不是任意 RD 電腦 build 出來都可以出 而是特定的 build machine 它一連串的批次動作,從取 code, build, 到包裝成出貨 package 放至特定子目錄 (子目錄名稱和時間日期相關)都是自動化的 我無法把我的 code 塞給它,除非我 check in code 他們不給我玩啦.. 所以,謝謝大家,警察可以出來洗地了... ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 18:15:49

06/22 18:15, , 77F
喔 因為你特別用 time millis 當範例 我才以為你很直覺
06/22 18:15, 77F

06/22 18:15, , 78F
的使用你原本的原始碼使用的判斷式當例子 才會猜你原本
06/22 18:15, 78F

06/22 18:15, , 79F
的判斷式是不是跟時間有關
06/22 18:15, 79F

06/22 18:15, , 80F
不過既然你知道是一樣的
06/22 18:15, 80F

06/22 18:15, , 81F
怎麼會認為他跑不了第七次?
06/22 18:15, 81F

06/22 18:15, , 82F
是跑不了第八次才對
06/22 18:15, 82F

06/22 18:15, , 83F
如果是你說的那個什麼堆疊爆了...
06/22 18:15, 83F

06/22 18:15, , 84F
這範例才 10 行不到 不太可能吧
06/22 18:15, 84F
但我有說,這段程式,要放在我們程式內 我不想怪你沒爬文耶,我是來請教大家的,要有禮貌 :P

06/22 18:15, , 85F
print currenttime 會超過 timeout? 還是永遠等於?
06/22 18:15, 85F
會超過 :P ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 18:18:22

06/22 18:35, , 86F
這次沒得試了,下次遇到再說吧,我是覺得比較可能是編出來
06/22 18:35, 86F

06/22 18:36, , 87F
執行檔就是錯的,不然stack爆了再把long cast成double反而
06/22 18:36, 87F

06/22 18:37, , 88F
會對不太合理
06/22 18:37, 88F

06/22 18:38, , 89F
我多少表達個參戰的決心,唉
06/22 18:38, 89F

06/22 18:38, , 90F
講話要先大聲才有機會
06/22 18:38, 90F

06/22 18:39, , 91F
我很有想法,我覺得log太少了
06/22 18:39, 91F
for ( ; ; __lCurrentTime++) { __result = (__lCurrentTime<=__lTimeout); Log.v();//__result, __lCurrentTime, __lTimeout 三個都要看 //一次就能讓大家標定問題 if ( !__result ) break; } 這樣更龜毛 問題應該可以限縮至"為什麼 long compare 出錯了" ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 19:09:55

06/22 19:10, , 92F
我一向不喜歡同樣的事做兩次,然後預設它們結果一樣
06/22 19:10, 92F

06/22 19:10, , 93F
我喜歡的 code style,就是只做一次,把它存起來用兩次
06/22 19:10, 93F

06/22 19:11, , 94F
這樣爭議小很多..
06/22 19:11, 94F

06/22 19:12, , 95F
比如 multi-thread 變數瞬變,我踢的鐵板也夠多了..
06/22 19:12, 95F

06/22 19:51, , 96F
我沒爬完整 我錯了 我自己去角落玩沙...
06/22 19:51, 96F

06/23 00:14, , 97F
:P 我自己安全過關就好..
06/23 00:14, 97F

06/23 11:45, , 98F
c compile出問題並不是啥新鮮事
06/23 11:45, 98F

06/23 11:45, , 99F
通常都要追bytecode才看的出來
06/23 11:45, 99F

06/23 12:41, , 100F
byte code 要怎樣追?以前寫 VC 時,可以輕易調出組合語
06/23 12:41, 100F

06/23 12:41, , 101F
言,以及監看所有暫存器;現在在 Android Studio 下,也
06/23 12:41, 101F

06/23 12:41, , 102F
可以輕易看到 bytecode 嗎?
06/23 12:41, 102F

06/24 14:56, , 103F
拿artifact出來直接看編出來的bytecode不同機器是不是一樣
06/24 14:56, 103F

07/01 12:41, , 104F
1446154850 本來就比 1466154837+6 要小啊
07/01 12:41, 104F
文章代碼(AID): #1NQCbUxq (java)
文章代碼(AID): #1NQCbUxq (java)