// Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package private import ( "bytes" "fmt" "io" "net/http" "runtime" "time" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" process_module "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/services/context" ) // Processes prints out the processes func Processes(ctx *context.PrivateContext) { pid := ctx.FormString("cancel-pid") if pid != "" { process_module.GetManager().Cancel(process_module.IDType(pid)) runtime.Gosched() time.Sleep(100 * time.Millisecond) } flat := ctx.FormBool("flat") noSystem := ctx.FormBool("no-system") stacktraces := ctx.FormBool("stacktraces") json := ctx.FormBool("json") var processes []*process_module.Process goroutineCount := int64(0) var processCount int var err error if stacktraces { processes, processCount, goroutineCount, err = process_module.GetManager().ProcessStacktraces(flat, noSystem) if err != nil { log.Error("Unable to get stacktrace: %v", err) ctx.JSON(http.StatusInternalServerError, private.Response{ Err: fmt.Sprintf("Failed to get stacktraces: %v", err), }) return } } else { processes, processCount = process_module.GetManager().Processes(flat, noSystem) } if json { ctx.JSON(http.StatusOK, map[string]any{ "TotalNumberOfGoroutines": goroutineCount, "TotalNumberOfProcesses": processCount, "Processes": processes, }) return } ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8") ctx.Resp.WriteHeader(http.StatusOK) if err := writeProcesses(ctx.Resp, processes, processCount, goroutineCount, "", flat); err != nil { log.Error("Unable to write out process stacktrace: %v", err) if !ctx.Written() { ctx.JSON(http.StatusInternalServerError, private.Response{ Err: fmt.Sprintf("Failed to get stacktraces: %v", err), }) } return } } func writeProcesses(out io.Writer, processes []*process_module.Process, processCount int, goroutineCount int64, indent string, flat bool) error { if goroutineCount > 0 { if _, err := fmt.Fprintf(out, "%sTotal Number of Goroutines: %d\n", indent, goroutineCount); err != nil { return err } } if _, err := fmt.Fprintf(out, "%sTotal Number of Processes: %d\n", indent, processCount); err != nil { return err } if len(processes) > 0 { if err := writeProcess(out, processes[0], " ", flat); err != nil { return err } } if len(processes) > 1 { for _, process := range processes[1:] { if _, err := fmt.Fprintf(out, "%s | \n", indent); err != nil { return err } if err := writeProcess(out, process, " ", flat); err != nil { return err } } } return nil } func writeProcess(out io.Writer, process *process_module.Process, indent string, flat bool) error { sb := &bytes.Buffer{} if flat { if process.ParentPID != "" { _, _ = fmt.Fprintf(sb, "%s+ PID: %s\t\tType: %s\n", indent, process.PID, process.Type) } else { _, _ = fmt.Fprintf(sb, "%s+ PID: %s:%s\tType: %s\n", indent, process.ParentPID, process.PID, process.Type) } } else { _, _ = fmt.Fprintf(sb, "%s+ PID: %s\tType: %s\n", indent, process.PID, process.Type) } indent += "| " _, _ = fmt.Fprintf(sb, "%sDescription: %s\n", indent, process.Description) _, _ = fmt.Fprintf(sb, "%sStart: %s\n", indent, process.Start) if len(process.Stacks) > 0 { _, _ = fmt.Fprintf(sb, "%sGoroutines:\n", indent) for _, stack := range process.Stacks { indent := indent + " " _, _ = fmt.Fprintf(sb, "%s+ Description: %s", indent, stack.Description) if stack.Count > 1 { _, _ = fmt.Fprintf(sb, "* %d", stack.Count) } _, _ = fmt.Fprintf(sb, "\n") indent += "| " if len(stack.Labels) > 0 { _, _ = fmt.Fprintf(sb, "%sLabels: %q:%q", indent, stack.Labels[0].Name, stack.Labels[0].Value) if len(stack.Labels) > 1 { for _, label := range stack.Labels[1:] { _, _ = fmt.Fprintf(sb, ", %q:%q", label.Name, label.Value) } } _, _ = fmt.Fprintf(sb, "\n") } _, _ = fmt.Fprintf(sb, "%sStack:\n", indent) indent += " " for _, entry := range stack.Entry { _, _ = fmt.Fprintf(sb, "%s+ %s\n", indent, entry.Function) _, _ = fmt.Fprintf(sb, "%s| %s:%d\n", indent, entry.File, entry.Line) } } } if _, err := out.Write(sb.Bytes()); err != nil { return err } sb.Reset() if len(process.Children) > 0 { if _, err := fmt.Fprintf(out, "%sChildren:\n", indent); err != nil { return err } for _, child := range process.Children { if err := writeProcess(out, child, indent+" ", flat); err != nil { return err } } } return nil }