# Quick Start

This document takes you through the basics of using PIPA by measuring the performance of the `perf bench` micro-benchmark as an example.

## Installation

Use the following command to install the package:

```shell
pip install pypipa
```

## 1. Data Collection

First, use the `pipa generate` command to create a data collection script. It will guide you through an interactive setup.

```sh
❯ pipa generate
? Please select the way of workload you want to run. Build a script that collects performance data and start the workloa
d by perf.
? Where do you want to store your data? (Default: ./)
 ./data
? What's the frequency of perf-record? (Default: 999)
 999
? What's the event of perf-record? (Default: {cycles,instructions}:S)
 {cycles,instructions}:S
? Whether to use perf-annotate?
 No
? Do you want to use perf-stat or emon?
 perf-stat
? What's count deltas of perf-stat? (Default: 1000 milliseconds)
 1000
? What's the event of perf-stat?
 cycles:D,instructions:D,ref-cycles:D
? Whether to use taskset?
 Yes
? Which cores do you want to use? (Default: 0-15)
 0-15
? What's the command of workload?
 perf bench futex hash
Shell script generated successfully.
Please check the script in ./data/pipa-run.sh
Note that you may need to modify the script to meet your requirements. The core list is generated according to the
machine which runs this script now. pipa-run.sh is more suitable for reproducible workloads such as benchmark.
```

This will generate a script named `pipa-run.sh` in the `./data` directory. The generated script will use tools like `perf` and `sar` to collect detailed performance data while running the specified workload.

After the script is generated, execute it and redirect the output to a log file. Note that `sudo` might be required for `perf` to access performance counters.

In this example, we use the `perf bench futex hash` as the observed workload, which is a micro benchmark that comes with perf. As long as perf is installed successfully, you don't need to install any dependencies to run this example.

If all goes well, you will get the following script, in the `data/pipa-run.sh`.

```sh
#!/bin/bash
# The script generated by PIPA-TREE is used to collect performance data.
# Please check whether it meets expectations before running.
# ZJU SPAIL(https://github.com/ZJU-SPAIL) reserves all rights.
# Generated at 2025-09-16T12:45:51.050762

# Check if sar and perf are available
if ! command -v sar &> /dev/null; then
echo "sar command not found. Please install sar."
exit 1
fi

if ! command -v perf &> /dev/null; then
echo "perf command not found. Please install perf."
exit 1
fi

WORKSPACE=./data
# Check if $workspace exists
if [ -d "$workspace" ]; then
  # Check if perf-stat.csv or sar.dat exists in $workspace
  if [ -f "$workspace/perf-stat.csv" ] || [ -f "$workspace/sar.dat" ]; then
    # Move $workspace to $workspace_old
    mv "$workspace" "${workspace}_old"
  fi
fi
mkdir -p $WORKSPACE

ps -aux -ef --forest --sort=-%cpu > $WORKSPACE/ps.txt
perf record -e '{cycles,instructions}:S' -g -a -F 999 -o $WORKSPACE/perf.data /usr/bin/taskset -c 0-15 perf bench futex hash
perf script -i $WORKSPACE/perf.data -I --header > $WORKSPACE/perf.script
perf report -i $WORKSPACE/perf.data -I --header > $WORKSPACE/perf.report
perf buildid-list -i $WORKSPACE/perf.data > $WORKSPACE/perf.buildid

sar -o $WORKSPACE/sar.dat 1 >/dev/null 2>&1 &
sar_pid=$!
perf stat -e cycles:D,instructions:D,ref-cycles:D -C 0-15 -A -x , -I 1000 -o ./data/perf-stat.csv /usr/bin/taskset -c 0-15 perf bench futex hash
kill -9 $sar_pid
LC_ALL='C' sar -A -f $WORKSPACE/sar.dat >$WORKSPACE/sar.txt

DST="./data/config"
mkdir -p ./data/config
if [[ $(id -u) -eq 0 ]]; then
    # User is root, run dmidecode directly
    dmidecode >$DST/dmidecode.txt
else
    echo "You need to be root to run dmidecode, skipping..."
fi

if command -v lspci &>/dev/null; then
    lspci >"$DST/pci_devices.txt"
    echo "PCI devices exported to $DST/pci_devices.txt"
fi

if command -v lsusb &>/dev/null; then
    lsusb >"$DST/usb_devices.txt"
    echo "USB devices exported to $DST/usb_devices.txt"
fi

if command -v lsblk &>/dev/null; then
    lsblk >"$DST/block_devices.txt"
    echo "Block devices exported to $DST/block_devices.txt"
fi

if command -v lshw &>/dev/null; then
    lshw >"$DST/hardware.txt"
    echo "Hardware information exported to $DST/hardware.txt"
fi

if command -v lscpu &>/dev/null; then
    lscpu >"$DST/cpu.txt"
    echo "CPU information exported to $DST/cpu.txt"
    lscpu -a --extended >"$DST/cpu-extended.txt"
    echo "Extended CPU information exported to $DST/cpu-extended.txt"
fi

if command -v lsmod &>/dev/null; then
    lsmod >"$DST/modules.txt"
    echo "Kernel modules exported to $DST/modules.txt"
fi

if command -v lsinitrd &>/dev/null; then
    lsinitrd >"$DST/initrd.txt"
    echo "Initrd information exported to $DST/initrd.txt"
fi

if command -v ip &>/dev/null; then
    ip addr >"$DST/ip.txt"
    echo "IP information exported to $DST/ip.txt"
fi

df -h >"$DST/disk_usage.txt"
echo "Disk usage exported to $DST/disk_usage.txt"

cp /proc/meminfo "$DST/meminfo.txt"
echo "Memory information exported to $DST/meminfo.txt"

cp /proc/cpuinfo "$DST/cpuinfo.txt"
echo "CPU information exported to $DST/cpuinfo.txt"

perf list > "$DST/perf-list.txt"
echo "Perf list exported to $DST/perf-list.txt"

ulimit -a > "$DST/ulimit.txt"
echo "Ulimit information exported to $DST/ulimit.txt"

echo "Configuration exported to $DST"


echo 'Performance data collected successfully.'
```

This script will run the workload twice, using `perf record` and` perf stat` + `sar` to make observations and save the corresponding data. In addition, before running, the process data will be checked to determine the noise level, and after running, the system configuration file will be exported.

After the running script is built, you can run it and redirect the results to a file.

```shell
sudo bash ./data/pipa-run.sh > ./data/pipa-run.log
```

This process will create a `data/` directory containing the raw performance files like `perf-stat.csv`, `sar.txt`, and the log file `pipa-run.log`.

## 2. Data Loading & Analysis

Now that we have collected the data, let's analyze it using PIPA in a Python environment, such as a Jupyter Notebook.

### Prepare for Analysis

First, we need to calculate the total number of transactions executed during the benchmark. We can automate this by extracting the necessary information directly from the `pipa-run.log` file.

The `perf bench futex hash` workload is run twice by the default script. We need the result from the **second** run, which corresponds to the `perf stat` collection. The following shell commands will automatically find the correct value.

```bash
# This command finds the last "Averaged" line in the log, extracts the ops/sec value,
# gets the number of CPU cores, and calculates the total transactions.
AVG_OPS_PER_SEC=$(tac data/pipa-run.log | grep -m 1 "Averaged" | awk '{print $2}')
NUM_THREADS=$(grep -m 1 "threads, each operating on" data/pipa-run.log | tail -n 1 | awk '{print $4}')
TOTAL_TRANSACTIONS=$(echo "$AVG_OPS_PER_SEC * $NUM_THREADS * 10" | bc)

echo "Average ops/sec per thread: $AVG_OPS_PER_SEC"
echo "Number of threads: $NUM_THREADS"
echo "Total Transactions (approximated): $TOTAL_TRANSACTIONS"
```

Please use the calculated `TOTAL_TRANSACTIONS` value in the Python code below.

### Load Data and Calculate Metrics

Now, we can use PIPA's `PIPAShuData` class to load all the collected data and compute a comprehensive set of performance metrics.

```python
import os
import re
import pprint
from pipa.service.pipashu import PIPAShuData

# --- Automated Calculation of Total Transactions ---
# You can also run the shell commands above and paste the number here.
# This Python code does the same thing automatically.

log_path = './data/pipa-run.log'
total_transactions = 0
num_threads = 0

if os.path.exists(log_path):
    with open(log_path, 'r') as f:
        log_content = f.read()

    # Find the last "Averaged X ops/sec" line
    matches = re.findall(r"Averaged ([\d.]+) operations/sec", log_content)
    if matches:
        avg_ops_per_sec = float(matches[-1])

        # Find the number of threads from the last summary
        summary_matches = re.findall(r"Run summary.*? (\d+) threads", log_content)
        if summary_matches:
            num_threads = int(summary_matches[-1])
            run_time_sec = 10  # perf bench futex hash runs for 10 seconds
            total_transactions = int(avg_ops_per_sec * num_threads * run_time_sec)

print(f"Automatically calculated total transactions: {total_transactions}")
print(f"Detected number of threads: {num_threads}")

# --- Load Data with PIPAShuData ---
pipashu = PIPAShuData(
    perf_stat_path="./data/perf-stat.csv",
    sar_path="./data/sar.txt",
    perf_record_path="./data/perf.script",
)

# --- Calculate Comprehensive Metrics ---
if total_transactions > 0 and num_threads > 0:
    threads_list = list(range(num_threads))
    metrics = pipashu.get_metrics(
        num_transactions=total_transactions,
        threads=threads_list
    )

    # Pretty-print the resulting metrics dictionary
    print("\n--- Calculated Performance Metrics ---")
    pprint.pprint(metrics)
```

**Example Output:**

```
Automatically calculated total transactions: 303318880
Detected number of threads: 16
...some warnings...
--- Calculated Performance Metrics ---
{'%commit': 73.48,
 '%disk_util': 2.11,
 '%gnice': 0.0,
 '%guest': 0.0,
 '%idle': 75.84625,
 '%iowait': 1.0037500000000001,
 '%irq': 0.62125,
 '%memused': 29.61,
 '%nice': 0.0,
 '%soft': 0.2325,
 '%steal': 0.0,
 '%sys': 6.7437499999999995,
 '%usr': 15.556249999999999,
 '%util': 24.153750000000002,
 'CPI': np.float64(3.0929526093854363),
 'DEV': 'nvme1n1p3',
 'aqu-sz': 0.37,
 'areq-sz': 26.62,
 'cpu_frequency_mhz': np.float64(3997.2650000000003),
 'cycles': np.int64(748699758097),
 'cycles_per_requests': np.float64(2468.3585739766677),
 'cycles_per_second': np.float64(83119058228.55307),
 'disk_await': 4.56,
 'dkB/s': 1064.17,
 'instructions': np.int64(242066352981),
 'instructions_per_second': np.float64(26873692786.734505),
 'kbactive': 26091988,
 'kbanonpg': 15062933,
 'kbavail': 43644206,
 'kbbuffers': 4564,
 'kbcached': 29528084,
 'kbcommit': 72445737,
 'kbdirty': 31005,
 'kbinact': 19001961,
 'kbkstack': 58810,
 'kbmemfree': 14761028,
 'kbmemused': 19255940,
 'kbpgtbl': 175545,
 'kbslab': 1485131,
 'kbvmused': 518138,
 'path_length': np.float64(798.0589700878495),
 'rkB/s': 1.6,
 'run_time': np.float64(9.007558242999998),
 'throughput': np.float64(33673818.34424627),
 'tps': 81.66,
 'transactions': 303318880,
 'used_threads': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
 'wkB/s': 1107.68}
```

This `metrics` dictionary provides a rich summary of your system's performance during the benchmark, covering everything from high-level throughput and CPI to detailed CPU, memory, and disk utilization from `sar`. You are now ready to use this data for in-depth performance analysis.

## Export perf.data to SQLite via perf script

After installing `pypipa`, the helper CLI `pipa-perf-sqlite` is available on your PATH. You can ask `perf` to run it directly to export `perf.data` into SQLite (and optional CSV) without copying scripts around:

```bash
perf script -i /path/to/perf.data \
  --script=pipa-perf-sqlite \
  /path/to/output/perf.db calls csv=/path/to/output/csv_dir
```

- `calls` implies `callchains` in the original script and generates call-related tables.
- `csv=/path/to/output/csv_dir` is optional; omit it if you only need the SQLite DB.
- Make sure `perf` is installed (e.g., `sudo apt-get install linux-tools-$(uname -r)` on Debian/Ubuntu).

## Upload

PIPA provides two ways to upload your performance data with detailed information. One is to upload in command line interation, and the other is to upload with YAML.

### Upload in cmd interaction manner

Use the following command line to upload data in a cmd interaction manner:

```shell
pipa upload
```

Then

```bash
? What's the name of workload? Kafka
? What's the number of transaction? 10000000
? Where's the data collected by PIPAShu? data
? What are the threads used in the workload? Split by , 0,1,2,3
? What's the used disk device name? dm-2
? What's the hardware configuration (sockets*cores*SMT)? 1*1*1
? What's the software configuration? JDK1.8
? What's the platform? IceLake 8383C
? Any comments?
? What's the PIPAD server address?
? What's the PIPAD server port?
```

### Upload with YAML

Use the following command line to upload data with YAML:

```shell
pipa upload <config_file>
```

PIPA provides a template configuration file [config-upload.yaml](https://github.com/ZJU_SPAIL/pipa/blob/main/asset/config-upload.yaml).

```yaml
# PIPA-Shu Upload Configuration
# Use pipa upload to upload the data to PIPAD server based on this configuration.
# Command Example: pipa upload --config_path=./data/config-upload.yaml
workload: rocksdb
# The name of the workload.
transaction: 7561946
# The number of transactions.
data_location: /path/to/data/collected/by/pipashu
# The location of the data collected by PIPAShu.

cores: [36, 37, 38, 39]
# The numbers of logical cores used in the workload.
dev: sdc
# The used disk device name.

hw_info: 1*4*1
# The hardware configuration (sockets*cores*SMT).
sw_info: RocksDB 7.9.2 build in release mode, debug_level=0, threads_num=16, db_bench with benchmark.sh
# The software configuration.

platform: Intel SPR 4510
# The platform user used.
cpu_frequency_mhz: 2600
# The CPU frequency in MHz.
# Only needed when the platform is Huawei.

comment: "This is a template for the upload configuration."
# Any comments.
pipad_addr: 10.82.77.113
# The PIPAD server address.
pipad_port: 50051
# The PIPAD server port.
```

Data location is the directory of your PIPASHU data. It should at least include:

```bash
data/
├── perf-stat.csv
├── perf.script
└── sar.txt
```

So far, pipa's grafana panel has only been calculated based on data from perf-stat and sar. If perf.script is particularly large, you might consider not importing a perf.script file to speed things up. You just need to replace the `data_location` field with the `perf_stat_path`, `sar_path` field.

BTW, cores list is the cores you want to focus on. To get the list quickly, you can use python list.

```shell
$ python
Python 3.11.5 (main, Sep 11 2023, 13:54:46) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> list(range(32,64))
[32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]
```

## Dump

Before formally uploading PIPASHU data, you can use PIPA dump to dump overview data to a file for double check.

```shell
pipa dump -o <output_file> -c <config_file> -v
```

`-c <config_file>`: PIPA provides a template config file [config-upload.yaml](https://github.com/ZJU_SPAIL/pipa/blob/main/asset/config-upload.yaml).If this parameter is missed, you can dump data in a cmd interaction manner.

`-v`: If this parameter is used, output file will be printed to the screen.
