2013年4月5日 星期五

在 linux 和 iOS 上抓 memory leak 和 heap profiling 心得

問題描述

首先要定義一下什麼是「memory leak」。它的正式定義是指「程式弄丟了未釋放空間的位置」, 之後無法使用這塊空間也無法釋放它。另一方面, 程式沒有弄丟位置, 但是配置的空間愈用愈大, 或是用完後沒有歸還, 導致記憶體不足。若要應付第一種情況, 可用 memory leak 作為關鍵字找相關討論; 第二種情況要用 memory profiler 或 heap profiler 了解記憶體用去那裡, ( 感謝 Scott 告知 )。

就我自己少量的經驗來說, 第二種比較棘手, 第一種通常是粗心, 找到後補上 free/delete 即可。但第二種可能和程式配置和運用記憶體的策略有關。我遇到的問題都是第二種。若程式有針對不同區塊自己管記憶體的話, 比較方便日後植入統計程式找出瓶頸。順道一提, 如同 TDD 的精神, 在設計程式架構時就考慮測試, 會讓程式更易測試、維護和除錯。在設計架構時就考慮除錯, 也會讓程式更易維護和除錯。

在 Linux 上找 memory leak 和 profile heap

在 Linux 上試了兩套工具 mtracevalgrind。mtrace 滿簡單的, 可用來抓 C 的 memory leak, 但不適用於 C++, 原因是 mtrace 在 malloc/free 動手腳, 可以找出那一行呼叫 malloc 後沒呼叫 free。但是 C++ 的情況是 libstdc++ 呼叫 malloc, 而我們有興趣的是誰呼叫了 new。

後來改試 valgrind 效果還不錯。valgrind 是多個除錯工具的集合體, 我試了兩個工具:

  • memcheck: 抓 memory leak 和記憶體讀寫錯誤。
  • massif: profile heap 的用量, 可看到不同時間 heap 由各函式配置的比例。

使用 valgrind 的代價是程式會慢個 20 倍左右。但是用工具協助檢測問題, 99% 的情況會比自己憑空猜想來得準。還是有些耐性使用 valgrind 會比較快找到問題源頭。

memcheck

$ valgrind --tool=memcheck --leak-check=full PROG PROG_ARG ...

跑完後會輸出結果在 console。建議先有些耐心看完官網文件。對 memcheck 的報表有疑問的話, 可參考 "Understanding Valgrind memory leak reports"。重點是 possibly leak 表示 valgrind 無法判斷它是 still reachable 或 definitely leak。still reachable 可能是某些函式庫配置自己一塊 memory pool 來用, 程式離開時才會釋放, 所以嚴格說起來不是 memory leak。在這個階段, 我們關心的是 definitely leak。

massif

$ valgrind--tool=massif PROG PROG_ARG ...

跑完後會輸出結果在 massif.out.PID。然後執行 ms_print 看結果

$ ms_print massif.out.PID

建議先有些耐心看完官網文件。我試了許多參數想抓到程式離開前的 detailed snapshot 都徒勞無功。最後發覺用 vgdb 意外的簡單。

用法如下:

  1. 先執行 valgrind
  2. 在另一個 shell 下執行 vgdb detailed_snapshot /path/to/snapshot
  3. 閱讀當下的 snapshot: ms_print /path/to/snapshot

valgrind 也可以和 gdb 一起互動, 沒想到會在這個機會下試用到 gdb 的 remote debugging 功能。

經 Scott 告知, 除了用 ms_print 看文字報表外, 也可用 Massif Visualizer 看結果。這工具看起來滿不錯的, 先備忘在這裡。

另外補充一下 Linux 下記憶體用量的意思。平常我們是看 ps 回報的 VSZ 和 RSS, 但兩者其實都不夠準確, 用 pmap -d PID 看最後一行輸出的 private writable memory, 可能比較適合。

在 iOS 的找 memory leak 和 profile heap

就用 Mac OS X 附的 Instrutments, 使用 Leak 或 Allocations, 不用看文件即可輕鬆搞定! 全圖形介面操作, 還有詳細的報表。要看各 functions 用掉 heap 記憶體的比例的話, 在 Allocations 裡點 Statistics -> Call Tree。

同事用過 Instruments 後, 甚至說「真想將 Linux 的程式 port 到 Mac, 這樣才可以用 Instruments」, 由此可知 Instruments 有多麼地好用。

沒有留言:

張貼留言

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...