Пример #1
0
        /// <summary>
        /// Initialise the service to listen for reports.
        /// </summary>
        public WebHandler()
        {
            try
            {
                FileReceiptPath = Properties.Settings.Default.CrashReportRepository + "-Temp";

                LandingZone = new LandingZoneMonitor(
                    Properties.Settings.Default.CrashReportRepository,
                    CrashReporterReceiverServicer.Log
                    );

                Directory.CreateDirectory(FileReceiptPath);

                // Fire up a listener.
                ServiceHttpListener = new HttpListener();
                ServiceHttpListener.Prefixes.Add("http://*:57005/CrashReporter/");
                ServiceHttpListener.Start();
                ServiceHttpListener.BeginGetContext(AsyncHandleHttpRequest, null);

                bStartedSuccessfully = true;
            }
            catch (Exception Ex)
            {
                CrashReporterReceiverServicer.WriteEvent("Initialisation error: " + Ex.ToString());
            }
        }
Пример #2
0
        /// <summary>
        /// Save out the current list of received reports to disk
        /// </summary>
        void MemorizeReceivedReports()
        {
            var MemoPath = Path.Combine(Directory, MemoFilename);

            // Recreate the landing zone directory, just in case.
            System.IO.Directory.CreateDirectory(Directory);
            try
            {
                if (Interlocked.Exchange(ref MemoBeingWritten, 1) == 1)
                {
                    return;
                }


                new XElement
                (
                    "Root",
                    from Item in ReceivedReports.OrderBy(x => x.Value)
                    select new XElement("CrashReport", Item.Key, new XAttribute("time", Item.Value.ToString()))
                )
                .Save(MemoPath);
                CrashReporterReceiverServicer.WriteEvent("Saved to " + MemoPath);
                Interlocked.Exchange(ref MemoBeingWritten, 0);
            }
            catch (Exception Ex)
            {
                LogFile.Print("Failed to memorize received reports: " + Ex.Message);
                Interlocked.Exchange(ref MemoBeingWritten, 0);
            }
        }
Пример #3
0
        private void PoolPerformanceData()
        {
            while (true)
            {
                Int64 NumTotalFiles = NumFilesReceived + NumFilesSkipped;

                // Get the CPU for the last second.
                CurrentCPUUsage = PerfCounterProcessorTime.NextValue();

                if (CurrentReceivedData > 0 && CurrentTotalUploadCount > 0)
                {
                    CrashReporterReceiverServicer.WritePerf
                    (
                        string.Format("CPU Usage: {0:00}%, Received data: {1,5}MB/{2:00.00} of {4:00.00} MB, Uploads:{3,3}, Files:{5,6},{6,6} : {7:00}%",
                                      CurrentCPUUsage,
                                      (Int64)Math.Truncate(MBInv * TotalReceivedData),
                                      MBInv * CurrentReceivedData,
                                      UploadCount,
                                      MBInv * MaxAllowedBandwidth,
                                      NumFilesReceived, NumFilesSkipped,
                                      NumTotalFiles > 0 ? 100.0f * NumFilesSkipped / NumTotalFiles : 0.0f)
                    );
                }

                Interlocked.Add(ref TotalReceivedData, CurrentReceivedData);

                // Reset the the performance data.
                Interlocked.Exchange(ref CurrentReceivedData, 0);
                Interlocked.Exchange(ref CurrentTotalUploadCount, 0);

                // Sleep.
                System.Threading.Thread.Sleep(1000);
            }
        }
Пример #4
0
        /// <summary>
        /// Periodically delete folders of abandoned reports
        /// </summary>
        void CheckForAbandonedReports()
        {
            if (Interlocked.Exchange(ref CheckingForAbandonedReports, 1) == 1)
            {
                // Being checked by another thread, so no need
                return;
            }

            try
            {
                var Now = DateTime.Now;

                // No need to do this very often
                if ((Now - LastAbandonedReportCheckTime).Minutes < MinimumMinutesBetweenAbandonedReportChecks)
                {
                    return;
                }
                LastAbandonedReportCheckTime = Now;

                try
                {
                    foreach (string Dir in Directory.EnumerateDirectories(FileReceiptPath))
                    {
                        var    LastAccessTime = new DateTime();
                        string ReportId       = new DirectoryInfo(Dir).Name;
                        bool   bGotAccessTime = UploadsInProgress.TryGetLastAccessTime(ReportId, ref LastAccessTime);
                        if (!bGotAccessTime || (Now - LastAccessTime).Minutes > AgeMinutesToConsiderReportAbandoned)
                        {
                            try
                            {
                                Directory.Delete(Dir, true /* delete all contents */);
                            }
                            catch (Exception Ex)
                            {
                                CrashReporterReceiverServicer.WriteEvent(string.Format("Failed to delete directory {0}: {1}", ReportId, Ex.Message));
                            }
                        }
                    }
                }
                catch (Exception Ex)
                {
                    // If we get in here, the reports won't get cleaned up. May happen if
                    // permissions are incorrect or the directory is locked
                    CrashReporterReceiverServicer.WriteEvent("Error during folder tidy: " + Ex.Message);
                }
            }
            finally
            {
                // Allow other threads to check again
                Interlocked.Exchange(ref CheckingForAbandonedReports, 0);
            }
        }
Пример #5
0
		/// <summary>
		/// The main entry point for crash report receiver application.
		/// </summary>
		static void Main()
		{
			CrashReporterReceiverServicer CrashReporterReceiver = new CrashReporterReceiverServicer();
			if( !Environment.UserInteractive )
			{
				// Launch the service as normal if we aren't in the debugger, or aren't in an interactive environment
				ServiceBase.Run( CrashReporterReceiver );
			}
			else
			{
				// Call OnStart, wait for a console key press, call OnStop
				CrashReporterReceiver.DebugRun();
			}
		}
Пример #6
0
        /// <summary>
        /// Check to see if we wish to reject a report based on the WER meta data.
        /// </summary>
        /// <param name="Request">A request containing the XML representation of a WERReportMetadata class instance.</param>
        /// <returns>true if we do not reject.</returns>
        private CrashReporterResult CheckReportDetail(HttpListenerRequest Request)
        {
            CrashReporterResult ReportResult = new CrashReporterResult();

#if DISABLED_CRR
            ReportResult.bSuccess = false;
            ReportResult.Message  = "CRR disabled";
            CrashReporterReceiverServicer.WriteEvent("CheckReportDetail() Report rejected by disabled CRR");
#else
            string            WERReportMetadataString = GetContentStreamString(Request);
            WERReportMetadata WERData = null;

            if (WERReportMetadataString.Length > 0)
            {
                try
                {
                    WERData = XmlHandler.FromXmlString <WERReportMetadata>(WERReportMetadataString);
                }
                catch (System.Exception Ex)
                {
                    CrashReporterReceiverServicer.WriteEvent("Error during XmlHandler.FromXmlString, probably incorrect encoding, trying to fix: " + Ex.Message);

                    byte[] StringBytes  = System.Text.Encoding.Unicode.GetBytes(WERReportMetadataString);
                    string ConvertedXML = System.Text.Encoding.UTF8.GetString(StringBytes);
                    WERData = XmlHandler.FromXmlString <WERReportMetadata>(ConvertedXML);
                }
            }

            if (WERData != null)
            {
                // Ignore crashes in the minidump parser itself
                ReportResult.bSuccess = true;
                if (WERData.ProblemSignatures.Parameter0.ToLower() == "MinidumpDiagnostics".ToLower())
                {
                    ReportResult.bSuccess = false;
                    ReportResult.Message  = "Rejecting MinidumpDiagnostics crash";
                }

                // Ignore Debug and DebugGame crashes
                string CrashingModule = WERData.ProblemSignatures.Parameter3.ToLower();
                if (CrashingModule.Contains("-debug"))
                {
                    ReportResult.bSuccess = false;
                    ReportResult.Message  = "Rejecting Debug or DebugGame crash";
                }
            }
#endif
            return(ReportResult);
        }
Пример #7
0
        /// <summary>
        /// The main entry point for crash report receiver application.
        /// </summary>
        static void Main()
        {
            CrashReporterReceiverServicer CrashReporterReceiver = new CrashReporterReceiverServicer();

            if (!Environment.UserInteractive)
            {
                // Launch the service as normal if we aren't in the debugger, or aren't in an interactive environment
                ServiceBase.Run(CrashReporterReceiver);
            }
            else
            {
                // Call OnStart, wait for a console key press, call OnStop
                CrashReporterReceiver.DebugRun();
            }
        }
        /// <summary>
        /// Check to see if a report has already been uploaded.
        /// </summary>
        /// <param name="Request">A request containing either the Report Id as a string or an XML representation of a CheckReportRequest class instance.</param>
        /// <returns>Result object, indicating whether the report has already been uploaded.</returns>
        private CrashReporterResult CheckReport(HttpListenerRequest Request)
        {
            var ReportResult = new CrashReporterResult();

            var RequestClass = new CheckReportRequest();

            RequestClass.ReportId = GetReportIdFromPostData(GetContentStreamString(Request));

            ReportResult.bSuccess = !LandingZone.HasReportAlreadyBeenReceived(RequestClass.ReportId);

            if (!ReportResult.bSuccess)
            {
                CrashReporterReceiverServicer.WriteEvent(string.Format("Report \"{0}\" has already been received", RequestClass.ReportId));
            }

            return(ReportResult);
        }
Пример #9
0
        /// <summary>
        /// Check to see if a report has already been uploaded.
        /// </summary>
        /// <param name="Request">A request containing either the Report Id as a string or an XML representation of a CheckReportRequest class instance.</param>
        /// <returns>Result object, indicating whether the report has already been uploaded.</returns>
        private CrashReporterResult CheckReport(HttpListenerRequest Request)
        {
            CrashReporterResult ReportResult = new CrashReporterResult();

#if DISABLED_CRR
            ReportResult.bSuccess = false;
            CrashReporterReceiverServicer.WriteEvent("CheckReport() Report rejected by disabled CRR");
#else
            var RequestClass = new CheckReportRequest();
            RequestClass.ReportId = GetReportIdFromPostData(GetContentStreamString(Request));

            ReportResult.bSuccess = !LandingZone.HasReportAlreadyBeenReceived(RequestClass.ReportId);

            if (!ReportResult.bSuccess)
            {
                CrashReporterReceiverServicer.WriteEvent(string.Format("Report \"{0}\" has already been received", RequestClass.ReportId));
            }
#endif
            return(ReportResult);
        }
Пример #10
0
        /// <summary>
        /// Move a successfully uploaded WER into the landing zone folder
        /// </summary>
        /// <param name="SourceDirInfo">Temporary directory WER was uploaded to</param>
        /// <param name="ReportId">Unique ID of the report, used as the folder name in the landing zone</param>
        public void ReceiveReport(DirectoryInfo SourceDirInfo, string ReportId)
        {
            string ReportPath = Path.Combine(Directory, ReportId);

            // Recreate the landing zone directory, just in case.
            System.IO.Directory.CreateDirectory(Directory);

            DirectoryInfo DirInfo = new DirectoryInfo(ReportPath);

            if (DirInfo.Exists)
            {
                DirInfo.Delete(true);
                DirInfo.Refresh();
            }

            // Retry the move in case the OS keeps some locks on the uploaded files longer than expected
            for (int Retry = 0; Retry != MoveUploadedReportRetryAttempts; ++Retry)
            {
                try
                {
                    SourceDirInfo.MoveTo(DirInfo.FullName);
                    break;
                }
                catch (Exception)
                {
                }
                System.Threading.Thread.Sleep(100);
            }

            CrashReporterReceiverServicer.WriteEvent("A new report has been received into " + DirInfo.FullName);

            ReceivedReports[ReportId] = DateTime.Now;

            // Ensure the receive reports memo file doesn't grow indefinitely
            ForgetOldReports();

            // Update file on disk, in case service is restarted
            MemorizeReceivedReports();
        }
Пример #11
0
        /// <summary> Spin loop for a while, maybe we hit an opportunity to start the upload. </summary>
        private bool TryWaitForStartOperation()
        {
            bool bCPUExceeded       = false;
            bool bBandwidthExceeded = false;

            for (int Index = 0; Index < 4; Index++)
            {
                Thread.Sleep(1000);
                bCPUExceeded       = CurrentCPUUsage > MaxAllowedCPUtilizationPct;
                bBandwidthExceeded = CurrentReceivedData > MaxAllowedBandwidth;

                if (!bCPUExceeded && !bBandwidthExceeded)
                {
                    // Found, continue.
                    CrashReporterReceiverServicer.WritePerf("TryWaitForStartOperation was successful");
                    return(true);
                }
            }

            // Give up.
            Interlocked.Increment(ref NumFilesSkipped);
            return(false);
        }
Пример #12
0
        /// <summary>
        /// The main listener callback to handle client requests.
        /// </summary>
        /// <param name="ClientRequest">The request from the client.</param>
        private void AsyncHandleHttpRequest(IAsyncResult ClientRequest)
        {
            try
            {
                HttpListenerContext Context = ServiceHttpListener.EndGetContext(ClientRequest);
                ServiceHttpListener.BeginGetContext(AsyncHandleHttpRequest, null);
                HttpListenerRequest Request = Context.Request;
                bool bIgnorePerfData        = false;

                using (HttpListenerResponse Response = Context.Response)
                {
                    // Extract the URL parameters
                    string[] UrlElements = Request.RawUrl.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

                    // http://*:57005/CrashReporter/CheckReport
                    // http://*:57005/CrashReporter/CheckReportDetail
                    CrashReporterResult ReportResult = new CrashReporterResult();
                    if (UrlElements[0].ToLower() == "crashreporter")
                    {
                        switch (UrlElements[1].ToLower())
                        {
                        case "ping":
                            ReportResult.bSuccess = true;
                            break;

                        case "checkreport":
                            ReportResult = CheckReport(Context.Request);
                            break;

                        case "checkreportdetail":
                            ReportResult = CheckReportDetail(Context.Request);
                            break;

                        case "uploadreportfile":
                            ReportResult    = ReceiveFile(Context.Request);
                            bIgnorePerfData = true;
                            break;

                        case "uploadcomplete":
                            ReportResult = UploadComplete(Context.Request);
                            break;

                        default:
                            ReportResult.bSuccess = false;
                            ReportResult.Message  = "Invalid command: " + UrlElements[1];
                            break;
                        }
                    }
                    else
                    {
                        ReportResult.bSuccess = false;
                        ReportResult.Message  = "Invalid application: " + UrlElements[0] + " (expecting CrashReporter)";
                    }

                    string ResponseString = XmlHandler.ToXmlString <CrashReporterResult>(ReportResult);

                    Response.SendChunked = true;
                    Response.ContentType = "text/xml";

                    byte[] Buffer = Encoding.UTF8.GetBytes(ResponseString);
                    Response.ContentLength64 = Buffer.Length;
                    Response.OutputStream.Write(Buffer, 0, Buffer.Length);

                    Response.StatusCode = ( int )HttpStatusCode.OK;

                    if (!bIgnorePerfData)
                    {
                        // Update the overhead data.
                        Int64 ContentLenght = Response.ContentLength64 + Request.ContentLength64;
                        Interlocked.Add(ref UploadsInProgress.CurrentReceivedData, ContentLenght);
                    }
                }
            }
            catch (Exception Ex)
            {
                CrashReporterReceiverServicer.WriteEvent("Error during async listen: " + Ex.Message);
            }
        }
Пример #13
0
        /// <summary>
        /// Receive a file and write it to a temporary folder.
        /// </summary>
        /// <param name="Request">A request containing the file details in the headers (DirectoryName/FileName/FileLength).</param>
        /// <returns>true if the file is received successfully.</returns>
        /// <remarks>There is an arbitrary file size limit of CrashReporterConstants.MaxFileSizeToUpload as a simple exploit prevention method.</remarks>
        private CrashReporterResult ReceiveFile(HttpListenerRequest Request)
        {
            CrashReporterResult ReportResult = new CrashReporterResult();

            if (!Request.HasEntityBody)
            {
                return(ReportResult);
            }

            // Take this opportunity to clean out folders for reports that were never completed
            CheckForAbandonedReports();

            // Make sure we have a sensible file size
            long BytesToReceive = 0;

            if (long.TryParse(Request.Headers["FileLength"], out BytesToReceive))
            {
                if (BytesToReceive >= CrashReporterConstants.MaxFileSizeToUpload)
                {
                    return(ReportResult);
                }
            }

            string DirectoryName = Request.Headers["DirectoryName"];
            string FileName      = Request.Headers["FileName"];
            var    T             = Request.ContentLength64;
            bool   bIsOverloaded = false;

            if (!UploadsInProgress.TryReceiveFile(DirectoryName, FileName, BytesToReceive, ref ReportResult.Message, ref bIsOverloaded))
            {
                CrashReporterReceiverServicer.WriteEvent(ReportResult.Message);
                ReportResult.bSuccess = false;
                return(ReportResult);
            }

            string PathName = Path.Combine(FileReceiptPath, DirectoryName, FileName);

            // Recreate the file receipt directory, just in case.
            Directory.CreateDirectory(FileReceiptPath);

            // Create the folder to save files to
            DirectoryInfo DirInfo = new DirectoryInfo(Path.GetDirectoryName(PathName));

            DirInfo.Create();

            // Make sure the file doesn't already exist. If it does, delete it.
            if (File.Exists(PathName))
            {
                File.Delete(PathName);
            }

            FileInfo   Info       = new FileInfo(PathName);
            FileStream FileWriter = Info.OpenWrite();

            // Read in the input stream from the request, and write to a file
            long OriginalBytesToReceive = BytesToReceive;

            try
            {
                using (BinaryReader Reader = new BinaryReader(Request.InputStream))
                {
                    byte[] Buffer = new byte[CrashReporterConstants.StreamChunkSize];

                    while (BytesToReceive > 0)
                    {
                        int BytesToRead = Math.Min((int)BytesToReceive, CrashReporterConstants.StreamChunkSize);
                        Interlocked.Add(ref UploadsInProgress.CurrentReceivedData, BytesToRead);

                        int ReceivedChunkSize = Reader.Read(Buffer, 0, BytesToRead);

                        if (ReceivedChunkSize == 0)
                        {
                            ReportResult.Message  = string.Format("Partial file \"{0}\" received", FileName);
                            ReportResult.bSuccess = false;
                            CrashReporterReceiverServicer.WriteEvent(ReportResult.Message);

                            return(ReportResult);
                        }

                        BytesToReceive -= ReceivedChunkSize;
                        FileWriter.Write(Buffer, 0, ReceivedChunkSize);
                    }
                }
            }
            finally
            {
                FileWriter.Close();
                Request.InputStream.Close();

                UploadsInProgress.FileUploadAttemptDone(OriginalBytesToReceive, bIsOverloaded);

                bool bWriteMetadata = Path.GetExtension(FileName) == ".ue4crash";
                if (bWriteMetadata)
                {
                    string CompressedSize   = Request.Headers["CompressedSize"];
                    string UncompressedSize = Request.Headers["UncompressedSize"];
                    string NumberOfFiles    = Request.Headers["NumberOfFiles"];

                    string MetadataPath = Path.Combine(FileReceiptPath, DirectoryName, Path.GetFileNameWithoutExtension(FileName) + ".xml");
                    XmlHandler.WriteXml <FCompressedCrashInformation>(new FCompressedCrashInformation(CompressedSize, UncompressedSize, NumberOfFiles), MetadataPath);
                }
            }

            ReportResult.bSuccess = true;
            return(ReportResult);
        }