#!/bin/bash

# Cache Characteristics Analyzer
# This script displays detailed information about your CPU cache hierarchy

echo "=== CPU Cache Characteristics ==="
echo ""

# Get CPU model name
if [ -f /proc/cpuinfo ]; then
    CPU_MODEL=$(grep "model name" /proc/cpuinfo | head -1 | cut -d ':' -f 2 | sed 's/^[ \t]*//')
    echo "CPU Model: $CPU_MODEL"
    echo ""
fi

# Get number of CPU cores
NUM_CPUS=$(grep -c ^processor /proc/cpuinfo)
echo "Number of CPUs: $NUM_CPUS"
echo ""

# Function to convert size to bytes
convert_to_bytes() {
    local size=$1
    if [[ $size == *K ]]; then
        echo $((${size%K} * 1024))
    elif [[ $size == *M ]]; then
        echo $((${size%M} * 1024 * 1024))
    else
        echo $size
    fi
}

# Check if cache info is available
if [ ! -d /sys/devices/system/cpu/cpu0/cache ]; then
    echo "Cache information not available in sysfs"
    exit 1
fi

# Function to get bits required for mapping
bits_for_mapping() {
    local ways=$1
    local size=$2
    local line_size=$3
    
    # Calculate sets
    local sets=$((size / (ways * line_size)))
    
    # Calculate bits for offset (within cache line)
    local offset_bits=$(awk "BEGIN {print int(log($line_size)/log(2))}")
    
    # Calculate bits for set index
    local set_bits=$(awk "BEGIN {print int(log($sets)/log(2))}")
    
    echo "    - Cache line size: $line_size bytes"
    echo "    - Number of sets: $sets"
    echo "    - Offset bits: $offset_bits (bits 0-$((offset_bits-1)))"
    echo "    - Set index bits: $set_bits (bits $offset_bits-$((offset_bits+set_bits-1)))"
    echo "    - Tag bits: $((48-offset_bits-set_bits)) (bits $((offset_bits+set_bits))-47)"
    
    # Calculate and show potential pathological stride
    local stride=$((2**offset_bits * 2**set_bits))
    echo "    - Potential pathological stride: $stride bytes"
}

# Get info for each cache level and type
echo "Cache Hierarchy:"
for CPU in $(seq 0 $((NUM_CPUS-1))); do
    # Only process CPU0 to avoid duplication
    if [ "$CPU" -ne 0 ]; then
        continue
    fi
    
    CPU_DIR="/sys/devices/system/cpu/cpu$CPU/cache"
    if [ ! -d "$CPU_DIR" ]; then
        continue
    fi
    
    for CACHE_INDEX in $(ls -d $CPU_DIR/index* 2>/dev/null | sort); do
        if [ ! -d "$CACHE_INDEX" ]; then
            continue
        fi
        
        LEVEL=$(cat $CACHE_INDEX/level 2>/dev/null)
        TYPE=$(cat $CACHE_INDEX/type 2>/dev/null)
        SIZE=$(cat $CACHE_INDEX/size 2>/dev/null)
        WAYS=$(cat $CACHE_INDEX/ways_of_associativity 2>/dev/null)
        COHERENCY=$(cat $CACHE_INDEX/coherency_line_size 2>/dev/null)
        
        # Convert size to bytes for calculations
        SIZE_BYTES=$(convert_to_bytes $SIZE)
        
        echo "L$LEVEL $TYPE Cache:"
        echo "    - Size: $SIZE ($SIZE_BYTES bytes)"
        echo "    - Associativity: $WAYS-way set associative"
        
        # Calculate and show mapping bits
        bits_for_mapping $WAYS $SIZE_BYTES $COHERENCY
        echo ""
    done
    break
done
