You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

manager_process.go 4.6KB

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