event.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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 metrics
  15. // This file contains the functionality to represent a build event in respect
  16. // to the metric system. A build event corresponds to a block of scoped code
  17. // that contains a "Begin()" and immediately followed by "defer End()" trace.
  18. // When defined, the duration of the scoped code is measure along with other
  19. // performance measurements such as memory.
  20. //
  21. // As explained in the metrics package, the metrics system is a stacked based
  22. // system since the collected metrics is considered to be topline metrics.
  23. // The steps of the build system in the UI layer is sequential. Hence, the
  24. // functionality defined below follows the stack data structure operations.
  25. import (
  26. "os"
  27. "syscall"
  28. "time"
  29. soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
  30. "google.golang.org/protobuf/proto"
  31. )
  32. // _now wraps the time.Now() function. _now is declared for unit testing purpose.
  33. var _now = func() time.Time {
  34. return time.Now()
  35. }
  36. // event holds the performance metrics data of a single build event.
  37. type event struct {
  38. // The event name (mostly used for grouping a set of events)
  39. name string
  40. // The description of the event (used to uniquely identify an event
  41. // for metrics analysis).
  42. desc string
  43. nonZeroExitCode bool
  44. errorMsg *string
  45. // The time that the event started to occur.
  46. start time.Time
  47. // The list of process resource information that was executed.
  48. procResInfo []*soong_metrics_proto.ProcessResourceInfo
  49. }
  50. // newEvent returns an event with start populated with the now time.
  51. func newEvent(name, desc string) *event {
  52. return &event{
  53. name: name,
  54. desc: desc,
  55. start: _now(),
  56. }
  57. }
  58. func (e event) perfInfo() soong_metrics_proto.PerfInfo {
  59. realTime := uint64(_now().Sub(e.start).Nanoseconds())
  60. perfInfo := soong_metrics_proto.PerfInfo{
  61. Description: proto.String(e.desc),
  62. Name: proto.String(e.name),
  63. StartTime: proto.Uint64(uint64(e.start.UnixNano())),
  64. RealTime: proto.Uint64(realTime),
  65. ProcessesResourceInfo: e.procResInfo,
  66. NonZeroExit: proto.Bool(e.nonZeroExitCode),
  67. }
  68. if m := e.errorMsg; m != nil {
  69. perfInfo.ErrorMessage = proto.String(*m)
  70. }
  71. return perfInfo
  72. }
  73. // EventTracer is an array of events that provides functionality to trace a
  74. // block of code on time and performance. The End call expects the Begin is
  75. // invoked, otherwise panic is raised.
  76. type EventTracer []*event
  77. // empty returns true if there are no pending events.
  78. func (t *EventTracer) empty() bool {
  79. return len(*t) == 0
  80. }
  81. // lastIndex returns the index of the last element of events.
  82. func (t *EventTracer) lastIndex() int {
  83. return len(*t) - 1
  84. }
  85. // peek returns the active build event.
  86. func (t *EventTracer) peek() *event {
  87. if t.empty() {
  88. return nil
  89. }
  90. return (*t)[t.lastIndex()]
  91. }
  92. // push adds the active build event in the stack.
  93. func (t *EventTracer) push(e *event) {
  94. *t = append(*t, e)
  95. }
  96. // pop removes the active event from the stack since the event has completed.
  97. // A panic is raised if there are no pending events.
  98. func (t *EventTracer) pop() *event {
  99. if t.empty() {
  100. panic("Internal error: No pending events")
  101. }
  102. e := (*t)[t.lastIndex()]
  103. *t = (*t)[:t.lastIndex()]
  104. return e
  105. }
  106. // AddProcResInfo adds information on an executed process such as max resident
  107. // set memory and the number of voluntary context switches.
  108. func (t *EventTracer) AddProcResInfo(name string, state *os.ProcessState) {
  109. if t.empty() {
  110. return
  111. }
  112. rusage := state.SysUsage().(*syscall.Rusage)
  113. e := t.peek()
  114. e.procResInfo = append(e.procResInfo, &soong_metrics_proto.ProcessResourceInfo{
  115. Name: proto.String(name),
  116. UserTimeMicros: proto.Uint64(uint64(state.UserTime().Microseconds())),
  117. SystemTimeMicros: proto.Uint64(uint64(state.SystemTime().Microseconds())),
  118. MinorPageFaults: proto.Uint64(uint64(rusage.Minflt)),
  119. MajorPageFaults: proto.Uint64(uint64(rusage.Majflt)),
  120. // ru_inblock and ru_oublock are measured in blocks of 512 bytes.
  121. IoInputKb: proto.Uint64(uint64(rusage.Inblock / 2)),
  122. IoOutputKb: proto.Uint64(uint64(rusage.Oublock / 2)),
  123. VoluntaryContextSwitches: proto.Uint64(uint64(rusage.Nvcsw)),
  124. InvoluntaryContextSwitches: proto.Uint64(uint64(rusage.Nivcsw)),
  125. })
  126. }
  127. // Begin starts tracing the event.
  128. func (t *EventTracer) Begin(name, desc string) {
  129. t.push(newEvent(name, desc))
  130. }
  131. // End performs post calculations such as duration of the event, aggregates
  132. // the collected performance information into PerfInfo protobuf message.
  133. func (t *EventTracer) End() soong_metrics_proto.PerfInfo {
  134. return t.pop().perfInfo()
  135. }