いまさらgraphviz(デジャヴュ)
でじゃ部分
Google検索してまわって疲れ果てるよりは作っちまえ、のような「軽いタスク」のために、しょうもない個人ツールをまぁ毎日のように量産するわけなんだけれど、「しょうもない」がゆえに整理もしないもんだから、なぜだか何度も書くハメになるものがいくつかあって。
そのうちの一つが「ライブラリ依存関係を調べる ldd を再帰的に呼び出して Graphviz で可視化」。
毎度雑に書くんだけど、今回もかなり雑。こんな:
1 import os
2 import re
3 import sys
4 from subprocess import Popen, PIPE
5
6 MAXDEPTH = 4
7
8
9 def ldd(depth, tl, nodes, deps):
10 if depth >= MAXDEPTH:
11 return
12 proc = Popen(['ldd', tl], stdout=PIPE)
13 stdo, stde = proc.communicate()
14 for line in stdo.split("\n"):
15 m1 = re.search(r"[ \t]+(.*)[ \t]+=>[ \t]*(.*)[ \t]+\(.*\)", line)
16 m2 = re.search(r"[ \t]+(.*)[ \t]+\(.*\)", line)
17 s = ""
18 if m1:
19 if m1.group(2):
20 s = m1.group(2)
21 else:
22 s = m1.group(1)
23 elif m2:
24 s = m2.group(1)
25 item = (tl, s)
26 if s.startswith("/"):
27 if s not in nodes:
28 nodes.add(s)
29 if item not in deps:
30 deps.append(item)
31 ldd(depth + 1, s, nodes, deps)
32
33
34 tl = sys.argv[1]
35 nodes = set([tl])
36 deps = []
37 ldd(1, tl, nodes, deps)
38 print("""\
39 digraph g {
40 graph [ranksep=0.2 nodesep=0.1];
41
42 """)
43 for node in nodes:
44 print(' "%s" [shape="Mrecord" label="{%s|%s}"];' % (
45 node, os.path.dirname(node), os.path.basename(node)))
46 for tl, ch in deps:
47 print(
48 ' "%s" -> "%s" '
49 '[color="steelblue" style=dotted arrowsize=0.5];' % (
50 tl, ch))
51 print("}")
たとえばこれを ldd2dot.py として。
1 me@host: ~$ python ldd2dot.py /lib64/libcups.so.2 > libcups.dot
2 me@host: ~$ dot -Tpdf libcups.dot -olibcups.pdf
出力はこんな:
Unable to display PDF
Click here to download
Click here to download
Graphviz は普通バックエンドとして使うもんであって素のまま使うことってあんまり考えないんだろうけど、ワタシは結構昔から愛着あって、「凝ったものが要らない個人用途の可視化」には結構使います。
うーん、ていうか ldd2dot って名前そのものの、どっかで見た気がすんなー。
デジャばない
「いまさらgraphviz」がね、さすがに気に喰わなかったんだ。「何度も作るハメに」と言ったとおりでほんとに何回も書いてて、もっと「見やすい」ものも作ったことがあったのは確かで。
しかも「いまさらgraphviz」、見返してみたらしょーもないミスしとる。んで、ちとタマランので、「同じく雑だけれども相当マシなもの」に作り直し。
ほんのちょっとした差なんだ、使えるものになるかどうかなんて。
ところで。
Graphvizる際は常にお世話になっている、老舗「Graphviz チュートリアル」が、Google 検索順位が下がってて探しにくくなってて困る。必要最小限には一番使いやすい。ここ。