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:
@@ -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."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user