using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using XP.Common.Dump.Configs; using XP.Common.Dump.Interfaces; using XP.Common.Dump.Native; using XP.Common.Logging.Interfaces; namespace XP.Common.Dump.Implementations { /// /// Dump 文件管理服务实现,负责 Dump 生成、崩溃事件订阅、定时调度和清理 /// Dump file management service implementation, handles dump generation, crash event subscription, scheduled tasks and cleanup /// public class DumpService : IDumpService { private readonly DumpConfig _config; private readonly ILoggerService _logger; private readonly DumpCleaner _cleaner; private Timer? _scheduledTimer; private Timer? _cleanupTimer; private bool _isStarted; private bool _disposed; /// /// 默认存储路径 | Default storage path /// private const string DefaultStoragePath = @"D:\XplorePlane\Dump"; public DumpService(DumpConfig config, ILoggerService logger) { _config = config ?? throw new ArgumentNullException(nameof(config)); _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger)); _cleaner = new DumpCleaner(config, logger); } /// /// 生成 Dump 文件名(纯函数)| Generate dump file name (pure function) /// 格式:XplorePlane_{yyyyMMdd_HHmm}_{TriggerType}.dmp /// /// 触发类型 | Trigger type /// 时间戳 | Timestamp /// 文件名 | File name internal static string GenerateFileName(DumpTriggerType triggerType, DateTime timestamp) { return $"XplorePlane_{timestamp:yyyyMMdd_HHmm}_{triggerType}.dmp"; } /// /// 验证 Mini Dump 文件大小是否在限制范围内 | Validate Mini Dump file size is within limit /// /// 文件大小(字节)| File size in bytes /// 大小限制(MB)| Size limit in MB /// true 表示在限制内 | true if within limit internal static bool ValidateFileSize(long fileSizeBytes, long sizeLimitMB) { return fileSizeBytes <= sizeLimitMB * 1024 * 1024; } /// /// 写入 Dump 文件的核心方法 | Core method to write dump file /// /// 触发类型 | Trigger type /// 是否为 Mini Dump | Whether it is a Mini Dump /// 生成的文件完整路径,失败返回 null | Full path of generated file, null on failure private string? WriteDump(DumpTriggerType triggerType, bool isMiniDump) { try { // 非手动触发请求 Full Dump 时,降级为 Mini Dump | Downgrade to Mini Dump for non-manual Full Dump requests if (!isMiniDump && triggerType != DumpTriggerType.Manual) { _logger.Warn("非手动触发不允许生成 Full Dump,已降级为 Mini Dump:{TriggerType} | Non-manual trigger cannot generate Full Dump, downgraded to Mini Dump: {TriggerType}", triggerType); isMiniDump = true; } var fileName = GenerateFileName(triggerType, DateTime.Now); var filePath = Path.Combine(_config.StoragePath, fileName); // 确定 Dump 类型标志 | Determine dump type flags uint dumpType = isMiniDump ? (uint)(MiniDumpType.MiniDumpWithDataSegs | MiniDumpType.MiniDumpWithHandleData | MiniDumpType.MiniDumpWithThreadInfo) : (uint)MiniDumpType.MiniDumpWithFullMemory; var process = Process.GetCurrentProcess(); using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.None)) { bool success = NativeMethods.MiniDumpWriteDump( process.Handle, (uint)process.Id, fileStream.SafeFileHandle, dumpType, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); if (!success) { var errorCode = Marshal.GetLastWin32Error(); _logger.Error(null!, "MiniDumpWriteDump 调用失败,错误码:{ErrorCode} | MiniDumpWriteDump failed, error code: {ErrorCode}", errorCode); // 清理失败的文件 | Clean up failed file try { File.Delete(filePath); } catch { } return null; } } // Mini Dump 文件大小检查 | Mini Dump file size check if (isMiniDump) { var fileInfo = new FileInfo(filePath); if (!ValidateFileSize(fileInfo.Length, _config.MiniDumpSizeLimitMB)) { _logger.Warn("Mini Dump 文件超过大小限制({SizeMB}MB),已删除:{FilePath} | Mini Dump file exceeds size limit ({SizeMB}MB), deleted: {FilePath}", _config.MiniDumpSizeLimitMB, filePath); File.Delete(filePath); return null; } } var dumpTypeStr = isMiniDump ? "Mini" : "Full"; _logger.Info("Dump 文件已生成:{FilePath},触发类型:{TriggerType},Dump 类型:{DumpType} | Dump file generated: {FilePath}, trigger: {TriggerType}, type: {DumpType}", filePath, triggerType, dumpTypeStr); return filePath; } catch (IOException ex) { _logger.Error(ex, "Dump 文件写入失败:{Message} | Dump file write failed: {Message}", ex.Message); return null; } catch (UnauthorizedAccessException ex) { _logger.Error(ex, "Dump 文件写入权限不足:{Message} | Dump file write permission denied: {Message}", ex.Message); return null; } catch (Exception ex) { _logger.Error(ex, "Dump 文件生成异常:{Message} | Dump file generation error: {Message}", ex.Message); return null; } } #region 手动触发方法 | Manual trigger methods /// public string? CreateMiniDump() { _logger.Info("手动触发 Mini Dump 生成 | Manually triggering Mini Dump generation"); var filePath = WriteDump(DumpTriggerType.Manual, isMiniDump: true); if (filePath != null) { _logger.Info("手动 Mini Dump 已生成:{FilePath} | Manual Mini Dump generated: {FilePath}", filePath); } return filePath; } /// public string? CreateFullDump() { _logger.Info("手动触发 Full Dump 生成 | Manually triggering Full Dump generation"); var filePath = WriteDump(DumpTriggerType.Manual, isMiniDump: false); if (filePath != null) { _logger.Info("手动 Full Dump 已生成:{FilePath} | Manual Full Dump generated: {FilePath}", filePath); } return filePath; } #endregion #region 生命周期管理 | Lifecycle management /// public void Start() { if (_isStarted) return; _logger.Info("Dump 服务启动中 | Dump service starting"); // 确保存储目录存在 | Ensure storage directory exists EnsureStorageDirectory(); // 订阅崩溃事件 | Subscribe to crash events AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; TaskScheduler.UnobservedTaskException += OnUnobservedTaskException; // 启动定时 Dump 计时器 | Start scheduled dump timer if (_config.EnableScheduledDump) { var intervalMs = _config.ScheduledIntervalMinutes * 60 * 1000; _scheduledTimer = new Timer(OnScheduledDumpCallback, null, intervalMs, intervalMs); _logger.Info("定时 Dump 已启用,间隔:{Interval} 分钟 | Scheduled dump enabled, interval: {Interval} minutes", _config.ScheduledIntervalMinutes); } // 启动每日清理计时器(每24小时执行一次)| Start daily cleanup timer (every 24 hours) var dailyMs = 24 * 60 * 60 * 1000; _cleanupTimer = new Timer(OnCleanupCallback, null, dailyMs, dailyMs); // 立即执行一次清理 | Execute cleanup immediately _cleaner.CleanExpiredFiles(); _isStarted = true; _logger.Info("Dump 服务已启动 | Dump service started"); } /// public void Stop() { if (!_isStarted) return; _logger.Info("Dump 服务停止中 | Dump service stopping"); // 取消事件订阅 | Unsubscribe from events AppDomain.CurrentDomain.UnhandledException -= OnUnhandledException; TaskScheduler.UnobservedTaskException -= OnUnobservedTaskException; // 停止所有计时器 | Stop all timers _scheduledTimer?.Change(Timeout.Infinite, Timeout.Infinite); _cleanupTimer?.Change(Timeout.Infinite, Timeout.Infinite); _isStarted = false; _logger.Info("Dump 服务已停止 | Dump service stopped"); } /// public void Dispose() { if (_disposed) return; Stop(); _scheduledTimer?.Dispose(); _scheduledTimer = null; _cleanupTimer?.Dispose(); _cleanupTimer = null; _disposed = true; } #endregion #region 内部方法 | Internal methods /// /// 确保存储目录存在,不存在则创建,创建失败回退默认路径 /// Ensure storage directory exists, create if not, fallback to default on failure /// private void EnsureStorageDirectory() { try { if (!Directory.Exists(_config.StoragePath)) { Directory.CreateDirectory(_config.StoragePath); _logger.Info("已创建存储目录:{Path} | Created storage directory: {Path}", _config.StoragePath); } } catch (Exception ex) { _logger.Error(ex, "存储目录创建失败:{Path},回退到默认路径 | Storage directory creation failed: {Path}, falling back to default", _config.StoragePath); _config.StoragePath = DefaultStoragePath; try { if (!Directory.Exists(DefaultStoragePath)) { Directory.CreateDirectory(DefaultStoragePath); _logger.Info("已创建默认存储目录:{Path} | Created default storage directory: {Path}", DefaultStoragePath); } } catch (Exception fallbackEx) { _logger.Fatal(fallbackEx, "默认存储目录创建也失败:{Path} | Default storage directory creation also failed: {Path}", DefaultStoragePath); } } } /// /// 未处理异常回调 | Unhandled exception callback /// private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) { _logger.Info("检测到未处理异常,正在生成崩溃 Dump | Unhandled exception detected, generating crash dump"); WriteDump(DumpTriggerType.Crash, isMiniDump: true); } /// /// 未观察到的 Task 异常回调 | Unobserved task exception callback /// private void OnUnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) { _logger.Info("检测到未观察的 Task 异常,正在生成崩溃 Dump | Unobserved task exception detected, generating crash dump"); WriteDump(DumpTriggerType.Crash, isMiniDump: true); } /// /// 定时 Dump 回调 | Scheduled dump callback /// private void OnScheduledDumpCallback(object? state) { _logger.Debug("定时 Dump 触发 | Scheduled dump triggered"); WriteDump(DumpTriggerType.Scheduled, isMiniDump: true); } /// /// 清理回调 | Cleanup callback /// private void OnCleanupCallback(object? state) { _logger.Debug("定时清理触发 | Scheduled cleanup triggered"); _cleaner.CleanExpiredFiles(); } #endregion } }