status.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. // Copyright 2018 Google Inc. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Package status tracks actions run by various tools, combining the counts
  15. // (total actions, currently running, started, finished), and giving that to
  16. // multiple outputs.
  17. package status
  18. import (
  19. "sync"
  20. )
  21. // Action describes an action taken (or as Ninja calls them, Edges).
  22. type Action struct {
  23. // Description is a shorter, more readable form of the command, meant
  24. // for users. It's optional, but one of either Description or Command
  25. // should be set.
  26. Description string
  27. // Outputs is the (optional) list of outputs. Usually these are files,
  28. // but they can be any string.
  29. Outputs []string
  30. // Inputs is the (optional) list of inputs. Usually these are files,
  31. // but they can be any string.
  32. Inputs []string
  33. // Command is the actual command line executed to perform the action.
  34. // It's optional, but one of either Description or Command should be
  35. // set.
  36. Command string
  37. }
  38. // ActionResult describes the result of running an Action.
  39. type ActionResult struct {
  40. // Action is a pointer to the original Action struct.
  41. *Action
  42. // Output is the output produced by the command (usually stdout&stderr
  43. // for Actions that run commands)
  44. Output string
  45. // Error is nil if the Action succeeded, or set to an error if it
  46. // failed.
  47. Error error
  48. Stats ActionResultStats
  49. }
  50. type ActionResultStats struct {
  51. // Number of milliseconds spent executing in user mode
  52. UserTime uint32
  53. // Number of milliseconds spent executing in kernel mode
  54. SystemTime uint32
  55. // Max resident set size in kB
  56. MaxRssKB uint64
  57. // Minor page faults
  58. MinorPageFaults uint64
  59. // Major page faults
  60. MajorPageFaults uint64
  61. // IO input in kB
  62. IOInputKB uint64
  63. // IO output in kB
  64. IOOutputKB uint64
  65. // Voluntary context switches
  66. VoluntaryContextSwitches uint64
  67. // Involuntary context switches
  68. InvoluntaryContextSwitches uint64
  69. Tags string
  70. }
  71. // Counts describes the number of actions in each state
  72. type Counts struct {
  73. // TotalActions is the total number of expected changes. This can
  74. // generally change up or down during a build, but it should never go
  75. // below the number of StartedActions
  76. TotalActions int
  77. // RunningActions are the number of actions that are currently running
  78. // -- the number that have called StartAction, but not FinishAction.
  79. RunningActions int
  80. // StartedActions are the number of actions that have been started with
  81. // StartAction.
  82. StartedActions int
  83. // FinishedActions are the number of actions that have been finished
  84. // with FinishAction.
  85. FinishedActions int
  86. }
  87. // ToolStatus is the interface used by tools to report on their Actions, and to
  88. // present other information through a set of messaging functions.
  89. type ToolStatus interface {
  90. // SetTotalActions sets the expected total number of actions that will
  91. // be started by this tool.
  92. //
  93. // This call be will ignored if it sets a number that is less than the
  94. // current number of started actions.
  95. SetTotalActions(total int)
  96. // StartAction specifies that the associated action has been started by
  97. // the tool.
  98. //
  99. // A specific *Action should not be specified to StartAction more than
  100. // once, even if the previous action has already been finished, and the
  101. // contents rewritten.
  102. //
  103. // Do not re-use *Actions between different ToolStatus interfaces
  104. // either.
  105. StartAction(action *Action)
  106. // FinishAction specifies the result of a particular Action.
  107. //
  108. // The *Action embedded in the ActionResult structure must have already
  109. // been passed to StartAction (on this interface).
  110. //
  111. // Do not call FinishAction twice for the same *Action.
  112. FinishAction(result ActionResult)
  113. // Verbose takes a non-important message that is never printed to the
  114. // screen, but is in the verbose build log, etc
  115. Verbose(msg string)
  116. // Status takes a less important message that may be printed to the
  117. // screen, but overwritten by another status message. The full message
  118. // will still appear in the verbose build log.
  119. Status(msg string)
  120. // Print takes an message and displays it to the screen and other
  121. // output logs, etc.
  122. Print(msg string)
  123. // Error is similar to Print, but treats it similarly to a failed
  124. // action, showing it in the error logs, etc.
  125. Error(msg string)
  126. // Finish marks the end of all Actions being run by this tool.
  127. //
  128. // SetTotalEdges, StartAction, and FinishAction should not be called
  129. // after Finish.
  130. Finish()
  131. }
  132. // MsgLevel specifies the importance of a particular log message. See the
  133. // descriptions in ToolStatus: Verbose, Status, Print, Error.
  134. type MsgLevel int
  135. const (
  136. VerboseLvl MsgLevel = iota
  137. StatusLvl
  138. PrintLvl
  139. ErrorLvl
  140. )
  141. func (l MsgLevel) Prefix() string {
  142. switch l {
  143. case VerboseLvl:
  144. return "verbose: "
  145. case StatusLvl:
  146. return "status: "
  147. case PrintLvl:
  148. return ""
  149. case ErrorLvl:
  150. return "error: "
  151. default:
  152. panic("Unknown message level")
  153. }
  154. }
  155. // StatusOutput is the interface used to get status information as a Status
  156. // output.
  157. //
  158. // All of the functions here are guaranteed to be called by Status while
  159. // holding it's internal lock, so it's safe to assume a single caller at any
  160. // time, and that the ordering of calls will be correct. It is not safe to call
  161. // back into the Status, or one of its ToolStatus interfaces.
  162. type StatusOutput interface {
  163. // StartAction will be called once every time ToolStatus.StartAction is
  164. // called. counts will include the current counters across all
  165. // ToolStatus instances, including ones that have been finished.
  166. StartAction(action *Action, counts Counts)
  167. // FinishAction will be called once every time ToolStatus.FinishAction
  168. // is called. counts will include the current counters across all
  169. // ToolStatus instances, including ones that have been finished.
  170. FinishAction(result ActionResult, counts Counts)
  171. // Message is the equivalent of ToolStatus.Verbose/Status/Print/Error,
  172. // but the level is specified as an argument.
  173. Message(level MsgLevel, msg string)
  174. // Flush is called when your outputs should be flushed / closed. No
  175. // output is expected after this call.
  176. Flush()
  177. // Write lets StatusOutput implement io.Writer
  178. Write(p []byte) (n int, err error)
  179. }
  180. // Status is the multiplexer / accumulator between ToolStatus instances (via
  181. // StartTool) and StatusOutputs (via AddOutput). There's generally one of these
  182. // per build process (though tools like multiproduct_kati may have multiple
  183. // independent versions).
  184. type Status struct {
  185. counts Counts
  186. outputs []StatusOutput
  187. // Protects counts and outputs, and allows each output to
  188. // expect only a single caller at a time.
  189. lock sync.Mutex
  190. }
  191. // AddOutput attaches an output to this object. It's generally expected that an
  192. // output is attached to a single Status instance.
  193. func (s *Status) AddOutput(output StatusOutput) {
  194. if output == nil {
  195. return
  196. }
  197. s.lock.Lock()
  198. defer s.lock.Unlock()
  199. s.outputs = append(s.outputs, output)
  200. }
  201. // StartTool returns a new ToolStatus instance to report the status of a tool.
  202. func (s *Status) StartTool() ToolStatus {
  203. return &toolStatus{
  204. status: s,
  205. }
  206. }
  207. // Finish will call Flush on all the outputs, generally flushing or closing all
  208. // of their outputs. Do not call any other functions on this instance or any
  209. // associated ToolStatus instances after this has been called.
  210. func (s *Status) Finish() {
  211. s.lock.Lock()
  212. defer s.lock.Unlock()
  213. for _, o := range s.outputs {
  214. o.Flush()
  215. }
  216. }
  217. func (s *Status) updateTotalActions(diff int) {
  218. s.lock.Lock()
  219. defer s.lock.Unlock()
  220. s.counts.TotalActions += diff
  221. }
  222. func (s *Status) startAction(action *Action) {
  223. s.lock.Lock()
  224. defer s.lock.Unlock()
  225. s.counts.RunningActions += 1
  226. s.counts.StartedActions += 1
  227. for _, o := range s.outputs {
  228. o.StartAction(action, s.counts)
  229. }
  230. }
  231. func (s *Status) finishAction(result ActionResult) {
  232. s.lock.Lock()
  233. defer s.lock.Unlock()
  234. s.counts.RunningActions -= 1
  235. s.counts.FinishedActions += 1
  236. for _, o := range s.outputs {
  237. o.FinishAction(result, s.counts)
  238. }
  239. }
  240. func (s *Status) message(level MsgLevel, msg string) {
  241. s.lock.Lock()
  242. defer s.lock.Unlock()
  243. for _, o := range s.outputs {
  244. o.Message(level, msg)
  245. }
  246. }
  247. func (s *Status) Status(msg string) {
  248. s.message(StatusLvl, msg)
  249. }
  250. type toolStatus struct {
  251. status *Status
  252. counts Counts
  253. // Protects counts
  254. lock sync.Mutex
  255. }
  256. var _ ToolStatus = (*toolStatus)(nil)
  257. func (d *toolStatus) SetTotalActions(total int) {
  258. diff := 0
  259. d.lock.Lock()
  260. if total >= d.counts.StartedActions && total != d.counts.TotalActions {
  261. diff = total - d.counts.TotalActions
  262. d.counts.TotalActions = total
  263. }
  264. d.lock.Unlock()
  265. if diff != 0 {
  266. d.status.updateTotalActions(diff)
  267. }
  268. }
  269. func (d *toolStatus) StartAction(action *Action) {
  270. totalDiff := 0
  271. d.lock.Lock()
  272. d.counts.RunningActions += 1
  273. d.counts.StartedActions += 1
  274. if d.counts.StartedActions > d.counts.TotalActions {
  275. totalDiff = d.counts.StartedActions - d.counts.TotalActions
  276. d.counts.TotalActions = d.counts.StartedActions
  277. }
  278. d.lock.Unlock()
  279. if totalDiff != 0 {
  280. d.status.updateTotalActions(totalDiff)
  281. }
  282. d.status.startAction(action)
  283. }
  284. func (d *toolStatus) FinishAction(result ActionResult) {
  285. d.lock.Lock()
  286. d.counts.RunningActions -= 1
  287. d.counts.FinishedActions += 1
  288. d.lock.Unlock()
  289. d.status.finishAction(result)
  290. }
  291. func (d *toolStatus) Verbose(msg string) {
  292. d.status.message(VerboseLvl, msg)
  293. }
  294. func (d *toolStatus) Status(msg string) {
  295. d.status.message(StatusLvl, msg)
  296. }
  297. func (d *toolStatus) Print(msg string) {
  298. d.status.message(PrintLvl, msg)
  299. }
  300. func (d *toolStatus) Error(msg string) {
  301. d.status.message(ErrorLvl, msg)
  302. }
  303. func (d *toolStatus) Finish() {
  304. d.lock.Lock()
  305. defer d.lock.Unlock()
  306. if d.counts.TotalActions != d.counts.StartedActions {
  307. d.status.updateTotalActions(d.counts.StartedActions - d.counts.TotalActions)
  308. }
  309. // TODO: update status to correct running/finished edges?
  310. d.counts.RunningActions = 0
  311. d.counts.TotalActions = d.counts.StartedActions
  312. }