import sys
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from collections import defaultdict
# Read the CSV file
def parse_memory_data(csv_data):
# Read only required columns and use dtype specifications for better performance
df = pd.read_csv(
csv_data,
names=["timestamp", "operation", "pointer", "parent", "type", "size"],
dtype={
"timestamp": np.int64,
"operation": str,
"pointer": str,
"parent": str,
"type": str,
"size": np.float64,
},
usecols=["timestamp", "operation", "pointer", "parent", "type", "size"],
)
# Filter out entries with parents early
df = df[df["parent"].apply(lambda x: int(x, 16) == 0)]
# Convert size to MB once
df["size"] = df["size"] / (1024 * 1024)
# Sort by timestamp
df = df.sort_values("timestamp")
# Initialize data structures
memory_by_type = defaultdict(lambda: defaultdict(float))
memory_values_by_type = defaultdict(list)
# Process in chunks for better memory usage
chunk_size = 10000
for chunk_start in range(0, len(df), chunk_size):
chunk = df.iloc[chunk_start : chunk_start + chunk_size]
for _, row in chunk.iterrows():
mem_type = row["type"]
pointer = row["pointer"]
timestamp = row["timestamp"] / 1000000000.0 # Convert to seconds
# Update memory tracking
if row["operation"] == "alloc":
memory_by_type[mem_type][pointer] = row["size"]
elif pointer in memory_by_type[mem_type]:
del memory_by_type[mem_type][pointer]
# Calculate metrics for this timestamp
current_memory = sum(memory_by_type[mem_type].values())
buffer_count = len(memory_by_type[mem_type])
extra_info = ""
if row["operation"] == "alloc" or row["operation"] == "free":
extra_info = f"{row['operation']}(0x{pointer}={row['size']:.3f}MB)"
memory_values_by_type[mem_type].append(
(timestamp, current_memory, buffer_count, extra_info)
)
return memory_values_by_type
def create_memory_graph(csv_data):
memory_values_by_type = parse_memory_data(csv_data)
fig = go.Figure()
colors = {
"SystemMemory": "blue",
"GPU Memory": "red",
"GLBuffer": "orange",
"GLMemoryPBO": "darkorange",
"GLRenderBuffer": "darkturquoise",
"gst.cuda.memory": "green",
"DMABuf": "purple",
"fd": "magenta",
"shm": "darkviolet",
}
# Add traces for each memory type
for mem_type, values in memory_values_by_type.items():
color = colors.get(mem_type, "gray") # Default to gray for unknown types
decimation_factor = max(1, len(values) // 10000)
# Decimate the data points
decimated_values = values[::decimation_factor]
decimated_timestamps = [v[0] for v in decimated_values]
memory_values = [v[1] for v in decimated_values] # Extract memory values
buffer_counts = [v[2] for v in decimated_values] # Extract buffer counts
extra_info = [v[3] for v in decimated_values] # Extract extra info
extra_info = [v[2] for v in decimated_values] # Extract buffer counts
# Optimize hover text - only create it for visible points
hover_text = [
f"{mem_type}:
• Size: {mem:.2f}MB
• Buffers: {count}"
for mem, count, _ in zip(memory_values, buffer_counts, extra_info)
]
fig.add_trace(
go.Scatter(
x=decimated_timestamps,
y=memory_values,
mode="lines", # Removed markers for better performance
name=f"{mem_type}",
line=dict(color=color, width=2),
hovertext=hover_text,
hoverinfo="text",
hovertemplate="%{hovertext}", # Optimized hover template
)
)
# Customize the layout
fig.update_layout(
title="GStreamer Memory Usage Over Time",
xaxis_title="Timestamp",
yaxis_title="Memory Usage (MB)",
hovermode="x unified",
showlegend=True,
template="plotly_white",
yaxis=dict(rangemode="nonnegative"), # Ensure y-axis doesn't go below 0
)
return fig
if __name__ == "__main__":
csv_data = sys.argv[1]
fig = create_memory_graph(csv_data)
fig.show()