/* * Copyright 2018 faddenSoft * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; namespace CommonUtil { /// /// Collects timestamps from a series of events. Events may be nested, but may not overlap. /// A summary of task durations can be written to a log. /// public class TaskTimer { // TODO(maybe): create a start/end pair that works with a "using" directive to ensure // that a given item is always closed. /// /// Timed task info. /// private class TimedItem { public string mTag; public int mIndentLevel; public DateTime mStartWhen; public DateTime mEndWhen; public TimedItem(string tag, int indentLevel) { mTag = tag; mIndentLevel = indentLevel; mStartWhen = DateTime.Now; } } /// /// List of items. They are ordered by when the tasks ended. /// private List mItems = new List(); /// /// Indentation level. Cosmetic. /// private int mIndentLevel = 0; /// /// Place where next record is to be inserted. /// /// We keep inserting records ahead of whatever we inserted last, only advancing /// the insertion point when we close a record. Essentially a stack that moves /// forward when you pop() instead of removing the element. This lets us handle /// nested tasks correctly. /// private int mInsertPoint = 0; /// /// Resets object to initial state. /// public void Clear() { mItems.Clear(); mIndentLevel = mInsertPoint = 0; } /// /// Adds a start record for a task. /// /// Task tag. public void StartTask(string tag) { TimedItem ti = new TimedItem(tag, mIndentLevel); mItems.Insert(mInsertPoint, ti); mIndentLevel++; } /// /// Closes out a record. The tag must match the most recently started task. /// /// Task tag. public void EndTask(string tag) { TimedItem lastItem = mItems[mInsertPoint]; if (lastItem.mTag != tag) { Debug.WriteLine("ERROR: tag mismatch: " + tag + " vs. " + lastItem.mTag); Debug.Assert(false); return; } lastItem.mEndWhen = DateTime.Now; mIndentLevel--; mInsertPoint++; Debug.Assert(mIndentLevel >= 0); } /// /// Prints the timing data into a log object. /// /// Output destination. /// Header message. public void DumpTimes(string msg, DebugLog log) { if (mItems.Count == 0) { return; } if (!string.IsNullOrEmpty(msg)) { log.LogI(msg); } StringBuilder sb = new StringBuilder(); int lastIndent = 0; foreach (TimedItem ti in mItems) { sb.Clear(); FormatItem(ti, ref lastIndent, sb); log.LogI(sb.ToString()); } //DateTime firstStart = mItems[0].mStartWhen; //DateTime lastEnd = mItems[mItems.Count - 1].mEndWhen; //log.LogI(" Total: " + (lastEnd - firstStart).TotalMilliseconds + " ms"); } /// /// Prints the timing data into the debug log. /// /// Header message. public void DumpTimes(string msg) { if (mItems.Count == 0) { return; } if (!string.IsNullOrEmpty(msg)) { Debug.WriteLine(msg); } StringBuilder sb = new StringBuilder(); int lastIndent = 0; foreach (TimedItem ti in mItems) { sb.Clear(); FormatItem(ti, ref lastIndent, sb); Debug.WriteLine(sb.ToString()); } } /// /// Prints the timing data into a string with newlines. /// /// Output destination. /// Header message. public string DumpToString(string msg) { if (mItems.Count == 0) { return msg; } StringBuilder sb = new StringBuilder(); #if DEBUG sb.Append("[NOTE: debug build -- assertions and extra checks are enabled]\r\n\r\n"); #endif if (!string.IsNullOrEmpty(msg)) { sb.Append(msg); sb.Append("\r\n\r\n"); } int lastIndent = 0; foreach (TimedItem ti in mItems) { FormatItem(ti, ref lastIndent, sb); sb.Append("\r\n"); } return sb.ToString(); } /// /// Formats the specified item, appending it to the StringBuilder. /// /// Item to format. /// Previous indentation level. /// StringBuilder to append to. private void FormatItem(TimedItem ti, ref int lastIndent, StringBuilder sb) { for (int i = 0; i <= ti.mIndentLevel - 1; i++) { sb.Append("| "); } if (lastIndent < ti.mIndentLevel) { //sb.Append("/-"); sb.Append("/ "); } else /*if (lastIndent == ti.mIndentLevel)*/ { sb.Append("| "); } sb.Append(ti.mTag); sb.Append(": "); sb.Append((ti.mEndWhen - ti.mStartWhen).TotalMilliseconds.ToString()); sb.Append(" ms"); lastIndent = ti.mIndentLevel; } } }