feat(profiling): Add CPU and MEM profiling
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RuntimeStats struct {
|
||||
TotalAlloc uint64
|
||||
Mallocs uint64
|
||||
NumGC uint32
|
||||
AllocDelta uint64
|
||||
MallocsDelta uint64
|
||||
GCDelta uint32
|
||||
}
|
||||
|
||||
type RuntimeCollector struct {
|
||||
startStats runtime.MemStats
|
||||
memPath string
|
||||
}
|
||||
|
||||
func NewRuntimeCollector(memPath string) *RuntimeCollector {
|
||||
var stats runtime.MemStats
|
||||
runtime.ReadMemStats(&stats)
|
||||
|
||||
return &RuntimeCollector{
|
||||
startStats: stats,
|
||||
memPath: memPath,
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *RuntimeCollector) Collect() RuntimeStats {
|
||||
var current runtime.MemStats
|
||||
runtime.ReadMemStats(¤t)
|
||||
|
||||
return RuntimeStats{
|
||||
TotalAlloc: current.TotalAlloc,
|
||||
Mallocs: current.Mallocs,
|
||||
NumGC: current.NumGC,
|
||||
AllocDelta: current.TotalAlloc - rc.startStats.TotalAlloc,
|
||||
MallocsDelta: current.Mallocs - rc.startStats.Mallocs,
|
||||
GCDelta: current.NumGC - rc.startStats.NumGC,
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *RuntimeCollector) WriteStats() error {
|
||||
stats := rc.Collect()
|
||||
timestamp := time.Now().Format(time.RFC3339Nano)
|
||||
|
||||
// Check if file exists
|
||||
fileExists := false
|
||||
if _, err := os.Stat(rc.memPath); err == nil {
|
||||
fileExists = true
|
||||
}
|
||||
|
||||
// Open in append mode
|
||||
file, err := os.OpenFile(rc.memPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open mem.csv: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
writer := csv.NewWriter(file)
|
||||
|
||||
// Write header if new file
|
||||
if !fileExists {
|
||||
header := []string{
|
||||
"timestamp", "total_alloc_bytes", "mallocs", "gc_cycles",
|
||||
"alloc_delta", "mallocs_delta", "gc_delta",
|
||||
}
|
||||
if err := writer.Write(header); err != nil {
|
||||
return fmt.Errorf("failed to write mem.csv header: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Write data row
|
||||
row := []string{
|
||||
timestamp,
|
||||
fmt.Sprintf("%d", stats.TotalAlloc),
|
||||
fmt.Sprintf("%d", stats.Mallocs),
|
||||
fmt.Sprintf("%d", stats.NumGC),
|
||||
fmt.Sprintf("%d", stats.AllocDelta),
|
||||
fmt.Sprintf("%d", stats.MallocsDelta),
|
||||
fmt.Sprintf("%d", stats.GCDelta),
|
||||
}
|
||||
if err := writer.Write(row); err != nil {
|
||||
return fmt.Errorf("failed to write mem.csv row: %w", err)
|
||||
}
|
||||
|
||||
writer.Flush()
|
||||
return writer.Error()
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Exit on error
|
||||
set -e
|
||||
|
||||
# Default values
|
||||
NETNS_NAME="myapp"
|
||||
VETH_HOST="veth0"
|
||||
VETH_NS="veth1"
|
||||
HOST_IP="192.168.100.1"
|
||||
NS_IP="192.168.100.2"
|
||||
SUBNET="192.168.100.0/24"
|
||||
PHYSICAL_IF="eth0"
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-n|--namespace)
|
||||
NETNS_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p|--physical-if)
|
||||
PHYSICAL_IF="$2"
|
||||
shift 2
|
||||
;;
|
||||
--help)
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -n, --namespace NAME Namespace name (default: myapp)"
|
||||
echo " -p, --physical-if NAME Physical interface (default: eth0)"
|
||||
echo " --help Show this help"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
echo "Use --help for usage information"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "Configuration:"
|
||||
echo " Namespace: $NETNS_NAME"
|
||||
echo " Physical interface: $PHYSICAL_IF"
|
||||
echo ""
|
||||
|
||||
echo "Creating network namespace: $NETNS_NAME"
|
||||
sudo ip netns add $NETNS_NAME
|
||||
|
||||
echo "Creating veth pair: $VETH_HOST <-> $VETH_NS"
|
||||
sudo ip link add $VETH_HOST type veth peer name $VETH_NS
|
||||
|
||||
echo "Moving $VETH_NS into namespace"
|
||||
sudo ip link set $VETH_NS netns $NETNS_NAME
|
||||
|
||||
echo "Configuring host side ($VETH_HOST)"
|
||||
sudo ip addr add $HOST_IP/24 dev $VETH_HOST
|
||||
sudo ip link set $VETH_HOST up
|
||||
|
||||
echo "Configuring namespace side ($VETH_NS)"
|
||||
sudo ip netns exec $NETNS_NAME ip addr add $NS_IP/24 dev $VETH_NS
|
||||
sudo ip netns exec $NETNS_NAME ip link set $VETH_NS up
|
||||
sudo ip netns exec $NETNS_NAME ip link set lo up
|
||||
sudo ip netns exec $NETNS_NAME ip route add default via $HOST_IP
|
||||
|
||||
echo "Enabling IP forwarding"
|
||||
sudo sysctl -w net.ipv4.ip_forward=1
|
||||
|
||||
echo "Disabling IPv6"
|
||||
sudo ip netns exec $NETNS_NAME sysctl -w net.ipv6.conf.all.disable_ipv6=1
|
||||
|
||||
echo "Setting up NAT"
|
||||
sudo iptables -t nat -A POSTROUTING -s $SUBNET -o $PHYSICAL_IF -j MASQUERADE
|
||||
|
||||
echo "Setting up forwarding rules"
|
||||
sudo iptables -I FORWARD -i $VETH_HOST -o $PHYSICAL_IF -j ACCEPT
|
||||
sudo iptables -I FORWARD -i $PHYSICAL_IF -o $VETH_HOST -j ACCEPT
|
||||
|
||||
echo ""
|
||||
echo "Done! Network namespace '$NETNS_NAME' is ready."
|
||||
echo ""
|
||||
echo "To run your app in the namespace:"
|
||||
echo " sudo ip netns exec $NETNS_NAME ./your_app"
|
||||
echo ""
|
||||
echo "To capture traffic:"
|
||||
echo " sudo tshark -i $VETH_HOST -w app.pcap"
|
||||
Reference in New Issue
Block a user