|
| 1 | +using Microsoft.AspNetCore.Mvc; |
| 2 | +using PerfProblemSimulator.Models; |
| 3 | +using PerfProblemSimulator.Services; |
| 4 | + |
| 5 | +namespace PerfProblemSimulator.Controllers; |
| 6 | + |
| 7 | +/// <summary> |
| 8 | +/// Controller for triggering intentional application crashes. |
| 9 | +/// </summary> |
| 10 | +/// <remarks> |
| 11 | +/// <para> |
| 12 | +/// <strong>Educational Note:</strong> This controller provides endpoints to intentionally |
| 13 | +/// crash the application in various ways. This is useful for learning: |
| 14 | +/// </para> |
| 15 | +/// <list type="bullet"> |
| 16 | +/// <item>How to configure Azure App Service crash monitoring</item> |
| 17 | +/// <item>How to collect and analyze crash dumps</item> |
| 18 | +/// <item>Understanding different types of fatal errors in .NET</item> |
| 19 | +/// <item>Practicing crash dump analysis with WinDbg or Visual Studio</item> |
| 20 | +/// </list> |
| 21 | +/// <para> |
| 22 | +/// <strong>WARNING:</strong> These endpoints will terminate the application! |
| 23 | +/// Azure App Service will automatically restart the application after a crash. |
| 24 | +/// </para> |
| 25 | +/// </remarks> |
| 26 | +[ApiController] |
| 27 | +[Route("api/[controller]")] |
| 28 | +[Produces("application/json")] |
| 29 | +[Tags("Crash Simulation")] |
| 30 | +public class CrashController : ControllerBase |
| 31 | +{ |
| 32 | + private readonly ICrashService _crashService; |
| 33 | + private readonly ILogger<CrashController> _logger; |
| 34 | + |
| 35 | + public CrashController(ICrashService crashService, ILogger<CrashController> logger) |
| 36 | + { |
| 37 | + _crashService = crashService ?? throw new ArgumentNullException(nameof(crashService)); |
| 38 | + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); |
| 39 | + } |
| 40 | + |
| 41 | + /// <summary> |
| 42 | + /// Triggers an application crash of the specified type. |
| 43 | + /// </summary> |
| 44 | + /// <param name="request">The crash configuration.</param> |
| 45 | + /// <returns>Confirmation that the crash has been scheduled (response sent before crash occurs).</returns> |
| 46 | + /// <remarks> |
| 47 | + /// <para> |
| 48 | + /// <strong>WARNING:</strong> This endpoint will TERMINATE the application! |
| 49 | + /// </para> |
| 50 | + /// <para> |
| 51 | + /// The response is sent before the crash occurs, so you will receive a 200 OK |
| 52 | + /// indicating the crash has been scheduled. The actual crash happens shortly after. |
| 53 | + /// </para> |
| 54 | + /// <para> |
| 55 | + /// <strong>Azure Crash Monitoring Setup:</strong> |
| 56 | + /// </para> |
| 57 | + /// <list type="number"> |
| 58 | + /// <item>In Azure Portal, go to your App Service</item> |
| 59 | + /// <item>Navigate to "Diagnose and solve problems"</item> |
| 60 | + /// <item>Search for "Crash Monitoring"</item> |
| 61 | + /// <item>Enable crash monitoring with "Collect Memory Dump"</item> |
| 62 | + /// <item>Call this endpoint to trigger a crash</item> |
| 63 | + /// <item>Download the crash dump from the Azure portal</item> |
| 64 | + /// </list> |
| 65 | + /// </remarks> |
| 66 | + /// <response code="200">Crash has been scheduled</response> |
| 67 | + /// <response code="400">Invalid crash configuration</response> |
| 68 | + /// <response code="503">Problem endpoints are disabled</response> |
| 69 | + [HttpPost("trigger")] |
| 70 | + [ProducesResponseType(typeof(SimulationResult), StatusCodes.Status200OK)] |
| 71 | + [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status400BadRequest)] |
| 72 | + [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status503ServiceUnavailable)] |
| 73 | + public IActionResult TriggerCrash([FromBody] CrashRequest? request) |
| 74 | + { |
| 75 | + request ??= new CrashRequest(); |
| 76 | + |
| 77 | + // Validate delay |
| 78 | + if (request.DelaySeconds < 0 || request.DelaySeconds > 60) |
| 79 | + { |
| 80 | + return BadRequest(new ErrorResponse |
| 81 | + { |
| 82 | + Error = "INVALID_DELAY", |
| 83 | + Message = "DelaySeconds must be between 0 and 60" |
| 84 | + }); |
| 85 | + } |
| 86 | + |
| 87 | + _logger.LogCritical( |
| 88 | + "🚨 CRASH REQUESTED: Type={CrashType}, Delay={DelaySeconds}s", |
| 89 | + request.CrashType, request.DelaySeconds); |
| 90 | + |
| 91 | + // Trigger the crash |
| 92 | + _crashService.TriggerCrash(request.CrashType, request.DelaySeconds, request.Message); |
| 93 | + |
| 94 | + var crashMessage = $"💥 {request.CrashType} crash scheduled! " + |
| 95 | + (request.DelaySeconds > 0 |
| 96 | + ? $"Crash will occur in {request.DelaySeconds} seconds." |
| 97 | + : "Crash will occur in ~100ms (after this response is sent)."); |
| 98 | + |
| 99 | + return Ok(new SimulationResult |
| 100 | + { |
| 101 | + SimulationId = Guid.NewGuid(), |
| 102 | + Type = SimulationType.Crash, |
| 103 | + Status = "Scheduled", |
| 104 | + Message = crashMessage, |
| 105 | + ActualParameters = new Dictionary<string, object> |
| 106 | + { |
| 107 | + ["CrashType"] = request.CrashType.ToString(), |
| 108 | + ["DelaySeconds"] = request.DelaySeconds |
| 109 | + } |
| 110 | + }); |
| 111 | + } |
| 112 | + |
| 113 | + /// <summary> |
| 114 | + /// Gets information about available crash types. |
| 115 | + /// </summary> |
| 116 | + /// <returns>A dictionary of crash types and their descriptions.</returns> |
| 117 | + /// <remarks> |
| 118 | + /// Use this endpoint to understand what each crash type does before triggering it. |
| 119 | + /// </remarks> |
| 120 | + /// <response code="200">List of crash types and descriptions</response> |
| 121 | + [HttpGet("types")] |
| 122 | + [ProducesResponseType(typeof(Dictionary<string, CrashTypeInfo>), StatusCodes.Status200OK)] |
| 123 | + public IActionResult GetCrashTypes() |
| 124 | + { |
| 125 | + var descriptions = _crashService.GetCrashTypeDescriptions(); |
| 126 | + |
| 127 | + var result = descriptions.ToDictionary( |
| 128 | + kvp => kvp.Key.ToString(), |
| 129 | + kvp => new CrashTypeInfo |
| 130 | + { |
| 131 | + Name = kvp.Key.ToString(), |
| 132 | + Value = (int)kvp.Key, |
| 133 | + Description = kvp.Value, |
| 134 | + RecommendedForAzure = kvp.Key == CrashType.FailFast || kvp.Key == CrashType.StackOverflow |
| 135 | + }); |
| 136 | + |
| 137 | + return Ok(result); |
| 138 | + } |
| 139 | + |
| 140 | + /// <summary> |
| 141 | + /// Triggers a FailFast crash (most common for Azure crash monitoring). |
| 142 | + /// </summary> |
| 143 | + /// <param name="delaySeconds">Optional delay before crash (0-60 seconds).</param> |
| 144 | + /// <returns>Confirmation that the crash has been scheduled.</returns> |
| 145 | + /// <remarks> |
| 146 | + /// This is a convenience endpoint for the most commonly used crash type. |
| 147 | + /// Environment.FailFast is the recommended method for testing Azure crash monitoring. |
| 148 | + /// </remarks> |
| 149 | + /// <response code="200">Crash has been scheduled</response> |
| 150 | + /// <response code="503">Problem endpoints are disabled</response> |
| 151 | + [HttpPost("failfast")] |
| 152 | + [ProducesResponseType(typeof(SimulationResult), StatusCodes.Status200OK)] |
| 153 | + [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status503ServiceUnavailable)] |
| 154 | + public IActionResult TriggerFailFast([FromQuery] int delaySeconds = 0) |
| 155 | + { |
| 156 | + return TriggerCrash(new CrashRequest |
| 157 | + { |
| 158 | + CrashType = CrashType.FailFast, |
| 159 | + DelaySeconds = Math.Clamp(delaySeconds, 0, 60), |
| 160 | + Message = "FailFast triggered via /api/crash/failfast endpoint" |
| 161 | + }); |
| 162 | + } |
| 163 | + |
| 164 | + /// <summary> |
| 165 | + /// Triggers a StackOverflow crash. |
| 166 | + /// </summary> |
| 167 | + /// <param name="delaySeconds">Optional delay before crash (0-60 seconds).</param> |
| 168 | + /// <returns>Confirmation that the crash has been scheduled.</returns> |
| 169 | + /// <remarks> |
| 170 | + /// Creates interesting stack traces for analysis. The crash dump will show |
| 171 | + /// repeated calls to the same method, demonstrating infinite recursion. |
| 172 | + /// </remarks> |
| 173 | + /// <response code="200">Crash has been scheduled</response> |
| 174 | + /// <response code="503">Problem endpoints are disabled</response> |
| 175 | + [HttpPost("stackoverflow")] |
| 176 | + [ProducesResponseType(typeof(SimulationResult), StatusCodes.Status200OK)] |
| 177 | + [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status503ServiceUnavailable)] |
| 178 | + public IActionResult TriggerStackOverflow([FromQuery] int delaySeconds = 0) |
| 179 | + { |
| 180 | + return TriggerCrash(new CrashRequest |
| 181 | + { |
| 182 | + CrashType = CrashType.StackOverflow, |
| 183 | + DelaySeconds = Math.Clamp(delaySeconds, 0, 60) |
| 184 | + }); |
| 185 | + } |
| 186 | +} |
| 187 | + |
| 188 | +/// <summary> |
| 189 | +/// Information about a crash type. |
| 190 | +/// </summary> |
| 191 | +public class CrashTypeInfo |
| 192 | +{ |
| 193 | + /// <summary> |
| 194 | + /// Name of the crash type. |
| 195 | + /// </summary> |
| 196 | + public required string Name { get; set; } |
| 197 | + |
| 198 | + /// <summary> |
| 199 | + /// Numeric value for the crash type enum. |
| 200 | + /// </summary> |
| 201 | + public int Value { get; set; } |
| 202 | + |
| 203 | + /// <summary> |
| 204 | + /// Detailed description of what this crash type does. |
| 205 | + /// </summary> |
| 206 | + public required string Description { get; set; } |
| 207 | + |
| 208 | + /// <summary> |
| 209 | + /// Whether this crash type is recommended for Azure crash monitoring. |
| 210 | + /// </summary> |
| 211 | + public bool RecommendedForAzure { get; set; } |
| 212 | +} |
0 commit comments