public override RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { bool richpresenceFound = false; var richPresenceJsonObj = new { activity = new Object() }; foreach (var call in items) { var richPresenceResult = JsonConvert.DeserializeAnonymousType(call.m_reqBody, richPresenceJsonObj); if (richPresenceResult != null && richPresenceResult.activity != null) { richpresenceFound = true; break; } } RuleResult result = InitializeResult(DisplayName, ""); if (!richpresenceFound) { result.Violations.Add(new Violation()); } return(result); }
public override RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); //check invalid log versions if (items.Count(item => item.m_logVersion == Constants.Version1509) > 0) { result.AddViolation(ViolationLevel.Warning, "Data version does not support this rule. You need an updated Xbox Live SDK to support this rule"); return(result); } StringBuilder description = new StringBuilder(); // Traverse through each pattern set found in rule parameter foreach (var pattern in m_MatchPatterns) { Dictionary <ServiceCallItem, int> matchesFoundDict = new Dictionary <ServiceCallItem, int>(); int patternInstancesFound = 0; int lowXUIDInstancesFound = 0; // This first section reports on violations which are from batch calls made with not enough XUIDs in the request body foreach (ServiceCallItem thisItem in items) { Match match = Regex.Match(thisItem.m_uri, pattern.Key); if (match.Success) { if (!thisItem.m_reqHeader.Contains("SocialManager")) { patternInstancesFound++; try { JObject requestBodyJSON = JObject.Parse(thisItem.m_reqBody); var values = requestBodyJSON.SelectToken(pattern.Value) as JArray; if (values != null) { matchesFoundDict.Add(thisItem, values.Count); if (values.Count < m_minBatchXUIDsPerBatchCall) { lowXUIDInstancesFound++; description.Clear(); description.AppendFormat("Batch call detected for endpoint for a small sized set of {0} XUIDs.", values.Count); result.AddViolation(ViolationLevel.Warning, description.ToString(), thisItem); } } } catch (Exception) { // ignored } } } } // finished traversing calls made to endpoint m_patternInstancesFound.Add(new Tuple <int, int>(patternInstancesFound, lowXUIDInstancesFound)); } // end of foreach pattern in patterns var finalStats = PatternsFoundSumAsTuple(); result.Results.Add("Total Batch Calls", finalStats.Item1); result.Results.Add("Min. Users Allowed", m_minBatchXUIDsPerBatchCall); result.Results.Add("Calls Below Count", finalStats.Item2); result.Results.Add("% Below Count", (double)(finalStats.Item2) / finalStats.Item1); return(result); }
override public RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); m_stats = stats; m_endpointSustainedViolations = 0; m_endpointBurstViolations = 0; // For set of limits, look through items to determine where excess calls occurred foreach (var limits in m_rateLimits) { // Filter the full list of service calls to those which apply to this set of limits List <ServiceCallItem> applicableCalls = items.Where(serviceCall => { foreach (var subpath in limits.m_applicableSubpaths) { var subpathRegex = new Regex("^" + Regex.Escape(subpath).Replace("\\?", ".").Replace("\\*", ".*") + "$"); if (subpathRegex.IsMatch(new Uri(serviceCall.m_uri).AbsolutePath)) { return(true); } } return(false); }).ToList(); var sustainedExcessCallsPerWindow = Utils.GetExcessCallsForTimeWindow(applicableCalls, limits.m_sustainedTimePeriodSeconds * 1000, limits.m_sustainedCallLimit); var burstExcessCallsPerWindow = Utils.GetExcessCallsForTimeWindow(applicableCalls, limits.m_burstTimePeriodSeconds * 1000, limits.m_burstCallLimit); foreach (var excessCalls in sustainedExcessCallsPerWindow) { if (excessCalls.Count >= limits.m_sustainedCallLimit * 10) { var desc = $"Exceeding rate limits for '{limits.m_description}' required for title certification( limit of {limits.m_sustainedCallLimit * 10} calls with {excessCalls.Count} calls in {limits.m_sustainedTimePeriodSeconds} seconds). Failure to adhere to the specified limits may block a title from release, and in-production issues with released titles may result in service suspension up to and including title removal."; result.AddViolation(ViolationLevel.Error, desc, excessCalls); } else { var desc = $"Call frequency above the sustained call limit for '{limits.m_description}' (limit of {limits.m_sustainedCallLimit} exceeded with {excessCalls.Count} calls in {limits.m_sustainedTimePeriodSeconds} seconds)."; result.AddViolation(ViolationLevel.Warning, desc, excessCalls); } m_endpointSustainedViolations++; } foreach (var excessCalls in burstExcessCallsPerWindow) { var desc = $"Call frequency above the burst call limit for '{limits.m_description}' (limit of {limits.m_burstCallLimit} exceeded with {excessCalls.Count} calls in {limits.m_burstTimePeriodSeconds} seconds)."; result.AddViolation(ViolationLevel.Warning, desc, excessCalls); m_endpointBurstViolations++; } // The following is information that would only be useful for internal purposes. if (RulesEngine.m_isInternal) { UInt64 avgTimeBetweenReqsMs = stats.m_avgTimeBetweenReqsMs; UInt64 avgElapsedCallTimeMs = stats.m_avgElapsedCallTimeMs; UInt64 maxElapsedCallTimeMs = stats.m_maxElapsedCallTimeMs; if (avgTimeBetweenReqsMs > 0 && avgTimeBetweenReqsMs < limits.m_avgTimeBetweenReqsMs) { result.AddViolation(ViolationLevel.Warning, "Average time of " + avgTimeBetweenReqsMs + "ms between calls is too short"); } if (avgElapsedCallTimeMs > 0 && avgElapsedCallTimeMs > limits.m_avgElapsedCallTimeMs) { result.AddViolation(ViolationLevel.Warning, "Calls are taking longer than expected to return " + avgElapsedCallTimeMs + "ms"); } if (maxElapsedCallTimeMs > 0 && maxElapsedCallTimeMs > limits.m_maxElapsedCallTimeMs) { result.AddViolation(ViolationLevel.Warning, "The maximum call time for calls is greater than allowed " + maxElapsedCallTimeMs + "ms"); } } } result.Results.Add("Total Calls", m_stats == null ? 0 : m_stats.m_numCalls); result.Results.Add("Times Sustained Exceeded", m_endpointSustainedViolations); result.Results.Add("Times Burst Exceeded", m_endpointBurstViolations); return(result); }
public JObject ReportRule(RuleResult rule) { JObject jsonRule = new JObject(); if (rule == null) { return(null); } if (!s_supportedRules.Contains(rule.RuleName)) { return(null); } JObject jsonResult = new JObject(); jsonRule["Name"] = rule.RuleName; jsonRule["Description"] = rule.RuleDescription; foreach (var data in rule.Results) { jsonResult[data.Key] = data.Value.ToString(); } jsonRule["ResultData"] = jsonResult; foreach (ViolationLevel level in System.Enum.GetValues(typeof(ViolationLevel))) { jsonRule[System.Enum.GetName(typeof(ViolationLevel), level)] = rule.Violations.Count(v => v.m_level == level); } if (jsonRule[System.Enum.GetName(typeof(ViolationLevel), ViolationLevel.Error)].ToObject <Int32>() > 0) { jsonRule["Result"] = System.Enum.GetName(typeof(ViolationLevel), ViolationLevel.Error); } else if (jsonRule[System.Enum.GetName(typeof(ViolationLevel), ViolationLevel.Warning)].ToObject <Int32>() > 0) { jsonRule["Result"] = System.Enum.GetName(typeof(ViolationLevel), ViolationLevel.Warning); } else { jsonRule["Result"] = "Pass"; } JArray jsonViolations = new JArray(); foreach (var violation in rule.Violations) { JObject jsonViolation = new JObject(); jsonViolation["Level"] = System.Enum.GetName(typeof(ViolationLevel), violation.m_level); jsonViolation["Summary"] = violation.m_summary; JArray calls = new JArray(); foreach (var call in violation.m_violatingCalls) { JObject jsonCall = new JObject(); jsonCall["Call Id"] = call.m_id; jsonCall["UriMethod"] = call.m_uri; if (call.m_xsapiMethods != null) { jsonCall["CppMethod"] = call.m_xsapiMethods.Item1; jsonCall["WinRTMethod"] = call.m_xsapiMethods.Item2; } calls.Add(jsonCall); } jsonViolation["Calls"] = calls; jsonViolations.Add(jsonViolation); } jsonRule["Violations"] = jsonViolations; return(jsonRule); }
public override RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); if (items.Count() == 0) { return(result); } //check invalid log versions if (items.Count(item => item.m_logVersion == Constants.Version1509) > 0) { result.AddViolation(ViolationLevel.Warning, "Data version does not support this rule. You need an updated Xbox Live SDK to support this rule"); return(result); } List <ServiceCallItem> allRepeats = new List <ServiceCallItem>(); var nonShoulderTapItems = items.Where(item => item.m_isShoulderTap == false); List <Sequence> sequences = new List <Sequence>(); foreach (ServiceCallItem thisItem in nonShoulderTapItems) { if (thisItem.m_reqHeader.Contains("SocialManager")) { continue; } // Used to skip over already analyzed repeated calls. if (allRepeats.Contains(thisItem)) { continue; } // Discover all repeats to thisItem var myRepeats = from item in nonShoulderTapItems where ((item != thisItem) && (item.m_reqBodyHash == thisItem.m_reqBodyHash) && (item.m_uri == thisItem.m_uri)) select item; if (myRepeats.Count() > 1) { var deltas = BuildDeltas(myRepeats.ToList()); sequences.AddRange(GenerateSequences(deltas, m_sameDeltaThresholdPercentage)); allRepeats.AddRange(myRepeats); } } if (sequences == null) { return(result); } //Process Violations StringBuilder description = new StringBuilder(); foreach (var sequence in sequences.OrderByDescending(s => s.Length)) { description.Clear(); description.AppendFormat("Polling detected in a sequence of {0} calls at a rate of approximately {1} min:sec", sequence.Length, TimeSpan.FromTicks((long)sequence.m_averageDelta).ToString(@"mm\:ss\.fff")); result.AddViolation(ViolationLevel.Warning, description.ToString(), sequence.m_seqCalls); } result.Results.Add("Polling Sequences Found", result.ViolationCount); return(result); }
// Creates a RuleResult with a cleaner display veersion of the Rule's name, Endpoint and a custom description. protected RuleResult InitializeResult(String displayName, String description) { RuleResult result = new RuleResult(displayName, Endpoint, description); return(result); }
public override RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); m_totalCalls = items.Count(); m_throttledCallsCount = items.Where(call => call.m_httpStatusCode == 429).Count(); // We need to search over all of the calls to the endpoint for (int i = 0; i < m_totalCalls;) { var call = items.ElementAt(i); // If its not a throttled call, move to the next call if (call.m_httpStatusCode != 429) { ++i; continue; } // If it is throttled, start a list List <ServiceCallItem> throttledCallSet = new List <ServiceCallItem>(); throttledCallSet.Add(items.ElementAt(i)); var throttledCall = throttledCallSet.First(); string throttleGuidline = string.Empty; try { if (!string.IsNullOrEmpty(throttledCall.m_rspBody)) { JObject response = JObject.Parse(throttledCall.m_rspBody); if (response["maxRequests"] != null && response["periodInSeconds"] != null) { throttleGuidline = $"Allowed: {response["maxRequests"]} over {response["periodInSeconds"]} seconds."; } } } catch (Exception) { } // If there are 2 or more throttled calls in a row the title is not properly handling the response while (++i < m_throttledCallsCount) { var nextCall = items.ElementAt(i); if (call.m_httpStatusCode != 429) { ++i; break; } else { throttledCallSet.Add(call); } } // One call is a warning as we expect that they back off after getting the 429 response if (throttledCallSet.Count == 1) { result.AddViolation(ViolationLevel.Warning, "Throttled call detected on endpoint. " + throttleGuidline, throttledCall); } // More that one in a row means that the title didn't handle the 429 and we want them to fix that. else { result.AddViolation(ViolationLevel.Error, "Sequence of throttled calls detected on endpoint. " + throttleGuidline, throttledCallSet); } throttledCallSet.Clear(); } result.Results.Add("Total Calls", TotalCalls); result.Results.Add("Throttled Calls", ThrottledCallCount); result.Results.Add("Percentage", ((double)ThrottledCallCount) / TotalCalls); return(result); }
override public RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); if (items.Count() == 0) { return(result); } //check invalid log versions if (items.Count(item => item.m_logVersion == Constants.Version1509) > 0) { result.AddViolation(ViolationLevel.Warning, "Data version does not support this rule. You need an updated Xbox Live SDK to support this rule"); return(result); } StringBuilder description = new StringBuilder(); List <ServiceCallItem> repeats = new List <ServiceCallItem>(); m_totalCallsChecked = items.Count(); foreach (ServiceCallItem thisItem in items.Where(item => item.m_isShoulderTap == false)) { if (thisItem.m_reqHeader.Contains("SocialManager")) { continue; } if (repeats.Contains(thisItem)) { continue; } var timeWindow = from item in items.Where(item => item.m_isShoulderTap == false) where (item.m_reqTimeUTC > thisItem.m_reqTimeUTC && ((item.m_reqTimeUTC - thisItem.m_reqTimeUTC) / TimeSpan.TicksPerMillisecond) < m_minAllowedRepeatIntervalMs) select item; List <ServiceCallItem> repeatedCalls = new List <ServiceCallItem>(); repeatedCalls.Add(thisItem); foreach (var call in timeWindow) { if (thisItem.m_reqBodyHash == call.m_reqBodyHash && thisItem.m_uri == call.m_uri) { repeatedCalls.Add(call); } } if (repeatedCalls.Count > 1) { description.Clear(); description.AppendFormat("Repeated call found {0} other times in calls to endpoint.", repeatedCalls.Count, thisItem.m_id); result.AddViolation(ViolationLevel.Warning, description.ToString(), repeatedCalls); repeats.AddRange(repeatedCalls); } } m_numberOfRepeats = repeats.Count; result.Results.Add("Total Calls", m_totalCallsChecked); result.Results.Add("Duplicates", m_numberOfRepeats); result.Results.Add("Percentage", ((double)m_numberOfRepeats) / m_totalCallsChecked); return(result); }
public override RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); //check invalid log versions if (items.Count(item => item.m_logVersion == Constants.Version1509) > 0) { result.AddViolation(ViolationLevel.Warning, "Data version does not support this rule. You need an updated Xbox Live SDK to support this rule"); return(result); } StringBuilder description = new StringBuilder(); // Traverse through each pattern set found in rule parameter foreach (var pattern in m_MatchPatterns) { Dictionary <ServiceCallItem, int> matchesFoundDict = new Dictionary <ServiceCallItem, int>(); foreach (ServiceCallItem thisItem in items) { Match match = Regex.Match(thisItem.m_uri, pattern.Key); if (match.Success) { JObject requestBodyJSON = JObject.Parse(thisItem.m_reqBody); var values = requestBodyJSON.SelectToken(pattern.Value) as JArray; if (values != null) { matchesFoundDict.Add(thisItem, values.Count); } } } // finished traversing calls made to endpoint m_totalBatchCallCount = (UInt32)matchesFoundDict.Count; // For all the matches found, report on batch sets which happened within a specific time window int numDictItems = matchesFoundDict.Count; if (numDictItems >= 2) { int startWindowIdx = 0; List <ServiceCallItem> callsWithinWindow = new List <ServiceCallItem>(); int totalXUIDsForWindow = 0; totalXUIDsForWindow += matchesFoundDict.Values.ElementAt(startWindowIdx); callsWithinWindow.Add(matchesFoundDict.Keys.ElementAt(startWindowIdx)); for (int endWindowIdx = 1; endWindowIdx < matchesFoundDict.Count(); ++endWindowIdx) { UInt64 timeElapsed = (UInt64)Math.Abs((float) (matchesFoundDict.Keys.ElementAt(endWindowIdx).m_reqTimeUTC - matchesFoundDict.Keys.ElementAt(startWindowIdx).m_reqTimeUTC) / TimeSpan.TicksPerMillisecond); if (timeElapsed <= m_BatchSetDetectionWindowsMs) { callsWithinWindow.Add(matchesFoundDict.Keys.ElementAt(endWindowIdx)); totalXUIDsForWindow += matchesFoundDict.Values.ElementAt(endWindowIdx); } else //exceeded window { if (callsWithinWindow.Count >= 2) { result.AddViolation(ViolationLevel.Warning, $"A set of {callsWithinWindow.Count} Batch Calls was found within a time window of ({m_BatchSetDetectionWindowsMs}ms) to endpoint. Total XUID count ({totalXUIDsForWindow}). Consider combining into one call.", callsWithinWindow); } startWindowIdx = endWindowIdx; // shift window //reset figures totalXUIDsForWindow = 0; callsWithinWindow.Clear(); } } // in case we exited the last for early because we never exceeded the time window, then call // the following function once more to handle any dangling reports. if (callsWithinWindow.Count >= 2) { result.AddViolation(ViolationLevel.Warning, $"A set of {callsWithinWindow.Count} Batch Calls was found within a time window of ({m_BatchSetDetectionWindowsMs}ms) to endpoint. Total XUID count ({totalXUIDsForWindow}). Consider combining into one call.", callsWithinWindow); } } } // end of foreach pattern in patterns result.Results.Add("Total Batch Calls", m_totalBatchCallCount); result.Results.Add("Allowed Time Between Calls in ms", m_BatchSetDetectionWindowsMs); result.Results.Add("Times Exceeded", result.ViolationCount); result.Results.Add("Potential Reduced Call Count", m_totalBatchCallCount - result.ViolationCount); return(result); }
override public RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); m_stats = stats; m_endpointSustainedViolations = 0; m_endpointBurstViolations = 0; // Look through items to determine where excess calls occurred var sustainedExcessCallsPerWindow = Utils.GetExcessCallsForTimeWindow(items, m_sustainedTimePeriodSeconds * 1000, m_sustainedCallLimit); var burstExcessCallsPerWindow = Utils.GetExcessCallsForTimeWindow(items, m_burstTimePeriodSeconds * 1000, m_burstCallLimit); foreach (var excessCalls in sustainedExcessCallsPerWindow) { if (excessCalls.Count >= m_sustainedCallLimit * 10) { var desc = $"Exceeding service rate limits required for title certification( limit of {m_sustainedCallLimit * 10} calls with {excessCalls.Count} calls in {m_sustainedTimePeriodSeconds} seconds). Failure to adhere to the specified limits may block a title from release, and in-production issues with released titles may result in service suspension up to and including title removal."; result.AddViolation(ViolationLevel.Error, desc, excessCalls); } else { var desc = $"Call frequency above the sustained call limit of {m_sustainedCallLimit} with {excessCalls.Count} calls in {m_sustainedTimePeriodSeconds} seconds to endpoint."; result.AddViolation(ViolationLevel.Warning, desc, excessCalls); } m_endpointSustainedViolations++; } foreach (var excessCalls in burstExcessCallsPerWindow) { var desc = $"Call frequency above the burst call limit of {m_burstCallLimit} with {excessCalls.Count} calls {m_burstTimePeriodSeconds} seconds to endpoint."; result.AddViolation(ViolationLevel.Warning, desc, excessCalls); m_endpointBurstViolations++; } // The following is information that would only be useful for internal purposes. if (RulesEngine.m_isInternal) { UInt64 avgTimeBetweenReqsMs = stats.m_avgTimeBetweenReqsMs; UInt64 avgElapsedCallTimeMs = stats.m_avgElapsedCallTimeMs; UInt64 maxElapsedCallTimeMs = stats.m_maxElapsedCallTimeMs; if (avgTimeBetweenReqsMs > 0 && avgTimeBetweenReqsMs < m_avgTimeBetweenReqsMs) { result.AddViolation(ViolationLevel.Warning, "Average time of " + avgTimeBetweenReqsMs + "ms between calls is too short"); } if (avgElapsedCallTimeMs > 0 && avgElapsedCallTimeMs > m_avgElapsedCallTimeMs) { result.AddViolation(ViolationLevel.Warning, "Calls are taking longer than expected to return " + avgElapsedCallTimeMs + "ms"); } if (maxElapsedCallTimeMs > 0 && maxElapsedCallTimeMs > m_maxElapsedCallTimeMs) { result.AddViolation(ViolationLevel.Warning, "The maximum call time for calls is greater than allowed " + maxElapsedCallTimeMs + "ms"); } } result.Results.Add("Total Calls", m_stats == null ? 0 : m_stats.m_numCalls); result.Results.Add("Sustained Call Period", m_sustainedTimePeriodSeconds.ToString() + "sec."); result.Results.Add("Sustained Call Limit", m_sustainedCallLimit); result.Results.Add("Times Sustained Exceeded", m_endpointSustainedViolations); result.Results.Add("Burst Call Period", m_burstTimePeriodSeconds.ToString() + "sec."); result.Results.Add("Burst Call Limit", m_burstCallLimit); result.Results.Add("Times Burst Exceeded", m_endpointBurstViolations); return(result); }
override public RuleResult Run(IEnumerable <ServiceCallItem> items, ServiceCallStats stats) { RuleResult result = InitializeResult(DisplayName, Description); StringBuilder description = new StringBuilder(); m_stats = stats; m_endpointSustainedViolations = 0; m_endpointBurstViolations = 0; // Look through items to determine where excess calls occurred var sustainedExcessCallsPerWindow = Utils.GetExcessCallsForTimeWindow(items, m_sustainedTimePeriodSeconds * 1000, (UInt32)(m_sustainedCallLimit * .9)); var burstExcessCallsPerWindow = Utils.GetExcessCallsForTimeWindow(items, m_burstTimePeriodSeconds * 1000, (UInt32)(m_burstCallLimit * .9)); foreach (var excessCalls in sustainedExcessCallsPerWindow) { description.Clear(); if (excessCalls.Count < m_sustainedCallLimit) { description.AppendFormat("Call frequency nearing the sustained call limit of {0} with {1} calls to endpoint.", m_sustainedCallLimit, excessCalls.Count); result.AddViolation(ViolationLevel.Warning, description.ToString(), excessCalls); } else { m_endpointSustainedViolations++; description.AppendFormat("Call frequency above the sustained call limit of {0} with {1} calls to endpoint.", m_sustainedCallLimit, excessCalls.Count); result.AddViolation(ViolationLevel.Error, description.ToString(), excessCalls); } } foreach (var excessCalls in burstExcessCallsPerWindow) { description.Clear(); if (excessCalls.Count < m_burstCallLimit) { description.AppendFormat("Call frequency nearing the burst call limit of {0} with {1} calls to endpoint.", m_burstCallLimit, excessCalls.Count); result.AddViolation(ViolationLevel.Warning, description.ToString(), excessCalls); } else { m_endpointBurstViolations++; description.AppendFormat("Call frequency above the burst call limit of {0} with {1} calls to endpoint.", m_burstCallLimit, excessCalls.Count); result.AddViolation(ViolationLevel.Error, description.ToString(), excessCalls); } } // The following is information that would only be useful for internal purposes. if (RulesEngine.m_isInternal) { UInt64 avgTimeBetweenReqsMs = stats.m_avgTimeBetweenReqsMs; UInt64 avgElapsedCallTimeMs = stats.m_avgElapsedCallTimeMs; UInt64 maxElapsedCallTimeMs = stats.m_maxElapsedCallTimeMs; if (avgTimeBetweenReqsMs > 0 && avgTimeBetweenReqsMs < m_avgTimeBetweenReqsMs) { result.AddViolation(ViolationLevel.Warning, "Average time of " + avgTimeBetweenReqsMs + "ms between calls is too short"); } if (avgElapsedCallTimeMs > 0 && avgElapsedCallTimeMs > m_avgElapsedCallTimeMs) { result.AddViolation(ViolationLevel.Warning, "Calls are taking longer than expected to return " + avgElapsedCallTimeMs + "ms"); } if (maxElapsedCallTimeMs > 0 && maxElapsedCallTimeMs > m_maxElapsedCallTimeMs) { result.AddViolation(ViolationLevel.Warning, "The maximum call time for calls is greater than allowed " + maxElapsedCallTimeMs + "ms"); } } result.Results.Add("Total Calls", m_stats == null ? 0 : m_stats.m_numCalls); result.Results.Add("Sustained Call Period", m_sustainedTimePeriodSeconds.ToString() + "sec."); result.Results.Add("Sustained Call Limit", m_sustainedCallLimit); result.Results.Add("Times Sustained Exceeded", m_endpointSustainedViolations); result.Results.Add("Burst Call Period", m_burstTimePeriodSeconds.ToString() + "sec."); result.Results.Add("Burst Call Limit", m_burstCallLimit); result.Results.Add("Times Burst Exceeded", m_endpointBurstViolations); return(result); }