private static void DrawStackFrame(int i, string a, string filepath, bool fileExist) { StackTraceSettings settings = HQ.Settings.Get <StackTraceSettings>(); Rect r = GUILayoutUtility.GetRect(0F, settings.height, settings.Style); float width = r.width; // Substract viewRect to avoid scrollbar. r.height = settings.height; // Display the stack trace. r.width = width - 16F; FrameBuilder.Clear(); FrameBuilder.returnType = "Void"; FrameBuilder.namespaceName = "namespace"; FrameBuilder.classType = "class"; FrameBuilder.methodName = "func"; FrameBuilder.fileName = filepath; FrameBuilder.fileExist = fileExist; FrameBuilder.line = 1234; FrameBuilder.parameterTypes.Add("Rect"); FrameBuilder.parameterNames.Add("r"); FrameBuilder.parameterTypes.Add("int"); FrameBuilder.parameterNames.Add("a"); if (i == 0) { GUI.Button(r, FrameBuilder.ToString("→ ", settings), settings.Style); } else { GUI.Button(r, FrameBuilder.ToString("↑ ", settings), settings.Style); } r.x = r.width; r.width = 16F; GUI.Button(r, "+", settings.Style); r.y += r.height; }
private Frame ParseStackFrame(string raw, int n) { Frame frame = new Frame(); frame.raw = raw; FrameBuilder.Clear(); StringBuilder buffer = Utility.GetBuffer(); try { // Exception/Error log. if ((this.log.mode & (Mode.ScriptingException | Mode.ScriptingError | Mode.Error | Mode.Fatal | Mode.GraphCompileError)) != 0) { buffer.Append(raw); // For convenience, just convert it to a normal log, using the StringBuilder, also for convenience. int lastDot = 0; for (int i = 0; i < buffer.Length; ++i) { // Detect next symbol, skip constructors and static constructors. (".ctor" and ".cctor") if (buffer[i] == '.' && i > 0 && buffer[i - 1] != '.' && buffer[i - 1] != ':') { lastDot = i; } else if (buffer[i] == ':') { lastDot = i; } // Delete any spaces before parameters. else if (buffer[i] == ' ') { buffer.Remove(i, 1); --i; } // Find start of method and avoid special prefix starting with a parenthese. if (buffer[i] == '(' && i > 0) { buffer[lastDot] = ':'; break; } } raw = buffer.ToString(); buffer.Length = 0; } // Handle "Rethrow as Exception:" if (raw.StartsWith("Rethrow") == true) { return(null); } // Too much stacks get truncated possibly anywhere. if (raw.EndsWith("<messagetruncated>") == true || raw.EndsWith("<message truncated>") == true || raw.EndsWith("<message truncated") == true) { return(null); } //InternalNGDebug.LogFile("raw="+raw); StackTraceSettings stackTrace = HQ.Settings.Get <StackTraceSettings>(); bool unreachableFrame = false; int endClass = raw.IndexOf(':'); FrameBuilder.classType = raw.Substring(0, endClass); int endMethod = raw.IndexOf('(', endClass); FrameBuilder.methodName = raw.Substring(endClass + 1, endMethod - endClass - 1); int endParameters = raw.IndexOf(')', endMethod); string allParameters = raw.Substring(endMethod + 1, endParameters - endMethod - 1); int startFilepath = raw.IndexOf("at ", endMethod); // Handle yield context, i.e. "Namespace.Class.<Method>c__Iterator23:MoveNext". if (FrameBuilder.classType.Contains("<") == true) { unreachableFrame = true; buffer.Append(FrameBuilder.classType); int startYieldMethod = FrameBuilder.classType.IndexOf('<'); int endYieldMethod = FrameBuilder.classType.IndexOf('>'); buffer.Remove(endYieldMethod, buffer.Length - endYieldMethod); buffer.Remove(0, startYieldMethod + 1); if (stackTrace.displayReturnValue == true) { FrameBuilder.returnType = "YieldInstruction"; } FrameBuilder.classType = ""; FrameBuilder.methodName = buffer.ToString(); buffer.Length = 0; } // Handle special prefix, i.e. "(wrapper remoting-invoke-with-check) Namespace.Class". else if (FrameBuilder.classType.Contains("(") == true) { buffer.Append(FrameBuilder.classType); buffer.Remove(0, FrameBuilder.classType.IndexOf(')') + 1); FrameBuilder.classType = buffer.ToString(); buffer.Length = 0; } // Handle generic method, i.e. "Namespace.Class.Method[Type]". else if (FrameBuilder.methodName.Contains("[") == true) { buffer.Append(FrameBuilder.methodName); int startGenericType = FrameBuilder.methodName.IndexOf('['); int endGenericType = FrameBuilder.methodName.LastIndexOf(']'); buffer.Remove(startGenericType, endGenericType - startGenericType + 1); FrameBuilder.methodName = buffer.ToString(); buffer.Length = 0; } // Split namespace from class. int m = FrameBuilder.classType.LastIndexOf('.'); if (m != -1) { FrameBuilder.namespaceName = FrameBuilder.classType.Substring(0, m); FrameBuilder.classType = FrameBuilder.classType.Substring(m + 1); } //InternalNGDebug.LogFile("class=" + FrameBuilder.classType); //InternalNGDebug.LogFile("method=" + FrameBuilder.methodName); //InternalNGDebug.LogFile("param=" + allParameters); if (startFilepath != -1) { int endFilepath = raw.LastIndexOf(':', raw.Length - 1); string filepath = raw.Substring(startFilepath + 3, endFilepath - startFilepath - 3); int endLine = raw.IndexOf(')', endFilepath); string line = raw.Substring(endFilepath + 1, endLine - endFilepath - 1); // Skip non-editable frame. frame.fileExist = ConsoleUtility.files.FileExist(filepath); if (frame.fileExist == false && stackTrace.skipUnreachableFrame == true) { return(null); } frame.fileName = filepath; frame.line = int.Parse(line); FrameBuilder.fileExist = frame.fileExist; //InternalNGDebug.LogFile("filepath="+filepath); //InternalNGDebug.LogFile("line=" + int.Parse(line)); } // It is not guaranteed that the fileName is the first frame. Use it in the only case of total failure. i.e. Debug.Log*() else if (string.IsNullOrEmpty(this.log.file) == true && n == 0) { frame.fileName = this.log.file; frame.line = this.log.line; } else { frame.fileName = string.Empty; } FrameBuilder.fileName = frame.fileName; FrameBuilder.line = frame.line; if (FrameBuilder.classType == string.Empty && string.IsNullOrEmpty(frame.fileName) == true) { return(null); } if (unreachableFrame == false && (stackTrace.displayReturnValue == true || stackTrace.displayArgumentName == true)) { FastClassCache.HashClass classType = ConsoleUtility.classes.GetType(FrameBuilder.namespaceName, FrameBuilder.classType); if (classType != null) { if (FrameBuilder.methodName == ".cctor" || // Static constructor. FrameBuilder.methodName == ".ctor") // Normal constructor. { string[] parameters = allParameters.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); // Many parameters includes ambiguity. if (parameters.Length > 0 && classType.type != null) { ConstructorInfo ci = this.GetConstructorFromParameters(classType.type, parameters); // Append data from the found constructor. if (ci != null) { FrameBuilder.namespaceName = ci.ReflectedType.Namespace; FrameBuilder.classType = ci.ReflectedType.Name; if (stackTrace.displayArgumentType == true || stackTrace.displayArgumentName == true) { this.AppendParameters(ci.GetParameters()); } } // Append every data as we can from the condition. else { this.AddConditionParameters(parameters); } } } else if (classType.methods != null) { MethodInfo methodInfo = classType.methods.GetMethod(FrameBuilder.methodName, allParameters); // Append data from the found method. if (methodInfo != null) { FrameBuilder.isStaticMethod = methodInfo.IsStatic; NGLoggerAttribute[] attributes = methodInfo.GetCustomAttributes(typeof(NGLoggerAttribute), false) as NGLoggerAttribute[]; if (attributes.Length > 0) { frame.category = attributes[0].tag; } if (stackTrace.displayReturnValue == true) { FrameBuilder.returnType = methodInfo.ReturnType.Name; } if (methodInfo.ReflectedType.IsGenericTypeDefinition == true) { m = FrameBuilder.classType.IndexOf("["); if (m != -1) { string[] genericTypes = FrameBuilder.classType.Substring(m + 1, FrameBuilder.classType.Length - m - 2).Split(','); // Removed the 2 brackets. for (int i = 0; i < genericTypes.Length; i++) { Type type = Utility.GetType(genericTypes[i]); if (type != null) { FrameBuilder.genericTypes.Add(type.Name); } else { FrameBuilder.genericTypes.Add(genericTypes[i]); } } } else { foreach (var type in methodInfo.ReflectedType.GetGenericArguments()) { FrameBuilder.genericTypes.Add(type.Name); } } } FrameBuilder.namespaceName = methodInfo.ReflectedType.Namespace; FrameBuilder.classType = methodInfo.ReflectedType.Name; FrameBuilder.methodName = methodInfo.Name; if (stackTrace.displayArgumentType == true || stackTrace.displayArgumentName == true) { this.AppendParameters(methodInfo.GetParameters()); } } // Append every data as we can from the condition. else { string[] parameters = allParameters.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); this.AddConditionParameters(parameters); } } } } // Get custom Category from NGSettings, it does not have priority over attribute! if (frame.category == null) { string methodName = FrameBuilder.namespaceName + '.' + FrameBuilder.classType + ':' + FrameBuilder.methodName; for (int i = 0; i < stackTrace.categories.Count; i++) { if (stackTrace.categories[i].method == methodName) { frame.category = stackTrace.categories[i].category; break; } } } if (n == 0) { frame.frameString = FrameBuilder.ToString("→ ", stackTrace); } else { frame.frameString = FrameBuilder.ToString("↑ ", stackTrace); } //if (HQ.Settings.general.horizontalScrollbar == true) //{ // // Calcul max width // Utility.content.text = frame.frameString; // Vector2 size = HQ.Settings.stackTrace.style.CalcSize(Utility.content); // if (this.maxWidthFrame < size.x) // this.maxWidthFrame = size.x; //} } catch (Exception ex) { InternalNGDebug.LogFileException("Raw " + n + "=" + raw + Environment.NewLine + FrameBuilder.ToRawString(), ex); InternalNGDebug.LogFile(frame.frameString); return(null); } finally { Utility.RestoreBuffer(buffer); } return(frame); }