monotone

monotone Mtn Source Tree

Root/contrib/dtrace2calltree.py

  • Property mtn:execute set to true
1#!/usr/bin/python
2
3# to get calltree on stdout, feed to this file's stdin, the output of:
4#
5#----------------
6#
7# #!/usr/sbin/dtrace -qs
8# syscall:::entry
9# /pid == $target/
10# {
11# self->start = timestamp;
12# }
13# syscall:::return
14# /self->start/
15# {
16# now=timestamp;
17# printf("syscall: %d\t%s\n", now - self->start, probefunc);
18# ustack();
19# self->start = 0;
20# }
21#
22#----------------
23#
24# It looks like:
25#
26#----------------
27# syscall: 47029 munmap
28# ld.so.1`munmap+0x7
29# ld.so.1`leave+0x83
30# ld.so.1`call_init+0x41
31# ld.so.1`setup+0xe2c
32# ld.so.1`_setup+0x28c
33# ld.so.1`_rt_boot+0x56
34# 0x80473dc
35#
36# syscall: 20017 mmap
37# libc.so.1`mmap+0x15
38# libc.so.1`lmalloc+0x6c
39# libc.so.1`atexit+0x1c
40# libc.so.1`libc_init+0x40
41# ld.so.1`call_init+0x46
42# ld.so.1`setup+0xe2c
43# ld.so.1`_setup+0x28c
44# ld.so.1`_rt_boot+0x56
45# 0x80473dc
46#
47# syscall: 4092 fstat64
48# libc.so.1`fstat64+0x15
49# libc.so.1`opendir+0x3e
50# ls`0x8052605
51# ls`0x8051b2b
52# ls`main+0x637
53# ls`0x8051106
54#
55# syscall: 1234 fstat64
56# libc.so.1`mmap+0x15
57# libc.so.1`lmalloc+0x6c
58# libc.so.1`atexit+0x1c
59# libc.so.1`libc_init+0x40
60# ld.so.1`call_init+0x46
61# ld.so.1`setup+0xe2c
62# ld.so.1`_setup+0x28c
63# ld.so.1`_rt_boot+0x56
64# 0x80473dc
65#
66#----------------
67
68# See also:
69# http://kcachegrind.sourceforge.net/cgi-bin/show.cgi/KcacheGrindCalltreeFormat
70
71import sys
72import os.path
73
74def main():
75 data = read(sys.stdin)
76 data.writeto(sys.stdout)
77
78# We need to eventually get, for each function:
79# -- time spent directly in it
80# -- list of functions it calls
81# -- call count (which we don't have)
82# -- the file/line number/name of the function called
83# -- inclusive cost of those calls
84# To do this, we have to accumulate everything in memory, then write it out.
85
86class FnEntry:
87 def __init__(self, obj_symbol):
88 (self.obj, self.symbol) = obj_symbol
89 self.cost = 0
90 self.children = {}
91
92 def charge(self, cost):
93 self.cost += cost
94
95 def charge_child(self, obj_symbol, cost):
96 self.children.setdefault(obj_symbol, 0)
97 self.children[obj_symbol] += cost
98
99 def writeto(self, stream):
100 stream.write("ob=%s\n" % self.obj)
101 stream.write("fn=%s\n" % self.symbol)
102 # <line> <direct cost>
103 stream.write("%s %s\n" % (1, self.cost))
104 for ((obj, symbol), cost) in self.children.iteritems():
105 stream.write("cob=%s\n" % obj)
106 stream.write("cfn=%s\n" % symbol)
107 # <number calls> <location in file>
108 stream.write("calls=1 1\n")
109 # <caller line> <cost>
110 stream.write("%s %s\n" % (1, cost))
111 stream.write("\n")
112
113class Data:
114 def __init__(self):
115 self.fns = {}
116
117 def get_entry(self, obj_symbol):
118 if obj_symbol not in self.fns:
119 self.fns[obj_symbol] = FnEntry(obj_symbol)
120 return self.fns[obj_symbol]
121
122 # stack is like [("libc.so.1", "fstat64"), ("ls", "main")], with outermost
123 # function last
124 def charge(self, cost, stack):
125 assert stack
126 prev = None
127 for obj_symbol in stack:
128 entry = self.get_entry(obj_symbol)
129 if prev is None:
130 entry.charge(cost)
131 else:
132 entry.charge_child(prev, cost)
133 prev = obj_symbol
134
135 def writeto(self, stream):
136 stream.write("events: syscall_wallclock\n")
137 stream.write("\n")
138 for entry in self.fns.itervalues():
139 entry.writeto(stream)
140
141def read_stack(stream):
142 stack = []
143 for line in stream:
144 line = line.strip()
145 if not line:
146 break
147 if "`" in line:
148 obj, rest = line.split("`", 1)
149 # libc.so.1`fstat64+0x15
150 if "+" in rest:
151 symbol, rest = rest.split("+", 1)
152 # ls`0x8052605
153 else:
154 symbol = rest
155 stack.append((obj, symbol))
156 else:
157 # 0x80473dc
158 stack.append(("?", line))
159 return stack
160
161def read(stream):
162 data = Data()
163 for line in stream:
164 if not line.startswith("syscall:"):
165 # skip over nonsense
166 continue
167 else:
168 # we have the beginning of a sample
169 syscall_marker, cost_str, syscall = line.strip().split()
170 assert syscall_marker == "syscall:"
171 cost = int(cost_str)
172 stack = read_stack(stream)
173 data.charge(cost, [("kernel", syscall)] + stack)
174 return data
175
176if __name__ == "__main__":
177 main()

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status