Static analysis tool for kernel function call stack analysis and lock context checking.
- Function Call Stack Tracing: Static analysis of function call relationships with upward caller tracing
- Lock Context Analysis: Check if function calls are protected by specified locks
- Cscope-based: Fast querying using cscope database
- Python 3.12+
- cscope tool
- Pre-built cscope database
# Install dependencies
uv sync
# Development installation
uv sync --group devBuild a standalone binary executable:
# Install build dependencies
make install-dev
# Build binary executable
make binary
# Test the binary
./dist/lock-trace --help
# Install binary system-wide (optional)
make install-binaryThe project includes a comprehensive Makefile with the following targets:
# Development
make install # Install dependencies
make install-dev # Install development dependencies
make test # Run tests
make lint # Run code linting
make format # Format code
# Binary compilation
make binary # Build standalone binary
make binary-portable # Build portable binary with dependencies
make binary-debug # Build debug binary
# Installation
make install-binary # Install binary to /usr/local/bin
make uninstall-binary # Remove installed binary
# Cleaning
make clean # Clean build artifacts
make clean-all # Clean all generated files
# Utilities
make help # Show all available targets
make version # Show version information
make check-deps # Check system dependenciesBuild cscope database in kernel source directory:
# Generate file list (optional)
find . -name "*.c" -o -name "*.h" > cscope.files
# Build database
cscope -RkbqThe tool supports flexible configuration for different project layouts:
- Default: cscope.out and source code in current directory
- Custom database directory: Use
-d /path/to/database - Custom cscope.out file: Use
-f /path/to/cscope.out - Custom source directory: Use
-s /path/to/source
Examples:
# Default: everything in current directory
lock-trace callers schedule
# Custom database directory
lock-trace -d /path/to/kernel callers schedule
# Custom paths (useful for complex project structures)
lock-trace -f /data/cscope.out -s /code/linux callers scheduleAll analysis commands (callers, callees, lock-check, lock-context, unprotected) support three display modes:
- Default (no flags): Shows unique call chains in flat list format, removing duplicate paths for cleaner output
- Tree mode (
--tree,-t): Shows unique call chains in tree structure format for better visualization - Verbose mode (
--verbose,-v): Shows all paths including duplicates (original behavior)
These options can be combined with any command for consistent analysis across different tools.
# See who calls the schedule function (default: unique call chains)
lock-trace callers schedule --max-depth 5
# Show as tree structure
lock-trace callers schedule --tree
# Show all paths including duplicates
lock-trace callers schedule --verbose
# Specify cscope database path
lock-trace -d /path/to/kernel/source callers schedule
# Use custom cscope.out file and source directory
lock-trace -f /path/to/cscope.out -s /path/to/source callers schedule# See what functions kmalloc calls (default: unique call chains)
lock-trace callees kmalloc --max-depth 3
# Show as tree structure
lock-trace callees kmalloc --tree
# Show all paths including duplicates
lock-trace callees kmalloc --verbose# Check if my_function is called under spinlock protection (default: unique call chains)
lock-trace lock-check my_function spin
# Show as tree structure
lock-trace lock-check my_function rcu --tree
# Show all paths including duplicates
lock-trace lock-check my_function mutex --verbose# Analyze lock context of my_function (default: unique call chains)
lock-trace lock-context my_function
# Track specific locks only
lock-trace lock-context my_function --locks spin,rcu
# Show as tree structure
lock-trace lock-context my_function --tree
# Show all paths including duplicates
lock-trace lock-context my_function --verbose# Find my_function calls without required lock protection (default: unique call chains)
lock-trace unprotected my_function --required-locks spin,mutex
# Show as tree structure
lock-trace unprotected my_function --required-locks rcu --tree
# Show all paths including duplicates
lock-trace unprotected my_function --required-locks spin,rtnl --verbose# Get function call statistics
lock-trace stats my_function# Run tests
make test
# Code formatting
make format
# Build binary package
make binary
# Build Python package
make package
# Run all CI checks
make ci# Run tests
uv run pytest
# Code formatting
uv run black .
uv run ruff check .
# Build Python package
uv build- Spinlock:
spin_lock(),spin_unlock(),spin_lock_irq(), etc. - Mutex:
mutex_lock(),mutex_unlock(), etc. - RW Lock:
read_lock(),write_lock(),read_unlock(),write_unlock(), etc. - RCU:
rcu_read_lock(),rcu_read_unlock(), etc.
Unique call chains to function 'schedule':
==================================================
- init_task → kernel_thread → schedule
- kthreadd → kthread_create → schedule
- worker_thread → process_one_work → schedule
Unique call chains found: 3
Call tree to function 'schedule':
==================================================
init_task
└── kernel_thread
└── schedule
kthreadd
└── kthread_create
└── schedule
worker_thread
└── process_one_work
└── schedule
Unique call chains found: 3
All call paths to function 'schedule':
==================================================
1: init_task → kernel_thread → schedule
2: init_task → kernel_thread → schedule (duplicate)
3: kthreadd → kthread_create → schedule
4: worker_thread → process_one_work → schedule
5: worker_thread → process_one_work → schedule (duplicate)
Total paths found: 5
Lock protection analysis for function 'my_function' with lock 'my_lock':
======================================================================
Summary: 2/3 paths have lock protection
✓ PROTECTED: caller1 → my_function
✗ UNPROTECTED: caller2 → my_function
✓ PROTECTED: caller3 → lock_func → my_function
Lock protection analysis for function 'my_function' with lock 'my_lock':
======================================================================
Summary: 2/3 paths have lock protection
Protection status tree:
caller1
└── my_function [✓ PROTECTED]
caller2
└── my_function [✗ UNPROTECTED]
caller3
└── lock_func
└── my_function [✓ PROTECTED]
Lock context analysis for function 'my_function':
======================================================================
1: caller1 → my_function
Held locks: spin_lock_bh
Lock operations:
acquire spin_lock_bh (spinlock) in caller1
2: caller2 → my_function
Held locks: None
Call chains found: 2
Lock context analysis for function 'my_function':
======================================================================
Lock context tree:
caller1
└── my_function
caller2
└── my_function
Lock context details:
1: Held locks: spin_lock_bh
2: Held locks: None
Lock-trace supports an interactive shell mode that allows for easier exploration and iterative analysis of call stacks and lock contexts.
# Enter interactive mode
lock-trace -i
# Interactive mode with initial configuration
lock-trace -i -m 5 -d ~/kernel/build
# Execute command then enter interactive mode
lock-trace -i callers schedule
# Full example with initial configuration and command
lock-trace -i -m 2 -d ~/git/iproute2 callers ip_addr_listOnce in interactive mode, you can use all analysis commands with additional features:
lock-trace> callers schedule
lock-trace> callees kmalloc
lock-trace> lock-check my_function spin
lock-trace> lock-context my_function
lock-trace> lock-context my_function spin,mutex
lock-trace> unprotected my_function spin
lock-trace> stats my_functionlock-trace> config # Show current configuration
lock-trace> set max-depth 5 # Change max depth
lock-trace> set tree true # Enable tree mode
lock-trace> set verbose false # Disable verbose mode
lock-trace> set database-path /path/to/db # Change database pathlock-trace> track # Show all recent results with indices
lock-trace> track 1 3 5 # Show specific results by indexInteractive mode includes powerful stack filtering capabilities:
# Results are automatically numbered for easy reference
lock-trace> callers ip_addr_list
Unique call chains to function 'ip_addr_list':
==================================================
1. do_iplink → ipaddr_list_link → ipaddr_list_flush_or_save → ip_addr_list
2. iplink_usage → do_ipaddr → ipaddr_list_flush_or_save → ip_addr_list
3. main → do_ipaddr → ip_addr_list
4. another_function → some_call → ip_addr_list
# Exclude specific stacks by number
lock-trace> exclude stack 2,3
Added stack exclusions: 2, 3
# Re-run command to see filtered results
lock-trace> callers ip_addr_list
Unique call chains to function 'ip_addr_list':
==================================================
1. do_iplink → ipaddr_list_link → ipaddr_list_flush_or_save → ip_addr_list
4. another_function → some_call → ip_addr_list
Filtered results: 2 stacks shown (excluded: 2, 3)
# Include only specific stacks
lock-trace> include stack 1,4
Added stack inclusions: 1, 4
Note: Only included stacks will be shown
# Clear all filters
lock-trace> clear stacks # Clear stack filters
lock-trace> clear all # Clear all filters# Function and directory filtering
lock-trace> exclude functions debug_print,trace_func
lock-trace> exclude directories drivers,fs
lock-trace> include functions debug_print # Remove from exclusions
lock-trace> include directories drivers # Remove from exclusions
# Stack-based filtering
lock-trace> exclude stack 2,3 # Hide specific stacks
lock-trace> include stack 1,4 # Show only specific stackslock-trace> help # Show help
lock-trace> quit # Exit (also: exit, q)- Persistent Configuration: Set parameters once and reuse across multiple commands
- Stack Filtering: Focus on specific call paths using numbered stacks
- Result Tracking: Reference previous results by index
- Iterative Analysis: Gradually refine analysis by adding/removing filters
- Easy Exploration: Quick switching between tree, verbose, and default modes
$ lock-trace -i -m 2 -d ~/git/iproute2 callers ip_addr_list
🔒 Lock-trace Interactive Shell
==================================================
Current Configuration:
Database Path: ~/git/iproute2
Max Depth: 2
Display Mode: Default
Executing initial command: callers ip_addr_list
Unique call chains to function 'ip_addr_list':
==================================================
1. do_iplink → ipaddr_list_link → ipaddr_list_flush_or_save → ip_addr_list
2. iplink_usage → do_ipaddr → ipaddr_list_flush_or_save → ip_addr_list
Unique call chains found: 2
lock-trace> track
Recent results from command: callers ip_addr_list
==================================================
1: do_iplink → ipaddr_list_link → ipaddr_list_flush_or_save → ip_addr_list
2: iplink_usage → do_ipaddr → ipaddr_list_flush_or_save → ip_addr_list
Total: 2 results
lock-trace> exclude stack 2
Added stack exclusions: 2
lock-trace> callers ip_addr_list
Unique call chains to function 'ip_addr_list':
==================================================
1. do_iplink → ipaddr_list_link → ipaddr_list_flush_or_save → ip_addr_list
Filtered results: 1 stacks shown (excluded: 2)
lock-trace> set tree true
Tree mode enabled
lock-trace> callees ip_addr_list
Call tree from function 'ip_addr_list':
==================================================
ip_addr_list
├── print_addrinfo
└── fflush
lock-trace> quit
Goodbye!lock_trace/
├── __init__.py
├── cscope_interface.py # Cscope interface wrapper
├── call_tracer.py # Call stack tracer
├── lock_analyzer.py # Lock context analyzer
├── cli.py # Command-line interface
└── interactive.py # Interactive shell with stack filtering
GPLv3 License