-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathget_pprof.py
162 lines (136 loc) · 5.77 KB
/
get_pprof.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import requests
import argparse
import json
import os
import subprocess
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Optional, Dict, Any, List
# Constants
# PYROSCOPE_URL = "http://localhost:4040"
PYROSCOPE_URL = 'http://pyroscope-query-frontend.monitoring.svc.cluster.local:4040'
class PyroscopeClient:
def __init__(self, base_url: str):
self.base_url = base_url.rstrip('/')
self.session = requests.Session()
def get_profile(self, service_name: str, pod_name: str, from_time: datetime, until_time: datetime, query_type: str = "memory") -> Dict[str, Any]:
"""Fetch profile data in JSON format from Pyroscope API"""
endpoint = f"{self.base_url}/pyroscope/render"
query = f"{query_type}:inuse_objects:count:space:bytes{{service_name=\"{service_name}\",pod=\"{pod_name}\"}}"
params = {
'query': query,
'from': int(from_time.timestamp() * 1000),
'until': int(until_time.timestamp() * 1000),
'format': 'json',
'aggregation': 'sum'
}
try:
response = self.session.get(endpoint, params=params)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"Error fetching profile data for pod {pod_name}: {e}")
return {}
def get_node_agent_pods() -> List[str]:
"""Get all node-agent pod names in the kubescape namespace"""
try:
cmd = "kubectl get pods -n kubescape --no-headers -o custom-columns=':metadata.name' | grep node-agent"
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
pods = [pod.strip() for pod in result.stdout.split('\n') if pod.strip()]
print(f"Found {len(pods)} node-agent pods: {pods}")
return pods
except subprocess.CalledProcessError as e:
print(f"Error getting node-agent pods: {e}")
return []
def convert_to_speedscope(profile_data: Dict[str, Any], pod_name: str) -> Dict[str, Any]:
"""
Convert Pyroscope JSON format to speedscope format
"""
if not profile_data or 'flamebearer' not in profile_data:
return {}
flamebearer = profile_data['flamebearer']
speedscope = {
"version": "0.0.7",
"shared": {
"frames": [{"name": name} for name in flamebearer['names']]
},
"profiles": [{
"type": "sampled",
"name": f"Memory Profile - {pod_name}",
"unit": "bytes",
"startValue": 0,
"endValue": flamebearer['numTicks'],
"samples": [],
"weights": []
}]
}
current_sample = []
for level in flamebearer['levels']:
for i in range(0, len(level), 4):
if i + 3 >= len(level):
continue
value = level[i + 1]
name_idx = level[i + 3]
if name_idx is not None:
current_sample.append(name_idx)
speedscope['profiles'][0]['samples'].append(list(current_sample))
speedscope['profiles'][0]['weights'].append(value)
return speedscope
def main():
# Read environment variables with defaults
duration_minutes = int(os.getenv('DURATION_TIME', '30'))
base_output_dir = os.getenv('OUTPUT_DIR', 'output')
print(f"Using DURATION_TIME={duration_minutes} minutes")
print(f"Using base output directory: {base_output_dir}")
# Calculate time range
until_time = datetime.now(timezone.utc)
from_time = until_time - timedelta(minutes=duration_minutes)
try:
# Try to create the base output directory
os.makedirs(base_output_dir, exist_ok=True)
except Exception as e:
print(f"Failed to create {base_output_dir}, falling back to 'output': {e}")
base_output_dir = 'output'
os.makedirs(base_output_dir, exist_ok=True)
# Create profiles subdirectory within the base output directory
output_dir = Path(base_output_dir) / "profiles"
try:
output_dir.mkdir(parents=True, exist_ok=True)
print(f"Successfully created profiles directory: {output_dir}")
except Exception as e:
print(f"Error creating profiles directory: {e}")
# Fallback to base output directory
output_dir = Path(base_output_dir)
print(f"Falling back to base directory: {output_dir}")
# Get all node-agent pods
pods = get_node_agent_pods()
if not pods:
print("No node-agent pods found!")
return
# Initialize client
client = PyroscopeClient(PYROSCOPE_URL)
# Process each pod
for pod_name in pods:
print(f"\nProcessing pod: {pod_name}")
# Get profile data
profile_data = client.get_profile("node-agent", pod_name, from_time, until_time)
if profile_data:
# Convert to speedscope format
speedscope_data = convert_to_speedscope(profile_data, pod_name)
if speedscope_data:
# Generate output filename using UTC timestamp
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d_%H-%M-%S_UTC")
output_file = output_dir / f"{pod_name}_{timestamp}.speedscope.json"
# Write to file
try:
with open(output_file, 'w') as f:
json.dump(speedscope_data, f)
print(f"Profile saved to {output_file}")
except Exception as e:
print(f"Error saving profile for {pod_name}: {e}")
else:
print(f"Failed to convert profile data for {pod_name}")
else:
print(f"Failed to fetch profile data for {pod_name}")
if __name__ == "__main__":
main()