Added option to view sample data in the web app.

- Fixed an issue with the CDN link for d3-flamegraph
- Added option for verbose logging to help with debugging
This commit is contained in:
Atharva Sawant
2025-11-28 09:11:13 +05:30
parent 3d4c3f7f04
commit fc7214adaf
3 changed files with 188 additions and 16 deletions

View File

@@ -93,6 +93,119 @@ class ProfilerWebServer:
"""Get statistics table data."""
return jsonify(self.analyzer.to_statistics_json())
@self.app.route('/api/sample/flamegraph')
def sample_flamegraph():
"""Get sample flame graph data."""
import os
import json
sample_file = os.path.join(os.path.dirname(__file__), '../tests/sample_flamegraph.json')
if os.path.exists(sample_file):
with open(sample_file, 'r') as f:
return jsonify(json.load(f))
return jsonify({"error": "Sample data not found"}), 404
@self.app.route('/api/sample/statistics')
def sample_statistics():
"""Get sample statistics data."""
import os
import json
sample_file = os.path.join(os.path.dirname(__file__), '../tests/sample_statistics.json')
if os.path.exists(sample_file):
with open(sample_file, 'r') as f:
return jsonify(json.load(f))
return jsonify({"error": "Sample data not found"}), 404
@self.app.route('/api/sample/timeline')
def sample_timeline():
"""Get sample timeline data."""
import os
import json
sample_file = os.path.join(os.path.dirname(__file__), '../tests/sample_timeline.json')
if os.path.exists(sample_file):
with open(sample_file, 'r') as f:
return jsonify(json.load(f))
return jsonify({"error": "Sample data not found"}), 404
@self.app.route('/api/sample/generate')
def generate_sample():
"""Generate sample profiling data on-the-fly."""
import sys
import os
# Add tests directory to path to import sample data generator
tests_dir = os.path.join(os.path.dirname(__file__), '../tests')
if tests_dir not in sys.path:
sys.path.insert(0, tests_dir)
try:
from sample_data_generator import generate_sample_profile_data, FUNCTIONS
# Generate sample records
records = generate_sample_profile_data(num_iterations=20)
# Create temporary analyzer to process the data
temp_analyzer = ProfileAnalyzer()
temp_analyzer.add_records(records)
# Generate all three visualization formats
flamegraph_data = temp_analyzer.to_flamegraph_json()
stats_data = temp_analyzer.to_statistics_json()
timeline_data = temp_analyzer.to_timeline_json()
# Add human-readable function names to flamegraph
def add_names(node):
"""Recursively add function names to flamegraph nodes."""
if 'name' in node and node['name'].startswith('func_0x'):
try:
# Extract address from "func_0x08000100" format
addr = int(node['name'][5:], 16) # Skip "func_"
if addr in FUNCTIONS:
node['name'] = FUNCTIONS[addr]
except ValueError:
pass
if 'children' in node:
for child in node['children']:
add_names(child)
add_names(flamegraph_data)
# Add function names to statistics
logger.debug(f"Processing {len(stats_data)} statistics entries")
logger.debug(f"FUNCTIONS has {len(FUNCTIONS)} entries")
for stat in stats_data:
if stat['name'].startswith('func_0x'):
try:
addr = int(stat['name'][5:], 16) # Skip "func_"
if addr in FUNCTIONS:
old_name = stat['name']
stat['name'] = FUNCTIONS[addr]
logger.debug(f"Replaced {old_name} with {stat['name']}")
else:
logger.debug(f"Address 0x{addr:08x} not in FUNCTIONS")
except ValueError as e:
logger.error(f"ValueError parsing {stat['name']}: {e}")
# Add function names to timeline
for event in timeline_data:
if event['name'].startswith('func_0x'):
try:
addr = int(event['name'][5:], 16) # Skip "func_"
if addr in FUNCTIONS:
event['name'] = FUNCTIONS[addr]
except ValueError:
pass
# Return combined response
return jsonify({
'flamegraph': flamegraph_data,
'statistics': stats_data,
'timeline': timeline_data
})
except Exception as e:
logger.error(f"Error generating sample data: {e}")
return jsonify({"error": str(e)}), 500
def _setup_socketio_handlers(self):
"""Setup SocketIO event handlers."""