Complete guide for performance profiling and structured logging in S.T.A.L.K.E.R. Anomaly
- What is Anomaly DevTools?
- Key Features
- Installation
- Quick Start
- Use Cases
- Features in Detail
- What's New in v1.3.5
- What's New in v1.3.0
- What's New in v1.2.2
- Important Notes
- Files Included
- Advanced Usage
- Contributing
DevTools is a standalone developer toolkit that lets you profile any Lua mod in seconds without touching a single line of code. Want to know why your mod is slow? DevTools tells you exactly which functions are eating your FPS—in real-time, with a clean ImGui interface.
Perfect for modders who want to optimize their work, debug performance issues, and understand game behavior without invasive code instrumentation.
- No code modifications needed — just load your mod normally
- Automatically discovers all Lua modules in
_G - Start profiling with one click
- Works with any mod, any script
- Track calls, execution time (avg/median/min/max/total), and more
- Live updates as functions execute
- Sort by any metric instantly
- Color-coded by module for easy tracking
- Sleek, integrated control panel (press F11 to open)
- Profiler tab: View live function statistics
- Logs tab: Structured logging with filters and severity levels
- Module browser: Select what to profile (optional)
- Resizable, moveable windows
- CSV Export: Dump statistics for spreadsheet analysis
- Flamegraph Export: Visualize call stacks in SpeedScope, Inferno, or FlameGraph.pl
- JSONL Live Streaming (Experimental): Real-time streaming export of function call data for external post-processing tools
- Perfect for identifying bottlenecks and optimization targets
- Profiling Presets: Save/load different module selections for different scenarios
- Timed Profiling: Auto-stop after a duration (1-300 seconds) with automatic CSV/flamegraph export
- Minimal Mode: Hide stats table for maximum FPS during collection
- Profile on Load: Auto-start profiling when game loads or transitions
- LuaBind Class Method Support: Profile class methods like
utils_ui.UICellItem.Update - Self-Time Tracking: Track time spent in functions excluding child calls (total, avg, median, min, max)
- Parent Function Tracking: See caller relationships and most common callers for each function
- Call Graph Export: Export call graphs in DOT format for visualization in Graphviz
- Custom categories with colors and quick filters
- Severity levels: DEBUG, INFO, WARN, ERROR
- File output for post-game analysis
- Clean, organized log viewer in-game
- Download the latest release
- In MO2, click "Install Mod"
- Select the zip file
- Activate and load the game
- Extract the zip file
- Copy the
gamedatafolder contents to your Anomalygamedatafolder - Load the game
- Install the mod ✓
- Load the game ✓
- Press F11 to open ImGui menu
- Navigate to DevTools → Profiler
- Enable profiling and watch the stats roll in
That's it. No code changes. No registration. No hassle.
"My mod is causing FPS drops" → Use DevTools to identify which functions are slow
"I need to optimize my script" → Export to flamegraph, visualize the call stack, find the bottleneck
"Which mods are conflicting?" → Profile each mod separately, compare execution times and call counts
"I want to understand how game systems work" → Profile vanilla callbacks and systems to see execution flow and timing
"I'm debugging a performance regression" → Save profiling presets, compare before/after with CSV exports
| Control | Purpose |
|---|---|
| Enable Profiling | Checkbox to turn profiling on/off |
| Profile on Load | Auto-start profiling when game loads or transitions |
| Timed Profiling | Duration spinner (1-300 seconds) — auto-stops after time expires, auto-exports CSV and flamegraph |
| Minimal Mode | Hide stats table for maximum FPS during collection |
| Module Browser | Expand to select/deselect modules (optional — auto-discovery is default) |
| Columns | Toggle individual columns: Calls/Avg/Median/Min/Max/Total/Self-Time metrics |
| Row Limit | Display All/50/100/200 rows (default: 100 for best performance) |
| Sort | Click column headers to sort by any metric |
- Calls: Number of times the function was called
- Avg (ms): Average execution time per call
- Median (ms): Median execution time (represents typical performance better than average)
- Min (ms): Fastest single call
- Max (ms): Slowest single call
- Total (ms): Total cumulative time across all calls
- Self Total (ms): Total time spent in function excluding child calls
- Self Avg (ms): Average self-time per call
- Self Median (ms): Median self-time per call
- Self Min (ms): Minimum self-time recorded
- Self Max (ms): Maximum self-time recorded
CSV Export
- Dumps all statistics to a
.csvfile - Perfect for spreadsheet analysis and trend tracking
- Includes all metrics (Calls, Avg, Median, Min, Max, Total)
Flamegraph Export
- Exports call stack data in collapsed stacks format (
.folded) - Visualize in SpeedScope (recommended, no installation needed)
- Or use FlameGraph.pl or Inferno
- Shows which call stacks consume the most time
Call Graph Export
- Exports function call relationships in DOT format (
.dot) - Visualize in Graphviz or online DOT viewers
- Shows module dependencies and call patterns
- Helps understand code architecture and flow
JSONL Live Streaming (Experimental)
- Streams individual function calls in real-time to a JSONL file (
.jsonl) - Each line is a JSON object containing function call data
- Uses buffered I/O for efficient disk writes
- Suitable for external post-processing and custom analysis tools
- UI controls for starting/stopping streams and exporting summaries
- Severity Levels: DEBUG (blue), INFO (green), WARN (yellow), ERROR (red)
- Custom Categories: Organize logs by system/module
- Filters: Show/hide logs by category
- File Output: Logs also written to disk for post-game review
- Clear Logs: Wipe current log history
- 📡 JSONL Live Streaming (Experimental): New real-time streaming export of function call data in JSONL format for post-processing and external tool integration
- 🎛️ JSONL Stream Controls (Experimental): UI controls for starting/stopping JSONL streams and exporting stream summaries
- 🏗️ Shared State Refactoring: Profiler scripts now use a shared state module for improved consistency across modules
- 🧹 Export Logic Cleanup: Streamlined export code paths for CSV, flamegraph, and JSONL
- 🏗️ Code Refactoring: Split monolithic codebase into modular architecture for better maintainability
- ⏱️ Self-Time Tracking: New self-time metrics (total, avg, median, min, max) show actual function performance excluding child calls
- 📞 Parent Function Tracking: Track caller relationships and identify most common callers for each function
- 🕸️ Call Graph Export: Export call graphs in DOT format for visualization in Graphviz
- 📤 Timed Profiling Auto-Export: CSV and flamegraph automatically export when timed profiling completes
- 🔧 Bug Fixes: Fixed timed profiling, preset load/delete, dropdown display issues
- 🔧 Critical Fixes: Fixed exponential function count growth bug, auto-rewrap not running, and decimal precision regression
- 🎨 Custom Module Colors: Set custom colors for modules via color picker UI, persisted with presets
- 📊 High Precision Mode: Toggleable 6-decimal precision display for detailed analysis
- ⏱️ Elapsed Time Tracking: Shows elapsed measurement time during profiling sessions
- 🎯 Improved Preset Management: Dropdown selector with better save/load workflow
- 📝 Auto-Rewrap Logging: Auto-rewrap operations now log to DevTools logger
- 💡 Comprehensive Tooltips: Added tooltips for all UI controls
- 📁 Filesystem Restructure: Organized
devtools/folder structure for all exports
- Median execution time statistic
- Toggleable column visibility
- Fixed module color mapping bug
- ✨ LuaBind class method profiling - Now wraps class methods from bindings (e.g.,
utils_ui.UICellItem.Update) - 🔇
DEVTOOLS_VERBOSEflag - Control console output noise - 🐛 Fixed re-scan duplicate wrapping - No more repeated wrapping on rescans
- 🎯 Fixed cross-module name pollution - Correct function paths in exports
- Profiling Overhead: Adds overhead that increases with the number of wrapped functions
- Too Many Modules: Profiling 100+ modules simultaneously can cause noticeable FPS drops
- Solution: Use presets to profile subsets at a time
- Minimal Mode: Hides stats table to maintain FPS during collection
- Set
DEVTOOLS_VERBOSE = truein config to debug DevTools itself - Default is
DEVTOOLS_VERBOSE = false(quiet mode) - Presets are saved automatically to
user_files/devtools_presets.ltx
| File | Purpose |
|---|---|
devtools_profiler.script |
Core profiling engine with auto-discovery and statistics tracking |
devtools_ui_profiler.script |
ImGui user interface and controls for the profiler |
devtools_ui_logger.script |
ImGui user interface for the logging system |
devtools_profiler_export.script |
Export functionality for CSV, flamegraph, and call graph |
devtools_logging.script |
Structured logging system with categories and severity levels |
devtools_config.script |
Configuration and preset storage |
devtools_README.md |
In-game documentation reference |
mod_script_devtools_early.ltx |
DLTX config for early script loading |
While auto-discovery is the recommended approach, you can manually control profiling if needed:
-- Register a specific module (optional)
devtools_profiler.register_module("my_custom_module")
-- Enable profiling
devtools_profiler.enable()
-- Get statistics
local stats = devtools_profiler.get_all_stats()
-- Disable profiling
devtools_profiler.disable()
-- Unregister a module
devtools_profiler.unregister_module("my_custom_module")For automatic profiling to work, store your scripts in _G:
-- Good - will be auto-discovered
_G.my_custom_module = {
function_one = function() ... end,
function_two = function() ... end,
}
-- Also good - profiler finds nested tables
_G.my_namespace = {
utils = {
helper_one = function() ... end,
helper_two = function() ... end,
}
}- Enable Profiling: Select modules you want to profile
- Play: Let the game run for the scenario you're testing (30 seconds - 5 minutes)
- Export: Click "Export CSV"
- Analyze: Open in Excel/LibreOffice/Google Sheets
- Optimize: Sort by Total (ms) or Avg (ms) to find slow functions
- Repeat: Profile again after optimization to measure improvement
- Enable Profiling: Select modules
- Play: Reproduce the scenario you're analyzing
- Export Flamegraph: Click "Export Flamegraph" (or use Timed Profiling for auto-export)
- Visualize: Open in SpeedScope.app
- Drag-and-drop the
.foldedfile - Look for thick stacks (high CPU time)
- Drag-and-drop the
- Optimize: Focus on the thickest call stacks first
- Verify: Re-profile and compare
- Enable Profiling: Select modules with call graph tracking
- Play: Reproduce the scenario you're analyzing
- Export Call Graph: Click "Export Call Graph"
- Visualize: Open the
.dotfile in a Graphviz viewer or online tool- View function call relationships
- Identify module dependencies
- Understand code architecture
- Optimize: Focus on heavily-called functions and unexpected call patterns
- Verify: Re-profile and compare
Found a bug? Have a feature request?
- ModDB: Report on the mod page
- GitHub: Open an issue
- Discord: Discuss in the GAMMA/Anomaly modding communities
Created by: CDEV
Version: 1.3.5
For: S.T.A.L.K.E.R. Anomaly modding community
Happy optimizing! 🚀