coverage.py的覆蓋率統計原理

原文出處:http://blog.theerrorlog.com/how-coverage-py-produce-coverage-statistics.html


在Erlang下,用rebar可以在運行單元測試的同時調用cover模塊,從而得 到單元測試的覆蓋率,這是很方便的一個功能。最近發現Python下的nose也可 以通過--with-coverage選項達到類似的效果,而這個選項實際上是調用了 coverage.py. 出於好奇,我研究了一下coverage.py的工作原理。

coverage.py的使用方法

抄一下coverage.py主頁的例子:

  1. coverage命令運行你的程序

    $ coverage run my_program.py arg1 arg2 
    blah blah ..your program's output.. blah blah
    
  2. 生成報告

    1
    2
    3
    4
    5
    6
    7
    $ coverage report -m
    Name                      Stmts   Miss  Cover   Missing
    -------------------------------------------------------
    my_program                   20      4    80%   33-35, 39
    my_other_module              56      6    89%   17-23
    -------------------------------------------------------
    TOTAL                        76     10    87%
    
  3. 或者,生成更漂亮的HTML報告

    $ coverage html
    

好吧對於一個開發工具來說,coverage算是挺成功的,因爲夠傻瓜。和 nosetests搭配使用的時候甚至更簡單,加一個參數就可以了,“猴子都能學 會”……

既然這裏有個腳本入口coverage,我們就從這裏開始吧~

工作原理

首先從源碼庫把代碼clone下來,觀察一下setup.py

1
2
3
4
5
scripts = [                                                                      
    'coverage = coverage:main',                                                  
    'coverage%d = coverage:main' % sys.version_info[:1],                         
    'coverage-%d.%d = coverage:main' % sys.version_info[:2],                     
    ]

這裏說明,coverage命令其實是調用了coverage模塊下的main函數,根據這個 函數的內容——爲了簡潔起見這裏省略掉一堆處理命令行參數的代碼——我們可以跟蹤到 coverage/control.py文件中的Coverage類。

這個類包含了所有從數據採集到生成報告的代碼,我們只關心它如何採集到程序執行 數據就好了。而“採集數據”這個操作,通過這裏的源碼可以推斷出,是由 coverage/collector.py中的Collector類和PyTracer類,又或者是CTracer 類合作完成的。

PyTracerCTracer其實在邏輯上是等價的,只不過一個是純Python實現,而另 一個是C語言實現。爲什麼會這樣呢?我們先繼續看下去……

既然這兩個類是一樣的,我們還是來看稍微漂亮一點的Python版本吧。PyTracer 的start函數有句docstring說,這個函數是用來“Return a Python function suitable for use with sys.settrace()”的。

好吧,真相大白了,coverage.py利用了Python虛擬機的trace機制。我怎麼就沒想 到呢?

sys.settrace(...)

這個函數的文檔在這裏,所以參數和用法說明什麼的我就省略了。實際上 這是Python程序調試機制的核心——幾乎每種虛擬機或者操作系統,都有類似的 機制,用於干預其上執行的程序,可以認爲這是給對應的調試器開的“後門”。 例如Erlang下有erlang:trace(...),POSIX系統下也有ptrace; Erlang的dbg模塊正是基於erlang:trace(...)的,而gdb也是在ptrace基 礎上工作的。

大家都知道調試器都是牛逼到可以把運行中的程序拆開再裝回去的,它們依賴 的trace機制自然也是大殺器。但是這貨通常要啓動各種鉤子(Hook),會嚴 重拖慢被trace的程序的運行速度,所以一般不會用在生產環境中。

這也就解釋了爲什麼coverage.py裏會有兩個tracer:tracer代碼基本上在 Python虛擬機的每步執行中都要被調用一遍,所以有一個C語言實現是能有效 提高效率的。那爲什麼還需要Python實現?這是爲了支持CPython以外的虛擬 機,像PyPy、Jython之類。

其他利用trace機制的有趣玩意

pycallgraph能生成Python程序中的函數調用關係圖(Call Graph)。

另外我在很~久之前用ptrace做了一個更改cd命令行爲的小玩具 :)(轉載注:作者寫的)


發佈了144 篇原創文章 · 獲贊 40 · 訪問量 102萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章