Skip to main content

Visualization & Export Formats

async-inspect provides multiple ways to visualize and export your async execution data. This guide covers all available formats and how to use them effectively.

Overview

FormatBest ForToolsInteractivity
JSONProgrammatic analysis, data pipelinesjq, pandas, custom tools
CSVSpreadsheet analysis, statisticsExcel, Google Sheets
Chrome TraceTimeline visualization, performancechrome://tracing, Perfetto
FlamegraphPerformance hotspots, call stacksSpeedscope, inferno
HTML ReportShareable reports, documentationWeb browser

JSON Export

Basic Usage

use async_inspect::export::JsonExporter;
use async_inspect::prelude::*;

let inspector = Inspector::global();

// Export to file
JsonExporter::export_to_file(&inspector, "data.json")?;

// Or get as string
let json = JsonExporter::export_to_string(&inspector)?;
println!("{}", json);

JSON Structure

The exported JSON contains three main sections:

{
"tasks": [
{
"id": 1,
"name": "fetch_user_data",
"state": "Completed",
"created_at_ms": 12345,
"duration_ms": 234.5,
"poll_count": 3,
"run_time_ms": 45.2,
"parent_id": null
}
],
"events": [
{
"event_id": 0,
"task_id": 1,
"timestamp_ms": 12345,
"kind": "TaskSpawned",
"details": "name=fetch_user_data, parent=None, location=Some(...)"
}
],
"metadata": {
"version": "0.0.1",
"timestamp": "2025-01-23T10:30:00Z",
"total_tasks": 10,
"total_events": 45,
"duration_ms": 567.8
}
}

Analysis with jq

# Pretty print
jq . data.json

# List all task names
jq '.tasks[].name' data.json

# Find slowest task
jq '.tasks | sort_by(.duration_ms) | reverse | .[0]' data.json

# Calculate average duration
jq '.tasks | map(.duration_ms) | add / length' data.json

# Filter tasks by name pattern
jq '.tasks[] | select(.name | contains("fetch"))' data.json

# Count events by kind
jq '.events | group_by(.kind) | map({kind: .[0].kind, count: length})' data.json

Analysis with Python

import pandas as pd
import json

# Load data
with open('data.json') as f:
data = json.load(f)

# Create DataFrames
tasks = pd.DataFrame(data['tasks'])
events = pd.DataFrame(data['events'])

# Statistical summary
print(tasks.describe())

# Find bottlenecks (tasks with low efficiency)
tasks['efficiency'] = tasks['run_time_ms'] / tasks['duration_ms']
bottlenecks = tasks[tasks['efficiency'] < 0.3]
print(bottlenecks[['name', 'duration_ms', 'efficiency']])

# Event timeline analysis
events['timestamp_s'] = events['timestamp_ms'] / 1000
event_counts = events.groupby('kind').size()
print(event_counts)

# Plot duration distribution
tasks['duration_ms'].hist(bins=20)
plt.xlabel('Duration (ms)')
plt.ylabel('Count')
plt.title('Task Duration Distribution')
plt.show()

CSV Export

Basic Usage

use async_inspect::export::CsvExporter;
use async_inspect::prelude::*;

let inspector = Inspector::global();

// Export tasks
CsvExporter::export_tasks_to_file(&inspector, "tasks.csv")?;

// Export events
CsvExporter::export_events_to_file(&inspector, "events.csv")?;

CSV Format

tasks.csv columns:

  • id: Unique task identifier
  • name: Task name (function name)
  • state: Current state (Running, Blocked, Completed, etc.)
  • created_at_ms: Creation timestamp
  • duration_ms: Total duration
  • poll_count: Number of times polled
  • run_time_ms: Actual running time (not blocked)
  • parent_id: Parent task ID (if spawned from another task)

events.csv columns:

  • event_id: Event identifier
  • task_id: Associated task ID
  • timestamp_ms: Event timestamp
  • kind: Event type (TaskSpawned, PollStarted, AwaitEnded, etc.)
  • details: Additional event-specific information

Analysis with Spreadsheets

  1. Open tasks.csv in Excel/Google Sheets
  2. Create pivot tables for analysis:
    • Average duration by task name
    • Poll count distribution
    • Tasks by state
  3. Add calculated columns:
    • efficiency = run_time_ms / duration_ms
    • avg_poll_time = duration_ms / poll_count
  4. Create visualizations:
    • Duration histogram
    • Timeline chart
    • State distribution pie chart

Chrome Trace Event Format

Basic Usage

use async_inspect::export::ChromeTraceExporter;
use async_inspect::prelude::*;

let inspector = Inspector::global();

ChromeTraceExporter::export_to_file(&inspector, "trace.json")?;

Viewing in Chrome DevTools

  1. Open Chrome or Chromium
  2. Navigate to chrome://tracing
  3. Click "Load" button
  4. Select trace.json
  5. Explore the interactive timeline!

Features:

  • Zoom in/out with mouse wheel
  • Pan with WASD or click-and-drag
  • Click events for details
  • Search for specific tasks
  • Measure time between events

Perfetto provides more advanced analysis features:

  1. Go to https://ui.perfetto.dev/
  2. Click "Open trace file"
  3. Select trace.json

Advanced Features:

  • Thread view: See tasks organized by thread
  • SQL queries: Query trace data with SQL
    SELECT name, dur/1e6 as duration_ms
    FROM slice
    WHERE name LIKE '%fetch%'
    ORDER BY dur DESC
    LIMIT 10;
  • Metrics: Built-in performance metrics
  • Custom tracks: Create custom visualizations
  • Statistical summaries: Percentiles, histograms

Understanding the Timeline

The Chrome Trace format shows:

  • Complete events (X): Tasks and operations with duration
    • Blue bars: Task lifetime
    • Green bars: Poll operations (active work)
    • Yellow bars: Await operations (blocked/waiting)
  • Instant events (i): Point-in-time events
    • Task spawning
    • State changes
    • Inspection points
  • Metadata (M): Thread names and process info

Reading the timeline:

  • Tasks appear as horizontal bars
  • Longer bars = longer duration
  • Stacked bars = nested operations
  • Gaps = waiting/idle time

Flamegraph Export

Basic Usage

use async_inspect::export::{FlamegraphExporter, FlamegraphBuilder};
use async_inspect::prelude::*;

let inspector = Inspector::global();

// Simple export
FlamegraphExporter::export_to_file(&inspector, "flamegraph.txt")?;

// Customized export
FlamegraphBuilder::new()
.include_polls(false) // Hide poll events
.include_awaits(true) // Show await points
.min_duration_ms(10) // Filter operations < 10ms
.export_to_file(&inspector, "flamegraph_filtered.txt")?;

Viewing with Speedscope (Online)

  1. Go to https://www.speedscope.app/
  2. Drop flamegraph.txt onto the page
  3. Explore the flamegraph!

View modes:

  • Time Order: Shows execution over time
  • Left Heavy: Optimizes for reading call stacks
  • Sandwich: Shows both callers and callees

Generating SVG with inferno

# Install inferno
cargo install inferno

# Generate SVG
cat flamegraph.txt | inferno-flamegraph > flamegraph.svg

# Open in browser
open flamegraph.svg # macOS
xdg-open flamegraph.svg # Linux
start flamegraph.svg # Windows

Generating SVG with Rust (Optional)

Enable the flamegraph feature:

[dependencies]
async-inspect = { version = "0.0.1", features = ["flamegraph"] }
#[cfg(feature = "flamegraph")]
{
use async_inspect::export::FlamegraphExporter;
FlamegraphExporter::generate_svg(&inspector, "flamegraph.svg")?;
}

Reading Flamegraphs

Anatomy of a flamegraph:

  • Width: Time spent (wider = more time)
  • Height: Call stack depth (higher = deeper nesting)
  • Color: Usually just for visual distinction

How to use:

  1. Find hotspots: Look for wide plateaus
  2. Identify bottlenecks: Wide sections indicate time-consuming operations
  3. Trace call paths: Follow from bottom to top
  4. Compare tasks: Different root nodes represent different tasks

Common patterns:

  • Wide base, narrow top: Good - work is distributed
  • Narrow base, wide top: Bottleneck in nested call
  • Uniform width: Evenly distributed work
  • Many narrow spikes: Fragmented work (may need batching)

HTML Reports

Basic Usage

use async_inspect::reporting::HtmlReporter;

let reporter = HtmlReporter::global();
reporter.save_to_file("report.html")?;

Features

The HTML report includes:

  • Summary statistics: Task counts, durations, success/failure rates
  • Interactive timeline: Gantt-style visualization with hover details
  • Task list: Sortable table of all tasks
  • Event log: Complete event history
  • State machine view: Visual representation of task states

Sharing Reports

HTML reports are self-contained and can be:

  • Emailed to team members
  • Attached to bug reports
  • Archived for historical analysis
  • Opened without any installation

Comprehensive Example

See examples/export_formats.rs for a complete working example:

cargo run --example export_formats

This generates:

async_inspect_exports/
├── data.json # JSON export
├── tasks.csv # Task metrics
├── events.csv # Event timeline
├── trace.json # Chrome Trace Event Format
├── flamegraph.txt # Flamegraph (folded stacks)
└── flamegraph_filtered.txt # Filtered flamegraph

Best Practices

Choose the Right Format

  • Debugging a hang: Chrome Trace Event Format (Perfetto)
  • Performance optimization: Flamegraph
  • Statistical analysis: CSV + pandas
  • Custom tooling: JSON
  • Sharing with non-technical users: HTML report

Filtering Data

For large traces, filter before exporting:

// Only export long-running tasks
let long_tasks: Vec<_> = inspector.get_all_tasks()
.into_iter()
.filter(|t| t.age().as_secs() > 1)
.collect();

// Create custom export data...

Combining Formats

Use multiple formats for comprehensive analysis:

// Quick overview
HtmlReporter::global().save_to_file("report.html")?;

// Detailed timeline
ChromeTraceExporter::export_to_file(&inspector, "trace.json")?;

// Performance analysis
FlamegraphExporter::export_to_file(&inspector, "flame.txt")?;

// Data analysis
CsvExporter::export_tasks_to_file(&inspector, "tasks.csv")?;

Troubleshooting

Large Trace Files

If trace files are too large:

// Use filtered flamegraph
FlamegraphBuilder::new()
.min_duration_ms(50) // Only operations >= 50ms
.export_to_file(&inspector, "flame.txt")?;

// Sample events for Chrome Trace
// (custom filtering before export)

Chrome Trace Won't Load

  • Check file size (Chrome has limits ~500MB)
  • Try Perfetto UI instead (handles larger files)
  • Use jq to validate JSON structure:
    jq . trace.json > /dev/null

Flamegraph Looks Empty

  • Check if you have any completed tasks
  • Reduce min_duration_ms filter
  • Ensure include_awaits is true for blocked operations

Next Steps