-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathsamples2calltree.py
executable file
·139 lines (113 loc) · 4.59 KB
/
samples2calltree.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/usr/bin/python
import sys
import os
import time
from sample_reader import parseFile
import syscalls
outFd = None
allThreads = None
numThreads = None
def handleHeader (header):
global allThreads, numThreads
allThreads = header['all_threads']
numThreads = len(allThreads)
outFd.write("pid: %d\n" % allThreads[0]) # assume that first thread ID is PID of whole process
outFd.write("events: NumSamples")
if len(allThreads) > 1:
threadListStr = " ".join( [ "thread_%d" % t for t in allThreads ] )
outFd.write(" " + threadListStr)
outFd.write("\n\n")
def handleEvent (e):
threadId = e[2]
if numThreads > 1:
# hack to work around https://bugs.kde.org/show_bug.cgi?id=263594:
# (Kcachegrind chokes if a call has an inclusive cost of zero for some event types)
costZero = 1
costOne = 1000
threadCost = [costZero] * numThreads
threadIndex = allThreads.index(threadId)
threadCost[threadIndex] = costOne
threadCostStr = " " + (" ".join( [ str(x) for x in threadCost ] ))
else:
costZero = 0
costOne = 1
threadCostStr = ""
outFd.write("# event at %s (PID: %d)\n" % ( time.strftime('%c', time.localtime(e[0]) ), threadId) )
frames = []
knownFunctions = {}
regs = e[3]
currFrame = 1
for f in e[1]:
binName = f[1]
if binName is None: binName = '???'
if binName == '[vdso]': binName = 'linux-gate.dso.1'
funcName = f[2]
if f[1] == '[vdso]' and currFrame == 1:
syscallId = None
if regs.has_key('oeax') and regs['oeax'] > 0 and regs['oeax'] <= 0x7fffffff:
syscallId = regs['oeax']
elif regs.has_key('eax') and regs['eax'] > 0 and regs['eax'] <= 0x7fffffff:
syscallId = regs['eax']
# resolve syscall number which was stored in EAX or ORIG_EAX:
if syscallId is None:
funcName = 'SYS_unknown'
elif syscalls.SYSCALL_TABLE.has_key(syscallId):
funcName = 'SYS_%s' % syscalls.SYSCALL_TABLE[syscallId]
else:
funcName = 'SYS_%d' % syscallId
if funcName is None:
funcName = '_0x%08x' % f[0]
if f[1] is not None:
funcName += "_" + os.path.basename(f[1])
lineNo = f[4]
if lineNo is None or lineNo <= 0: lineNo = 1
fileName = f[3]
if fileName is None: fileName = '???'
# TODO: try to fix address (it currently usually points to _next_ instruction)
localAddr = f[5]
if localAddr is None: localAddr = 0
# simple cycle detection
if not(knownFunctions.has_key(funcName)):
knownFunctions[funcName] = 1
else:
knownFunctions[funcName] += 1
if knownFunctions[funcName] > 1:
funcName += "'%d" % knownFunctions[funcName]
# (address, binary file, function, source file, line number, address in binary file)
frames.append( (f[0], binName, funcName, fileName, lineNo, localAddr) )
if currFrame == 1:
outFd.write("ob=%s\n" % binName)
outFd.write("fl=%s\n" % fileName)
outFd.write("fn=%s\n" % funcName)
outFd.write("0x%x %d %d%s\n" % (localAddr, lineNo, costOne, threadCostStr))
currFrame += 1
# function calls
outFd.write("# function calls\n")
currFrame = 1
for f in frames[1:]:
if f[2] is not None:
prevFrame = frames[ currFrame-1 ]
if prevFrame[2] is not None:
lineNo = f[4]
prevLineNo = prevFrame[4]
outFd.write("ob=%s\n" % f[1])
outFd.write("fl=%s\n" % f[3])
outFd.write("fn=%s\n" % f[2])
outFd.write("cob=%s\n" % prevFrame[1])
outFd.write("cfl=%s\n" % prevFrame[3])
outFd.write("cfn=%s\n" % prevFrame[2])
outFd.write("calls=%d 0x%x %d\n" % (costOne, prevFrame[5], prevLineNo))
outFd.write("0x%x %d %d%s\n" % (f[5], lineNo, costOne, threadCostStr))
currFrame += 1
outFd.write("\n")
if __name__ == '__main__':
if len(sys.argv) != 2:
print "Usage: %s <sample data file>" % sys.argv[0]
sys.exit(1)
sampleFile = sys.argv[1]
outFd = open("calltree.%s" % os.path.basename(sampleFile), "w")
outFd.write("version: 1\n")
outFd.write("creator: %s\n" % sys.argv[0])
outFd.write("# callgrind output file, generated by '%s'\n" % sys.argv)
outFd.write("positions: instr line\n")
parseFile(sampleFile, handleEvent, handleHeader)