defmodule Mix.Tasks.Raxol.Perf do @moduledoc """ Performance analysis and optimization tools for development. This task provides various performance analysis capabilities: ## Commands mix raxol.perf analyze # Analyze current performance mix raxol.perf profile # Profile a specific module mix raxol.perf hints # Show performance hints mix raxol.perf monitor # Start continuous monitoring mix raxol.perf memory # Memory usage analysis mix raxol.perf report # Generate performance report ## Examples # Quick performance check mix raxol.perf analyze # Profile terminal operations mix raxol.perf profile Raxol.Terminal.Buffer # Start continuous monitoring mix raxol.perf monitor --interval 30 # Generate detailed report mix raxol.perf report --format html """ use Mix.Task require Logger @shortdoc "Performance analysis and optimization tools" @switches [ format: :string, interval: :integer, duration: :integer, memory: :boolean, processes: :boolean, call_graph: :boolean, help: :boolean ] @aliases [ f: :format, i: :interval, d: :duration, m: :memory, p: :processes, h: :help ] def run(args) do {opts, args, _} = OptionParser.parse(args, switches: @switches, aliases: @aliases) if opts[:help] do show_help() else ensure_started() handle_command(args, opts) end end defp handle_command([], _opts), do: handle_command(["analyze"], []) defp handle_command(["analyze"], _opts) do Mix.shell().info("šŸ” Analyzing current performance...") analysis = Raxol.Performance.DevProfiler.analyze_current_performance() Mix.shell().info("\nšŸ“Š Performance Analysis Results:") Mix.shell().info("Memory: #{analysis.memory.total_mb}MB total") Mix.shell().info("Processes: #{analysis.processes.count}") if length(analysis.memory.issues) > 0 do Mix.shell().info("\nāš ļø Issues detected:") Enum.each( analysis.memory.issues ++ analysis.processes.issues ++ analysis.system.issues, fn issue -> Mix.shell().info(" • #{issue}") end ) else Mix.shell().info("\nāœ… No performance issues detected") end end defp handle_command(["profile", module_name], opts) when is_binary(module_name) do try do module = Module.concat([module_name]) if Code.ensure_loaded?(module) do Mix.shell().info("šŸ”¬ Profiling module: #{module}") profile_opts = build_profile_opts(opts) # Profile module functions _result = Raxol.Performance.DevProfiler.profile(profile_opts, fn -> # Call some representative functions if they exist profile_module_functions(module) end) Mix.shell().info("āœ… Profiling complete") else Mix.shell().error("Module #{module} not found or not loaded") end rescue ArgumentError -> Mix.shell().error("Invalid module name: #{module_name}") end end defp handle_command(["profile"], _opts) do Mix.shell().error("Usage: mix raxol.perf profile ") end defp handle_command(["hints"], _opts) do Mix.shell().info("šŸ’” Current Performance Hints:") if Raxol.Performance.DevHints.enabled?() do stats = Raxol.Performance.DevHints.stats() Mix.shell().info( "Hints system: Enabled (#{stats.total_hints} hints shown)" ) Mix.shell().info("Uptime: #{Float.round(stats.uptime_ms / 1000, 1)}s") if map_size(stats.hint_categories) > 0 do Mix.shell().info("\nHint categories:") Enum.each(stats.hint_categories, fn {category, count} -> Mix.shell().info(" #{category}: #{count}") end) end else Mix.shell().info("Performance hints are not enabled") Mix.shell().info( "Enable with: config :raxol, Raxol.Performance.DevHints, enabled: true" ) end end defp handle_command(["monitor"], opts) do interval = Keyword.get(opts, :interval, 30) * 1000 duration = Keyword.get(opts, :duration, 5) * 1000 Mix.shell().info("šŸ“Š Starting continuous performance monitoring...") Mix.shell().info( "Interval: #{interval / 1000}s, Duration: #{duration / 1000}s per session" ) Mix.shell().info("Press Ctrl+C to stop") Raxol.Performance.DevProfiler.start_continuous( interval: interval, duration: duration, auto_hints: true ) # Keep the task running receive do :never -> :ok end end defp handle_command(["memory"], opts) do duration = Keyword.get(opts, :duration, 10) * 1000 Mix.shell().info("🧠 Profiling memory usage for #{duration / 1000}s...") Raxol.Performance.DevProfiler.profile_memory(duration) end defp handle_command(["report"], opts) do format = Keyword.get(opts, :format, "text") Mix.shell().info("šŸ“‹ Generating performance report...") profile_opts = build_profile_opts(opts) profile_opts = Keyword.put(profile_opts, :output_format, String.to_atom(format)) # Generate comprehensive report Raxol.Performance.DevProfiler.profile(profile_opts, fn -> # Simulate typical workload simulate_workload() end) end defp handle_command([command], _opts) do Mix.shell().error("Unknown command: #{command}") show_help() end defp handle_command(command, _opts) do Mix.shell().error("Invalid command: #{inspect(command)}") show_help() end defp build_profile_opts(opts) do profile_opts = [] profile_opts = if duration = opts[:duration] do Keyword.put(profile_opts, :duration, duration * 1000) else profile_opts end profile_opts = if opts[:memory] do Keyword.put(profile_opts, :memory, true) else profile_opts end profile_opts = if opts[:processes] do Keyword.put(profile_opts, :processes, true) else profile_opts end profile_opts = if opts[:call_graph] do Keyword.put(profile_opts, :call_graph, true) else profile_opts end profile_opts end defp profile_module_functions(module) do # Try to call some common functions to generate profile data functions = module.__info__(:functions) # Look for common patterns and call them safely Enum.each(functions, fn {name, arity} -> case {name, arity} do {:new, 0} -> try do apply(module, :new, []) catch _, _ -> :ok end {:new, 1} -> try do apply(module, :new, [%{}]) catch _, _ -> :ok end {:start_link, 0} -> # Skip GenServer start_link calls in profiling :skip _ -> :skip end end) end defp simulate_workload do # Simulate typical Raxol operations for comprehensive profiling try do # Terminal operations if Code.ensure_loaded?(Raxol.Terminal.Emulator) do emulator = Raxol.Terminal.Emulator.new(80, 24) Raxol.Terminal.Emulator.process_input(emulator, "Hello, World!") end # Buffer operations if Code.ensure_loaded?(Raxol.Terminal.ScreenBuffer) do buffer = Raxol.Terminal.ScreenBuffer.new(80, 24) Raxol.Terminal.ScreenBuffer.write_string(buffer, 0, 0, "Test content") end catch _, _ -> :ok end end defp ensure_started do # Ensure the application is started for profiling Mix.Task.run("app.start") end defp show_help do Mix.shell().info(""" Performance analysis and optimization tools for Raxol development. ## Commands mix raxol.perf analyze # Quick performance analysis mix raxol.perf profile # Profile specific module mix raxol.perf hints # Show performance hints status mix raxol.perf monitor [options] # Start continuous monitoring mix raxol.perf memory [options] # Memory usage analysis mix raxol.perf report [options] # Generate performance report ## Options --format, -f Output format (text, html, json) --interval, -i Monitoring interval in seconds (default: 30) --duration, -d Profiling duration in seconds (default: 10) --memory, -m Include memory profiling --processes, -p Include process analysis --call-graph Generate call graph (expensive) --help, -h Show this help ## Examples # Quick analysis mix raxol.perf analyze # Profile with memory analysis mix raxol.perf profile Raxol.Terminal.Buffer --memory # Continuous monitoring every 60 seconds mix raxol.perf monitor --interval 60 # Generate HTML report mix raxol.perf report --format html --duration 30 # Memory profiling for 30 seconds mix raxol.perf memory --duration 30 """) end end