// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. using System.IO; using System.Xml; using System.Xml.Serialization; using System.Reflection; namespace Tools.CrashReporter.CrashReportProcess { /// /// Config values for the Crash Report Process. The binary expects to find an xml representation in the binary folder at runtime. /// [XmlRoot] public class Config { /// /// CRP version for displaying in status reports/logs etc /// [XmlElement] public string VersionString { get; set; } /// /// Folder where the files that are linked from the website are stored. /// [XmlElement] public string ProcessedReports { get; set; } /// /// Time after which old, unprocessed reports in landing zones are deleted. /// [XmlElement] public int DeleteWaitingReportsDays { get; set; } /// /// The website domain to which we output crashes. /// [XmlElement] public string CrashReportWebSite { get; set; } /// /// Folder where the video file that are linked from the website are stored. /// [XmlElement] public string ProcessedVideos { get; set; } /// /// Folder where the Perforce depot is located. /// [XmlElement] public string DepotRoot { get; set; } /// /// Perforce username used by the CRP and MDD. /// [XmlElement] public string P4User { get; set; } /// /// Perforce workspace used by the CRP and MDD. /// [XmlElement] public string P4Client { get; set; } /// /// Folder where new crash reports are queued from internal, company users. /// [XmlElement] public string InternalLandingZone { get; set; } /// /// Folder where new crash reports are queued from external users. /// [XmlElement] public string ExternalLandingZone { get; set; } /// /// Folder where new crash reports are queued from the data router S3 bucket. /// [XmlElement] public string DataRouterLandingZone { get; set; } /// /// Folder where new crash reports are queued from the PS4 crash service. /// [XmlElement] public string PS4LandingZone { get; set; } /// /// Number of reports in a queue at which reports will start to be discarded to stop a backlog from growing uncontrollably. /// [XmlElement] public int QueueLowerLimitForDiscard { get; set; } /// /// Number of reports in a queue at which reports will all be discarded so this is the upper limit for a backlog. /// [XmlElement] public int QueueUpperLimitForDiscard { get; set; } /// /// String passed to Minidump Diagnostics to modify its Perforce depot root. /// [XmlElement] public string DepotIndex { get; set; } /// /// String specifying the local path of the MinidumpDiagnostics exe /// [XmlElement] public string MDDExecutablePath { get; set; } /// /// String specifying the path to the folder used by MinidumpDiagnostics for the PDB cache /// [XmlElement] public string MDDPDBCachePath { get; set; } /// /// Number telling MinidumpDiagnostics how large it can make the PDB cache /// [XmlElement] public int MDDPDBCacheSizeGB { get; set; } /// /// Number telling MinidumpDiagnostics when it should start clearing cache entries to create more disk space /// [XmlElement] public int MDDPDBCacheMinFreeSpaceGB { get; set; } /// /// Number telling MinidumpDiagnostics the minimum age if a cache entry that should be considered for deletion/stale /// [XmlElement] public int MDDPDBCacheFileDeleteDays { get; set; } /// /// Timeout when waiting for MinidumpDiagnostics to complete /// [XmlElement] public int MDDTimeoutMinutes { get; set; } /// /// The number of threads created by each main processor thread to upload crashes to the website. (relieves a bottleneck when using a single processor thread) /// [XmlElement] public int AddReportsPerProcessor { get; set; } /// /// The number of main processor threads /// [XmlElement] public int ProcessorThreadCount { get; set; } /// /// The number of parallel MDD instances allowed /// [XmlElement] public int MaxConcurrentMDDs { get; set; } /// /// Incoming webhook URL for Slack integration. /// [XmlElement] public string SlackWebhookUrl { get; set; } /// /// Slack channel to which messages are posted. Can be null or empty to use the webhook default. /// [XmlElement] public string SlackChannel { get; set; } /// /// Slack username from which messages are posted. Can be null or empty to use the webhook default. /// [XmlElement] public string SlackUsername { get; set; } /// /// Slack user icon name (e.g. ":someemoji:") from which messages are posted. Can be null or empty to use the webhook default. /// [XmlElement] public string SlackEmoji { get; set; } /// /// The time period in which an alert with the same identifying key is not allowed to repeat. /// [XmlElement] public int SlackAlertRepeatMinimumMinutes { get; set; } /// /// The time period in which a crash decimation (large backlog) alert with the same identifying key is not allowed to repeat. /// [XmlElement] public int SlackDecimateAlertRepeatMinimumMinutes { get; set; } /// /// Local folder used to setup testing folders in Debug builds. Overrides other folder params in config. /// [XmlElement] public string DebugTestingFolder { get; set; } /// /// Period between reports to Slack (if available). /// [XmlElement] public int MinutesBetweenQueueSizeReports { get; set; } /// /// Size limit below which queues will attempt to enqueue more crashes into memory /// Above this size, a queue will skip enqueueing until the next update. /// [XmlElement] public int MinDesiredMemoryQueueSize { get; set; } /// /// Size limit beyond which crashes won't be enqueued into memory on each queue. /// [XmlElement] public int MaxMemoryQueueSize { get; set; } /// /// AWSSDK AWS credentials filepath containing the keys used to access SQS and S3 /// [XmlElement] public string AWSCredentialsFilepath { get; set; } /// /// AWSSDK Profile name used in the AWS credentials file for reading crashes from DataRouter /// [XmlElement] public string AWSProfileInputName { get; set; } /// /// AWSSDK service Url for S3 client reading crashes from DataRouter /// [XmlElement] public string AWSS3ServiceInputURL { get; set; } /// /// AWSSDK service Url for SQS client reading crashes from DataRouter /// [XmlElement] public string AWSSQSServiceInputURL { get; set; } /// /// AWSSDK queue Url for SQS client reading crashes from DataRouter /// [XmlElement] public string AWSSQSQueueInputUrl { get; set; } /// /// AWSSDK Profile name used in the AWS credentials file for writing crashes to S3 /// [XmlElement] public string AWSProfileOutputName { get; set; } /// /// AWSSDK service Url for S3 client for writing crashes to S3 /// [XmlElement] public string AWSS3ServiceOutputURL { get; set; } /// /// Should we output a copy of the crash report files to disk? (ProcessedReports, ProcessedVideos) /// [XmlElement] public bool CrashFilesToDisk { get; set; } /// /// Should we output a copy of the crash report files to S3? /// [XmlElement] public bool CrashFilesToAWS { get; set; } /// /// Should large crash files saved to S3 be compressed? (crash context will never be compressed) /// [XmlElement] public bool CompressCrashFilesOnAWS { get; set; } /// /// Should we save invalid reports that fail to process to S3? /// [XmlElement] public bool InvalidReportsToAWS { get; set; } /// /// AWSSDK AWS S3 bucket used for output of crash reporter files (optional) /// [XmlElement] public string AWSS3OutputBucket { get; set; } /// /// AWSSDK AWS S3 path/key prefix used for output of crash reporter files (suffix will be crash id and file name) (optional) /// [XmlElement] public string AWSS3OutputKeyPrefix { get; set; } /// /// AWSSDK AWS S3 path/key prefix used for writing invalid reports (optional) /// [XmlElement] public string AWSS3InvalidKeyPrefix { get; set; } /// /// AWSSDK AWS S3 path/key suffix used in place of the existing extension for writing compressed reports (optional) /// [XmlElement] public string AWSS3CompressedSuffix { get; set; } /// /// Buffer size used to decompress zlib archives taken from S3 /// [XmlElement] public int MaxUncompressedS3RecordSize { get; set; } /// /// Index file used to store all processed crash names and times. Stops duplicates. Leave blank to disable. /// [XmlElement] public string ProcessedReportsIndexPath { get; set; } /// /// Time that the ProcessedReportsIndex retains items for duplicate checking. /// [XmlElement] public int ReportsIndexRetentionDays { get; set; } /// /// Timeout when calling AddCrash to submit crashes to the website/database. /// [XmlElement] public int AddCrashRequestTimeoutSeconds { get; set; } /// /// Number of times to retry AddCrash after a bad response (doesn't count timeouts that are always retried). /// [XmlElement] public int AddCrashRejectedRetries { get; set; } /// /// Time that we wait between a failed AddCrash call and a retry. /// [XmlElement] public int AddCrashRetryDelaySeconds { get; set; } /// /// The time limit for consecutive failed AddCrash() calls after which an alert will be created. /// [XmlElement] public int FailedWebAddAlertTimeSeconds { get; set; } /// /// Disk space available threshold that generates alerts. If a disk has less space than this, it will generate alerts. /// [XmlElement] public float DiskSpaceAlertPercent { get; set; } /// /// Switch on perf monitoring via StatusReporting. Adds an extra report with perf info. /// [XmlElement] public bool MonitorPerformance { get; set; } /// /// List of GameNames to blacklist. Since we take crashes from external games, we need to filter out stuff we don't want, especially high volume stuff. /// [XmlElement] public string GameNamesToBlacklist { get; set; } /// /// Get the default config object (lazy loads it on first access) /// public static Config Default { get { if (DefaultSingleton == null) { LoadConfig(); } return DefaultSingleton; } } /// /// Force reload default config object from disk /// public static void LoadConfig() { DefaultSingleton = LoadConfigPrivate(); } private static Config LoadConfigPrivate() { Config LoadedConfig = new Config(); string ExePath = Assembly.GetEntryAssembly().Location; string ExeFolder = Path.GetDirectoryName(ExePath); string ConfigPath = Path.Combine(ExeFolder, "CrashReportProcess.config"); if (File.Exists(ConfigPath)) { if (!string.IsNullOrEmpty(ConfigPath)) { using (XmlReader Reader = XmlReader.Create(ConfigPath)) { CrashReporterProcessServicer.WriteEvent("Loading config from " + ConfigPath); XmlSerializer Xml = new XmlSerializer(typeof (Config)); LoadedConfig = Xml.Deserialize(Reader) as Config; } } } #if DEBUG // Debug mode redirects to local folder in DebugTestingFolder LoadedConfig.ProcessedReports = Path.Combine(LoadedConfig.DebugTestingFolder, "ProcessedReports"); LoadedConfig.ProcessedVideos = Path.Combine(LoadedConfig.DebugTestingFolder, "ProcessedVideos"); LoadedConfig.DepotRoot = Path.Combine(LoadedConfig.DebugTestingFolder, "DepotRoot"); if (!string.IsNullOrWhiteSpace(LoadedConfig.InternalLandingZone)) { LoadedConfig.InternalLandingZone = Path.Combine(LoadedConfig.DebugTestingFolder, "InternalLandingZone"); } if (!string.IsNullOrWhiteSpace(LoadedConfig.ExternalLandingZone)) { LoadedConfig.ExternalLandingZone = Path.Combine(LoadedConfig.DebugTestingFolder, "ExternalLandingZone"); } if (!string.IsNullOrWhiteSpace(LoadedConfig.DataRouterLandingZone)) { LoadedConfig.DataRouterLandingZone = Path.Combine(LoadedConfig.DebugTestingFolder, "DataRouterLandingZone"); } if (!string.IsNullOrWhiteSpace(LoadedConfig.PS4LandingZone)) { LoadedConfig.PS4LandingZone = Path.Combine(LoadedConfig.DebugTestingFolder, "PS4LandingZone"); } if (!string.IsNullOrWhiteSpace(LoadedConfig.MDDExecutablePath)) { LoadedConfig.MDDExecutablePath = Path.Combine(LoadedConfig.DebugTestingFolder, "MinidumpDiagnostics", "Engine", "Binaries", "Win64", "MinidumpDiagnostics.exe"); } LoadedConfig.VersionString += " debugbuild"; LoadedConfig.AWSCredentialsFilepath = Path.Combine(LoadedConfig.DebugTestingFolder, "AWS", "credentials.ini"); if (!string.IsNullOrWhiteSpace(LoadedConfig.ProcessedReportsIndexPath)) { LoadedConfig.ProcessedReportsIndexPath = Path.Combine(LoadedConfig.DebugTestingFolder, "ProcessedReports.ini"); } LoadedConfig.CrashReportWebSite = string.Empty; LoadedConfig.AWSS3OutputKeyPrefix = LoadedConfig.AWSS3OutputKeyPrefix.Replace("prod", "test"); LoadedConfig.AWSS3InvalidKeyPrefix = LoadedConfig.AWSS3InvalidKeyPrefix.Replace("prod", "test"); LoadedConfig.MinDesiredMemoryQueueSize = 5; LoadedConfig.MaxMemoryQueueSize = 15; #if SLACKTESTING LoadedConfig.SlackUsername = "CrashReportProcess_TESTING_IgnoreMe"; //LoadedConfig.SlackChannel = "OPTIONALTESTINGCHANNELHERE"; #else LoadedConfig.SlackWebhookUrl = string.Empty; // no Slack in dbeug #endif #endif return LoadedConfig; } private static Config DefaultSingleton; } }