forked from stackimpact/stackimpact-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathblock_profiler.py
More file actions
161 lines (114 loc) · 4.53 KB
/
Copy pathblock_profiler.py
File metadata and controls
161 lines (114 loc) · 4.53 KB
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
from __future__ import division
import os
import sys
import time
import threading
import re
import signal
from ..runtime import min_version, runtime_info
from ..metric import Metric
from ..metric import Breakdown
from ..frame import Frame
if runtime_info.GEVENT:
import gevent
class BlockProfiler(object):
SAMPLING_RATE = 0.05
MAX_TRACEBACK_SIZE = 25 # number of frames
def __init__(self, agent):
self.agent = agent
self.ready = False
self.profile = None
self.profile_lock = threading.Lock()
self.prev_signal_handler = None
self.sampler_active = False
def setup(self):
if self.agent.get_option('block_profiler_disabled'):
return
if not runtime_info.OS_LINUX and not runtime_info.OS_DARWIN:
self.agent.log('CPU profiler is only supported on Linux and OS X.')
return
sample_time = self.SAMPLING_RATE * 1000
main_thread_id = None
if runtime_info.GEVENT:
main_thread_id = gevent._threading.get_ident()
else:
main_thread_id = threading.current_thread().ident
def _sample(signum, signal_frame):
if self.sampler_active:
return
self.sampler_active = True
with self.profile_lock:
try:
self.process_sample(signal_frame, sample_time, main_thread_id)
signal_frame = None
except Exception:
self.agent.exception()
self.sampler_active = False
self.prev_signal_handler = signal.signal(signal.SIGALRM, _sample)
self.ready = True
def destroy(self):
if not self.ready:
return
signal.signal(signal.SIGALRM, self.prev_signal_handler)
def reset(self):
self.profile = Breakdown('Execution call graph', Breakdown.TYPE_CALLGRAPH)
def start_profiler(self):
self.agent.log('Activating block profiler.')
signal.setitimer(signal.ITIMER_REAL, self.SAMPLING_RATE, self.SAMPLING_RATE)
def stop_profiler(self):
signal.setitimer(signal.ITIMER_REAL, 0)
self.agent.log('Deactivating block profiler.')
def build_profile(self, duration):
with self.profile_lock:
self.profile.normalize(duration)
self.profile.propagate()
self.profile.floor()
self.profile.filter(2, 1, float("inf"))
return [{
'category': Metric.CATEGORY_BLOCK_PROFILE,
'name': Metric.NAME_BLOCKING_CALL_TIMES,
'unit': Metric.UNIT_MILLISECOND,
'unit_interval': 1,
'profile': self.profile
}]
def process_sample(self, signal_frame, sample_time, main_thread_id):
if self.profile:
start = time.clock()
current_frames = sys._current_frames()
items = current_frames.items()
for thread_id, thread_frame in items:
if thread_id == main_thread_id:
thread_frame = signal_frame
stack = self.recover_stack(thread_frame)
if stack:
current_node = self.profile
for frame in reversed(stack):
current_node = current_node.find_or_add_child(str(frame))
current_node.set_type(Breakdown.TYPE_CALLSITE)
current_node.increment(sample_time, 1)
thread_id, thread_frame, stack = None, None, None
items = None
current_frames = None
def recover_stack(self, thread_frame):
stack = []
system_only = True
depth = 0
while thread_frame is not None and depth <= self.MAX_TRACEBACK_SIZE:
if thread_frame.f_code and thread_frame.f_code.co_name and thread_frame.f_code.co_filename:
func_name = thread_frame.f_code.co_name
filename = thread_frame.f_code.co_filename
lineno = thread_frame.f_lineno
if self.agent.frame_cache.is_agent_frame(filename):
return None
if not self.agent.frame_cache.is_system_frame(filename):
system_only = False
frame = Frame(func_name, filename, lineno)
stack.append(frame)
thread_frame = thread_frame.f_back
depth += 1
if system_only:
return None
if len(stack) == 0:
return None
else:
return stack