[問題] 有沒有法子找出程式為何無法中止?

看板Python作者 (吹笛牧童)時間1年前 (2023/04/01 05:44), 1年前編輯推噓11(11040)
留言51則, 9人參與, 1年前最新討論串1/1
今天改的版本,突然間陷入 while loop 無法中止 強迫中止時錯誤訊息是 File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/ threading.py", line 1388, in _shutdown lock.acquire() 這種訊息有和沒有差不多 有 lock 沒有釋放? 於是我寫了個 MyLock 取代 threading.Lock 開始記錄到底是哪個 Lock 有進入而沒有退出 全都有退出!根本查不到 最後是透過 git 不斷捲回舊版,找到十二小時前的版本還可以正常退出程式 一查,重點不在程式,在資料! 某一種資料格式,我會啟動 Timer, 而 Timer 時間到後,我會再重啟 Timer (其實就是連續脈衝波,每隔幾秒一次的意思) 因為我預約了下一次的執行需求,所以程式無法釋放 只要 cancel timer, 就解掉這個 bug 了 (bug 是一直存在的,只是一直沒鍵入這種資料凸顯 bug) 還真是忘了,在 Python 的 Timer 是以 Thread 來實現 而骨子裡可能設了個 lock 那我問題來了:這次是幸運在 git 裡保存夠多細節,且能捲回一定能釋放的舊版 假設沒這樣的環境,只能靠我自己抓,而錯誤訊息又只說有 lock 沒釋放 我能回溯知道是哪一行程式產生的 lock 鎖住了嗎? 有什麼參數,什麼 debug tool 能用嗎? 謝謝 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 116.241.233.114 (臺灣) ※ 文章網址: https://www.ptt.cc/bbs/Python/M.1680299059.A.BEA.html

04/01 11:21, 1年前 , 1F
Rlock 試試?
04/01 11:21, 1F
RLock 是允許重複進入,避免同一個 thread 自己把自己卡住 而我的要求是回應卡住的物件是在哪行指令生成 舉例來說 Line 10: a = new C1() # test.c 這是產生物件;目前都有自動釋放,但假如在沒自動釋放的 C 語言 而且我沒有釋放物件,造成記憶體浪費, 有工具能回應我,浪費的記憶體在 test.c/Line 10 嗎? 我還真用過這種工具

04/01 12:27, 1年前 , 2F
gdb
04/01 12:27, 2F
哇!我試試! 謝謝 ... 超過我的程度 XD 裝了,跑不起來.. ※ 編輯: HuangJC (116.241.233.114 臺灣), 04/01/2023 15:21:44

04/02 17:33, 1年前 , 3F
c/c++ 的話有 sanitizer valgrind 之類的工具可以看
04/02 17:33, 3F

04/03 20:03, 1年前 , 4F
有考慮...print嗎?cc
04/03 20:03, 4F

04/03 20:16, 1年前 , 5F
cc
04/03 20:16, 5F

04/05 17:35, 1年前 , 6F
我猜二樓其實是要說pdb??
04/05 17:35, 6F
當年我用過 debug tool 抓記憶體漏洞之類,真的過癮 但有種很怪的感覺,那是高手在替我做事,而且能力是有限的 比如找尋未釋放記憶體的原理是什麼? a = new C1() // 配發記憶體 a del a // 釋回 a 如果 a 忘記釋放,要怎麼找出 a 在哪一行配發的? 畢竟 a 這個符號可能被重覆配發,也可能 b = a // 被 b 承接走 a = NULL // 棄用 a 這個符號,但 b 忘了釋放 XD 因此這種 tool 能做的只有。。當初 a 配發時,就把 debug info 順便存入 比如覆寫 new 這功能,成為 DEBUG_NEW 然後隱性的傳入程式的資訊(利用 __FILE__, __LINE__ 這種 compiler 維護的符號) 這也代表配發記憶體時,我本來只配幾個 byte, 但可能同時配發更多的 debug byte 因此 debug 時的速度會變慢,耗用記憶體會增加 但高手替我抓還是很好用 XD 等我程式寫太大時,這種 tool 因為要多耗用很多 debug 資源,就會跑不動 最佳方案仍然是自己寫 debug tool 但我如果功力高到會寫 debug tool,我當初就不會犯錯了! 先有雞還是先有蛋的問題 -------- 總之我這次學到了,Timer 元件其實要注意它的配發和釋放 ※ 編輯: HuangJC (123.204.157.162 臺灣), 04/06/2023 01:08:28

04/06 23:27, 1年前 , 7F
為什麼一般感覺不太會遇到這種問題,你的需求是特
04/06 23:27, 7F

04/06 23:27, 1年前 , 8F
別刁鑽還是你的寫法有可以再優化的地方?
04/06 23:27, 8F
是遇到了才知道遇到了啊.. 我一開始可超級謹慎的 class MyApp(App): def f1(self): self.destroy() app = MyApp() pApp = weakref.proxy(app) btn = PushButton(app, command=pApp.f1) app.display() 比如我曾這樣寫程式,其中 pApp 根本沒必要 為什麼這樣寫?因為我怕 btn 物件裡如果保有 app 因為參考到自己,會導致永不釋放 所以凡參考我都只留 weak 版本 後來我發現不這麼寫,照樣不會卡,照樣釋放,想法可以很單純 XD 我從完全沒有 gc 的 C 語言學起的,從前記憶體管理都自己來 那時如果釋放了不把變數標成 null, 再叫用到就 null access 能碰到的問題可非常多 a = new C1(); b = a; // 比如進入函式,也是拷走一個 a 指標在函式裡用 del b; // 除非我不在函式裡釋放物件,否則當然會寫這種東西 a.xxx // 這時外面還想取用 a pointer 就慘了,null access 後來開始用有 gc 的語言,可是參照次數很重要 如果還有任何指標參照到物件,物件就不會釋放 那循環參照怎麼算?還真是測了才知道 一般不會遇到?不是,我一開始學時曾經有遇到,但不知道就寫了兩年 舉例來說,樹莓派一開機就跑我的程式,但從沒想過要關機 執行完畢直接斷電,就算程式無法釋放,都斷電了是有何差別? 只有刻意去想,我就是要讓程式釋放,才會測試到這件事 比如我可能要寫遠端更新,要讓程式結束並且重開機 喔,還是有很暴力的寫法,比如下個 kill process 指令 在程式未能退出的情況下,由 os 端殺掉 XD 會去注意是潔癖吧.. 總覺得,類似檔案沒有 close 等循序退出問題,如果沒做,很不舒服 別人沒遇到嗎? 比如我在寫 ios 手機時,用的是 obj c 我也下載過別人的函式庫用 我看別人防這個也防得很嚴 一堆 weaf ref 在用 應該說,為什麼 python 在參照到自己的指標留著時還能釋放 這才是超乎我預期的 XD 所以 Timer 在幾秒後才要啟動,而它啟動時要用到的指標要不要 weak 版? 程式想退出了,幾秒後根本就不存在,若保留指標程式就無法退出 那按鍵按了要退出程式,按鍵的程式裡保留指標,為什麼可以退出 :P 那也是測了才知道。。 QA 不會做這項測試,都專注在跑程式,而不是退出程式 XD 我也是哪天心血來潮才測到的。 那時曾經呼叫同事寫的副程式,用 try-catch 包起來呼叫 甚至是每次呼叫前才 load library (本來可以用靜態寫的,不必這麼麻煩) 呼叫後就 free library 為什麼要這樣做?因為同事記憶體釋放沒寫好啊,我整個 free 掉才會乾淨 所以一般是沒遇到,還是沒注意到? ※ 編輯: HuangJC (123.204.157.162 臺灣), 04/07/2023 01:00:44 甚至我想過一個問題:參照到自己,或說交互參照 這種還有法子釋放資源是怎麼做的? (同事是說有很多論文,不過我不擅長查) 如果說這機制有 bug 會不會我剛用沒問題,但用多了,資源就變無法釋放? 那這不是我有問題,是寫釋放的有 bug, 沒確實 implement 出來 看過是說 gc 不會時時刻刻,而是久久做一次 所以我以前去主動檢查有沒有釋放,也常沒釋放 必需在主動檢查前,先主動催 gc 做一次垃圾搜集 會不會就是因為這樣,我下載的第三方函式庫才廣用 weak 版本?

04/07 18:10, 1年前 , 9F
這些資訊不夠去找,複現的情境也不夠清楚,能夠複現的話就
04/07 18:10, 9F

04/07 18:10, 1年前 , 10F
多埋幾個點去 logging 吧
04/07 18:10, 10F

04/07 18:12, 1年前 , 11F
樹莓派那段看不是很懂,你敘述了你的做法,但沒有敘述為什
04/07 18:12, 11F

04/07 18:12, 1年前 , 12F
麼要這樣做。這也是為什麼前面會說一般不會遇到,他想了解
04/07 18:12, 12F

04/07 18:12, 1年前 , 13F
的是你的「需求」跟你現在的「問題」之間是不是存在 X-Y 問
04/07 18:12, 13F

04/07 18:12, 1年前 , 14F
04/07 18:12, 14F

04/07 18:28, 1年前 , 15F
Hsins,建議你可以a他id參考一下
04/07 18:28, 15F
a 我 id 其實沒幫助 我直說好了,有人說我半桶水 那我能怎樣?膨風說自己全都懂? 前不久和另一位也是資深工程師聊這邊的狀況 聊到有人叫我去讀 os,他直接回我,他也從業幾十年,但從沒讀過 os 所以我們只能這麼說:底線就是,這本來就是我的責任 資訊不夠,多少是因為我不能把公司產品全丟到網路上,source code 公開 所以得用各種抽象的方式,只曝露一些點來討論 若實在是不行也就算了

04/07 19:42, 1年前 , 16F
~ " ~ 不是很能讀懂問題...
04/07 19:42, 16F

04/08 09:54, 1年前 , 17F
建議問問題可以把完整脈絡寫出來(包含範例、使用情境、需求
04/08 09:54, 17F

04/08 09:54, 1年前 , 18F
),而不是大量的類似內心os的口語文字。簡單說就是文字要
04/08 09:54, 18F

04/08 09:54, 1年前 , 19F
精煉一點
04/08 09:54, 19F

04/08 09:57, 1年前 , 20F
另外,寫python其實不需刻意del變數的,用不到gc自然就收
04/08 09:57, 20F

04/08 09:57, 1年前 , 21F
走了(simple is better)
04/08 09:57, 21F
這我知道,知道還這麼做,就代表混入 debug 等心得.. 當年寫 c,寫到某個地方,我說懷疑 compiler 有 bug 也是被人狠狠嘲笑 幾年後看到臉書上另一個朋友說,c compiler 有一大堆 bug... 咦,原來也有別人遇到,那我被笑又怎麼回事;只能說有沒有人同樣見識到了 責任不能推到 compiler 去,但我們得去驗證這些問題存不存在 所以對這些都是有限信任 obj c 也是有 gc 的 (應該說隨著年代, 很多語言的特性互相學習) 而它若是自我參照時,就不會釋放! 這我能寫一個極小的程式驗證到 發生時,就要'打破這個環節' 在 obj c 我也不會去 del, 我是把指標設為 None 這樣循環參照就打破了 (我這篇裡用到 del, 但那是 c 啊,先還原到最原始,沒有 gc 的環境描述問題) 去說明這些我知道似乎也沒有意義 知道我也是碰到這些 bug 了

04/08 11:10, 1年前 , 22F
Sunal懂我 哈哈哈
04/08 11:10, 22F

04/08 11:50, 1年前 , 23F
threading.py", line 1388, in _shutdown
04/08 11:50, 23F

04/08 11:50, 1年前 , 24F
光這一行就可以從 python threading.py source code 知道
04/08 11:50, 24F

04/08 11:51, 1年前 , 25F
class Timer(Thread): 這件事情...(而且文件也有寫)
04/08 11:51, 25F
Timer 是一種 Thread 這我知道 我不知道的是我啟動了 lock lock 一般來說,是 multi-thread 時, 某 thread 要求獨佔資源,其它 thread 不能進入 而我使用 Timer 時,我沒要獨佔什麼,所以我沒想到它啟動了 lock 或者,這問題不是 Timer 啟動了 lock 我程式中本來就用了很多 lock, 憑最後出現的錯誤訊息,我並不知是哪個 lock 未釋放 不然改個說法:我能不能把每個 lock 命名 當程式用強迫退出的方式並且收到錯誤訊息 能夠知道是哪個 lock 沒有釋放 ※ 編輯: HuangJC (116.241.233.114 臺灣), 04/08/2023 16:24:15

04/08 16:42, 1年前 , 26F
source code 中也有用到 lock 如果你看到錯有去看 code 就
04/08 16:42, 26F

04/08 16:42, 1年前 , 27F
知道了
04/08 16:42, 27F

04/08 16:44, 1年前 , 28F
另外,你也可以debug時hack python source 去達到你的目的
04/08 16:44, 28F
蠻常 hack 的,但還是不行,主要是猜測行為都猜錯了,就無法看著 code 想像 來看這麼短的程式就好 from guizero import App, PushButton app = App() btn = PushButton(app, command=app.destroy) app.display() 這程式的目的只有一個:產生一個按鍵,按了就退出程式 我本來以為這不會正常運作的 理由是 btn 是個物件,它裡面有變數參照了 app.destroy 那這樣 app 的 ref count 不是會加一嗎? 不用執行,光有變數拷走我的指標,就要加一了,沒錯吧.. 不然萬一要執行時 app 不存在怎麼辦? 按下按鍵,我以為程式要卡死不能退出,但它就是順利退出了 我就是以為,這個 gc 可以自行解決啊? 結果 timer 的倒沒自行解決,得由我來 cancel timer! ※ 編輯: HuangJC (116.241.233.114 臺灣), 04/09/2023 13:21:02

04/09 13:50, 1年前 , 29F
感覺你還是沒搞懂,實在幫不了。包含我前面說的「建議」也.
04/09 13:50, 29F

04/09 14:51, 1年前 , 30F
很難救
04/09 14:51, 30F

04/09 15:54, 1年前 , 31F
既然都在處理 thread 跟 lock 問題,去把 OS 裡這部分的概
04/09 15:54, 31F

04/09 15:54, 1年前 , 32F
念補齊滿合理的……
04/09 15:54, 32F
要講這麼籠統的話,建議計概重學,建議 python 重學,不也都說得通? 成語 邯鄲學步 就這意思了 學著學著有沒有更高明?還是反而被搞糊塗了呢? thread 就我所知,就是系統把目前所有的暫存器通通儲存起來, 然後切換到另一個 thread 的 context 去 之後再切換回來,除了時間點改變之外,CPU 完全可以在這個點繼續往下執行 而兩個 thread 如果有共用變數,且搶著修改,可能混成一團 因此在有必要獨佔一個變數時,就可以用一個同步物件來 lock 使用權 當然隨著應用的方式不同,看起來也許不像鎖著物件,而像鎖著程式區塊 比如 critical section 的應用方式 知道,但解決不了 因為在這個地方忽略而產生 bug, 錯訊訊息不見得是在這個地方 所以我是在請教 debug 技巧 就以我文章最前面所講的問題,我是已經解決了 我是知道解法,再回頭看錯誤訊息,覺得沒有幫助 其實我想要的是在程式強硬退出後,有'列舉所有 thread 狀態'這類指令 所以我已經在打造自己的 debug tool 了 希望可以追蹤 thread 當下的行為,以知道 thread 為何釋放不了 ※ 編輯: HuangJC (49.217.174.143 臺灣), 04/10/2023 01:51:25

04/10 11:31, 1年前 , 33F
就跟你們說那邊有馬蜂窩了,你們...
04/10 11:31, 33F

04/10 21:25, 1年前 , 34F
樓上+1
04/10 21:25, 34F

04/10 21:25, 1年前 , 35F
如果可以穩定重現又有行數其實蠻幸福的,對準點直接打lo
04/10 21:25, 35F

04/10 21:25, 1年前 , 36F
g就好。
04/10 21:25, 36F

04/10 21:25, 1年前 , 37F
之前寫mit 6.824有deadlock都是這樣找的。
04/10 21:25, 37F

04/10 22:00, 1年前 , 38F
我覺得這篇敘述的思考方向很怪……對我來說,應該是試圖找
04/10 22:00, 38F

04/10 22:01, 1年前 , 39F
到問題點,而不是去找一個或者是造出一個專門的除錯工具…
04/10 22:01, 39F

04/10 22:02, 1年前 , 40F
能夠複現當然是最好,不能夠複現會去找相關日誌,如果日誌
04/10 22:02, 40F

04/10 22:02, 1年前 , 41F
不足以判斷,那說明日誌輸出的埋設點不足;以你的例子來說
04/10 22:02, 41F

04/10 22:03, 1年前 , 42F
,我不會想著要怎麼在強制退出後列出 thread 狀態這件事,
04/10 22:03, 42F

04/10 22:05, 1年前 , 43F
而是紀錄 thread 切換不同狀態,以及他的初始化和結束。
04/10 22:05, 43F

04/10 22:07, 1年前 , 44F
另外,你是不是曲解邯鄲學步的意思了?建議回去補足作業系
04/10 22:07, 44F

04/10 22:09, 1年前 , 45F
統知識,我認為有其必要性;至於學習的好不好,那是學習者
04/10 22:09, 45F

04/10 22:10, 1年前 , 46F
策略跟方向上可能有錯誤,不代表這個建議是錯誤的呀。 
04/10 22:10, 46F

04/10 22:11, 1年前 , 47F
我反倒覺得如果有資深工程師說從業十幾年,沒讀過作業系統
04/10 22:11, 47F

04/10 22:12, 1年前 , 48F
;而有人聽信去效法,卻落得基礎不扎實,資深只與從業時間
04/10 22:12, 48F

04/10 22:12, 1年前 , 49F
長短而無關能力,那才真的是在邯鄲學步…
04/10 22:12, 49F

04/10 22:28, 1年前 , 50F
不過我很支持你打造自己的除錯工具,但如果你有時間壓力,
04/10 22:28, 50F

04/10 22:28, 1年前 , 51F
多埋一些日誌會比較適合
04/10 22:28, 51F
文章代碼(AID): #1a9rGplg (Python)