code.gitea.io/gitea@v1.21.7/routers/private/manager_process.go (about)

     1  // Copyright 2022 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package private
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"net/http"
    11  	"runtime"
    12  	"time"
    13  
    14  	"code.gitea.io/gitea/modules/context"
    15  	"code.gitea.io/gitea/modules/log"
    16  	"code.gitea.io/gitea/modules/private"
    17  	process_module "code.gitea.io/gitea/modules/process"
    18  )
    19  
    20  // Processes prints out the processes
    21  func Processes(ctx *context.PrivateContext) {
    22  	pid := ctx.FormString("cancel-pid")
    23  	if pid != "" {
    24  		process_module.GetManager().Cancel(process_module.IDType(pid))
    25  		runtime.Gosched()
    26  		time.Sleep(100 * time.Millisecond)
    27  	}
    28  
    29  	flat := ctx.FormBool("flat")
    30  	noSystem := ctx.FormBool("no-system")
    31  	stacktraces := ctx.FormBool("stacktraces")
    32  	json := ctx.FormBool("json")
    33  
    34  	var processes []*process_module.Process
    35  	goroutineCount := int64(0)
    36  	var processCount int
    37  	var err error
    38  	if stacktraces {
    39  		processes, processCount, goroutineCount, err = process_module.GetManager().ProcessStacktraces(flat, noSystem)
    40  		if err != nil {
    41  			log.Error("Unable to get stacktrace: %v", err)
    42  			ctx.JSON(http.StatusInternalServerError, private.Response{
    43  				Err: fmt.Sprintf("Failed to get stacktraces: %v", err),
    44  			})
    45  			return
    46  		}
    47  	} else {
    48  		processes, processCount = process_module.GetManager().Processes(flat, noSystem)
    49  	}
    50  
    51  	if json {
    52  		ctx.JSON(http.StatusOK, map[string]any{
    53  			"TotalNumberOfGoroutines": goroutineCount,
    54  			"TotalNumberOfProcesses":  processCount,
    55  			"Processes":               processes,
    56  		})
    57  		return
    58  	}
    59  
    60  	ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
    61  	ctx.Resp.WriteHeader(http.StatusOK)
    62  
    63  	if err := writeProcesses(ctx.Resp, processes, processCount, goroutineCount, "", flat); err != nil {
    64  		log.Error("Unable to write out process stacktrace: %v", err)
    65  		if !ctx.Written() {
    66  			ctx.JSON(http.StatusInternalServerError, private.Response{
    67  				Err: fmt.Sprintf("Failed to get stacktraces: %v", err),
    68  			})
    69  		}
    70  		return
    71  	}
    72  }
    73  
    74  func writeProcesses(out io.Writer, processes []*process_module.Process, processCount int, goroutineCount int64, indent string, flat bool) error {
    75  	if goroutineCount > 0 {
    76  		if _, err := fmt.Fprintf(out, "%sTotal Number of Goroutines: %d\n", indent, goroutineCount); err != nil {
    77  			return err
    78  		}
    79  	}
    80  	if _, err := fmt.Fprintf(out, "%sTotal Number of Processes: %d\n", indent, processCount); err != nil {
    81  		return err
    82  	}
    83  	if len(processes) > 0 {
    84  		if err := writeProcess(out, processes[0], "  ", flat); err != nil {
    85  			return err
    86  		}
    87  	}
    88  	if len(processes) > 1 {
    89  		for _, process := range processes[1:] {
    90  			if _, err := fmt.Fprintf(out, "%s  | \n", indent); err != nil {
    91  				return err
    92  			}
    93  			if err := writeProcess(out, process, "  ", flat); err != nil {
    94  				return err
    95  			}
    96  		}
    97  	}
    98  	return nil
    99  }
   100  
   101  func writeProcess(out io.Writer, process *process_module.Process, indent string, flat bool) error {
   102  	sb := &bytes.Buffer{}
   103  	if flat {
   104  		if process.ParentPID != "" {
   105  			_, _ = fmt.Fprintf(sb, "%s+ PID: %s\t\tType: %s\n", indent, process.PID, process.Type)
   106  		} else {
   107  			_, _ = fmt.Fprintf(sb, "%s+ PID: %s:%s\tType: %s\n", indent, process.ParentPID, process.PID, process.Type)
   108  		}
   109  	} else {
   110  		_, _ = fmt.Fprintf(sb, "%s+ PID: %s\tType: %s\n", indent, process.PID, process.Type)
   111  	}
   112  	indent += "| "
   113  
   114  	_, _ = fmt.Fprintf(sb, "%sDescription: %s\n", indent, process.Description)
   115  	_, _ = fmt.Fprintf(sb, "%sStart:       %s\n", indent, process.Start)
   116  
   117  	if len(process.Stacks) > 0 {
   118  		_, _ = fmt.Fprintf(sb, "%sGoroutines:\n", indent)
   119  		for _, stack := range process.Stacks {
   120  			indent := indent + "  "
   121  			_, _ = fmt.Fprintf(sb, "%s+ Description: %s", indent, stack.Description)
   122  			if stack.Count > 1 {
   123  				_, _ = fmt.Fprintf(sb, "* %d", stack.Count)
   124  			}
   125  			_, _ = fmt.Fprintf(sb, "\n")
   126  			indent += "| "
   127  			if len(stack.Labels) > 0 {
   128  				_, _ = fmt.Fprintf(sb, "%sLabels:      %q:%q", indent, stack.Labels[0].Name, stack.Labels[0].Value)
   129  
   130  				if len(stack.Labels) > 1 {
   131  					for _, label := range stack.Labels[1:] {
   132  						_, _ = fmt.Fprintf(sb, ", %q:%q", label.Name, label.Value)
   133  					}
   134  				}
   135  				_, _ = fmt.Fprintf(sb, "\n")
   136  			}
   137  			_, _ = fmt.Fprintf(sb, "%sStack:\n", indent)
   138  			indent += "  "
   139  			for _, entry := range stack.Entry {
   140  				_, _ = fmt.Fprintf(sb, "%s+ %s\n", indent, entry.Function)
   141  				_, _ = fmt.Fprintf(sb, "%s| %s:%d\n", indent, entry.File, entry.Line)
   142  			}
   143  		}
   144  	}
   145  	if _, err := out.Write(sb.Bytes()); err != nil {
   146  		return err
   147  	}
   148  	sb.Reset()
   149  	if len(process.Children) > 0 {
   150  		if _, err := fmt.Fprintf(out, "%sChildren:\n", indent); err != nil {
   151  			return err
   152  		}
   153  		for _, child := range process.Children {
   154  			if err := writeProcess(out, child, indent+"  ", flat); err != nil {
   155  				return err
   156  			}
   157  		}
   158  	}
   159  	return nil
   160  }