11import threading
22from typing import Callable
33
4+ import paramiko
45import requests
56from decorator import decorator
67from sshtunnel import SSHTunnelForwarder
@@ -41,7 +42,8 @@ class Profiler:
4142 DEBUG_PORTS = {
4243 'fts' : 8094 ,
4344 'index' : 9102 ,
44- 'kv' : 9998 , # goxdcr
45+ 'goxdcr' : 9998 ,
46+ 'kv' : 9998 , # will be deprecated in future
4547 'n1ql' : 8093 ,
4648 }
4749
@@ -60,6 +62,12 @@ def __init__(self, cluster_spec: ClusterSpec, test_config: TestConfig):
6062
6163 self .ssh_username , self .ssh_password = cluster_spec .ssh_credentials
6264
65+ self .cluster_spec = cluster_spec
66+
67+ self .profiling_settings = test_config .profiling_settings
68+
69+ self .linux_perf_path = '/opt/couchbase/var/lib/couchbase/logs/'
70+
6371 def new_tunnel (self , host : str , port : int ) -> SSHTunnelForwarder :
6472 return SSHTunnelForwarder (
6573 ssh_address_or_host = host ,
@@ -74,12 +82,59 @@ def save(self, host: str, service: str, profile: str, content: bytes):
7482 with open (fname , 'wb' ) as fh :
7583 fh .write (content )
7684
85+ def linux_perf_profile (self , host : str , fname : str , path : str ):
86+
87+ client = paramiko .SSHClient ()
88+ client .set_missing_host_key_policy (paramiko .AutoAddPolicy ())
89+
90+ try :
91+ client .connect (hostname = host , username = self .ssh_username ,
92+ password = self .ssh_password )
93+
94+ except Exception :
95+ logger .info ('Cannot connect to the "{}" via SSH Server' .format (host ))
96+ exit ()
97+
98+ logger .info ('Capturing linux profile using linux perf record ' )
99+
100+ cmd = 'perf record -a -F {} -g --call-graph {} ' \
101+ '-p $(pgrep memcached) -o {}{} ' \
102+ '-- sleep {}' .format (self .profiling_settings .linux_perf_frequency ,
103+ self .profiling_settings .linux_perf_callgraph ,
104+ path ,
105+ fname ,
106+ self .profiling_settings .linux_perf_profile_duration )
107+ stdin , stdout , stderr = client .exec_command (cmd )
108+ exit_status = stdout .channel .recv_exit_status ()
109+
110+ if exit_status == 0 :
111+ logger .info ("linux perf record: linux perf profile capture completed" )
112+ else :
113+ logger .info ("perf record failed , exit_status : " , exit_status )
114+
115+ client .close ()
116+
77117 def profile (self , host : str , service : str , profile : str ):
78118 logger .info ('Collecting {} profile on {}' .format (profile , host ))
79119
80120 endpoint = self .ENDPOINTS [profile ]
81121 port = self .DEBUG_PORTS [service ]
82122
123+ if self .profiling_settings .linux_perf_profile_flag :
124+ logger .info ('Collecting {} profile on {} using linux perf '
125+ 'reccord' .format (profile , host ))
126+
127+ fname = 'linux_{}_{}_{}_perf.data' .format (host , profile , uhex ()[:4 ])
128+ self .linux_perf_profile (host = host , fname = fname , path = self .linux_perf_path )
129+
130+ else :
131+ logger .info ('Collecting {} profile on {}' .format (profile , host ))
132+
133+ with self .new_tunnel (host , port ) as tunnel :
134+ url = endpoint .format (tunnel .local_bind_port )
135+ response = requests .get (url = url , auth = self .rest .auth )
136+ self .save (host , service , profile , response .content )
137+
83138 with self .new_tunnel (host , port ) as tunnel :
84139 url = endpoint .format (tunnel .local_bind_port )
85140 response = requests .get (url = url , auth = self .rest .auth )
0 commit comments