いまさらgraphviz

Google検索してまわって疲れ果てるよりは作っちまえ、のような「軽いタスク」のために、しょうもない個人ツールをまぁ毎日のように量産するわけなんだけれど、「しょうもない」がゆえに整理もしないもんだから、なぜだか何度も書くハメになるものがいくつかあって。

そのうちの一つが「ライブラリ依存関係を調べる ldd を再帰的に呼び出して Graphviz で可視化」。

毎度雑に書くんだけど、今回もかなり雑。こんな:

 1 import re
 2 import sys
 3 from subprocess import Popen, PIPE
 4 
 5 MAXDEPTH = 4
 6 
 7 def _cook_path(s):
 8    return r"\l".join(filter(lambda _: _, s.split("/")))
 9 
10 def ldd(depth, tl, dejav):
11    if depth >= MAXDEPTH:
12       return
13    proc = Popen(['ldd', tl], stdout=PIPE)
14    stdo, stde = proc.communicate()
15    for line in stdo.split("\n"):
16       m1 = re.search(r"[ \t]+(.*)[ \t]+=>[ \t]*(.*)[ \t]+\(.*\)", line)
17       m2 = re.search(r"[ \t]+(.*)[ \t]+\(.*\)", line)
18       s = ""
19       if m1:
20          if m1.group(2):
21             s = m1.group(2)
22          else:
23             s = m1.group(1)
24          if s.startswith("/") and s not in dejav:
25             dejav.add(s)
26             ldd(depth + 1, s, dejav)
27       elif m2:
28          s = m2.group(1)
29       if s:
30          print('    "%s" -> "%s";' % (_cook_path(tl), _cook_path(s)))
31 
32 print("digraph g {")
33 ldd(1, sys.argv[1], set())
34 print("}")

たとえばこれを ldd2dot.py として。

1 me@host: ~$ python ldd2dot.py /usr/bin/python2 > python2.dot
2 me@host: ~$ dot -Tpdf python2.dot -opython2.pdf

出力はこんな:

Unable to display PDF
Click here to download

Graphviz は普通バックエンドとして使うもんであって素のまま使うことってあんまり考えないんだろうけど、ワタシは結構昔から愛着あって、「凝ったものが要らない個人用途の可視化」には結構使います。

うーん、ていうか ldd2dot って名前そのものの、どっかで見た気がすんなー。