Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(35)

Unified Diff: client/logdog/annotee/annotation/annotation.go

Issue 2069283002: milo: fix step duration (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-go@master
Patch Set: simplify step duration computation Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: client/logdog/annotee/annotation/annotation.go
diff --git a/client/logdog/annotee/annotation/annotation.go b/client/logdog/annotee/annotation/annotation.go
index 0c0d724ed2bb264a3da8312829ec6d83cfc1ce22..930f78e904625ba2f86374cca4812f05fd91639f 100644
--- a/client/logdog/annotee/annotation/annotation.go
+++ b/client/logdog/annotee/annotation/annotation.go
@@ -9,6 +9,7 @@ import (
"sort"
"strconv"
"strings"
+ "time"
"github.com/luci/luci-go/common/clock"
"github.com/luci/luci-go/common/logdog/types"
@@ -42,7 +43,12 @@ type State struct {
// Execution is the supplied Execution. If nil, no execution details will be
// added to the generated Milo protos.
Execution *Execution
+ // Offline specifies whether parsing happens not at the same time as
+ // emitting. If true and CURRENT_TIMESTAMP annotations are not provided
+ // then step start/end times are left empty.
+ Offline bool
// Clock is the clock implementation to use for time information.
+ // Defaults to system time.
Clock clock.Clock
// stepMap is a map of step name to Step instance.
@@ -54,9 +60,13 @@ type State struct {
// stepCursor is the current cursor step name. This will always point to a
// valid Step, falling back to rootStep.
stepCursor *Step
+ // startedProcessing is true iff processed at least one annotation.
+ startedProcessing bool
- closed bool
- haltOnFailure bool
+ // currentTimestamp is time for the next annotation expected in Append.
+ currentTimestamp *google.Timestamp
+ closed bool
+ haltOnFailure bool
}
// initialize sets of the State's initial state. It will execute exactly once,
@@ -103,7 +113,11 @@ func (s *State) initialize() {
}
}
- s.rootStep.Start()
+ var annotatedNow *google.Timestamp
+ if !s.Offline {
+ annotatedNow = s.now()
+ }
+ s.rootStep.Start(annotatedNow)
}
// Append adds an annotation to the state. If the state was updated, Append will
@@ -121,6 +135,9 @@ func (s *State) initialize() {
func (s *State) Append(annotation string) error {
s.initialize()
+ firstAnnotation := !s.startedProcessing
+ s.startedProcessing = true
+
command, params := annotation, ""
splitIdx := strings.IndexAny(command, "@ ")
if splitIdx > 0 {
@@ -138,7 +155,30 @@ func (s *State) Append(annotation string) error {
}
}
+ annotatedNow := s.currentTimestamp
+ s.currentTimestamp = nil
+ if annotatedNow == nil && !s.Offline {
+ annotatedNow = s.now()
+ }
+
switch command {
+ // @@@CURRENT_TIMESTAMP@unix_timestamp@@@
+ case "CURRENT_TIMESTAMP":
+ // This annotation is printed at the beginning and end of the
+ // stream, as well as before each STEP_STARTED and STEP_CLOSED
+ // annotations. It effectively specifies step start/end times,
+ // including root step.
+ timestamp, err := strconv.ParseFloat(params, 64)
+ if err != nil {
+ return fmt.Errorf("CURRENT_TIMESTAMP parameter %q is not a number: %s", params, err)
+ }
+ s.currentTimestamp = google.NewTimestamp(time.Unix(
+ int64(timestamp),
+ int64(timestamp*1000000000)%1000000000))
+ if firstAnnotation {
+ s.rootStep.StepComponent.Started = s.currentTimestamp
+ }
+
// @@@BUILD_STEP <stepname>@@@
case "BUILD_STEP":
// Close the last section.
@@ -148,11 +188,11 @@ func (s *State) Append(annotation string) error {
// Same step; ignore the command.
break
}
- step.Close()
+ step.Close(annotatedNow)
}
step = s.rootStep.AddStep(params)
- step.Start()
+ step.Start(annotatedNow)
s.SetCurrentStep(step)
updatedIf(step, true)
@@ -187,7 +227,7 @@ func (s *State) Append(annotation string) error {
// @@@STEP_STARTED@@@
case "STEP_STARTED":
step := s.CurrentStep()
- updatedIf(step, step.Start())
+ updatedIf(step, step.Start(annotatedNow))
// @@@STEP_WARNINGS@@@
case "BUILD_WARNINGS":
@@ -219,9 +259,9 @@ func (s *State) Append(annotation string) error {
// @@@STEP_CLOSED@@@
case "STEP_CLOSED":
step := s.CurrentStep()
- updatedIf(step, step.Close())
+ updatedIf(step, step.Close(annotatedNow))
- // @@@STEP_LOG_LINE@<label>@<line>@@@
+ // @@@STEP_LOG_LINE@<label>@<line>@@@
case "STEP_LOG_LINE":
step := s.CurrentStep()
@@ -321,24 +361,32 @@ func (s *State) finishWithStatus(st milo.Status) bool {
return s.finishWithStatusImpl(&st)
}
-func (s *State) finishWithStatusImpl(sp *milo.Status) bool {
+func (s *State) finishWithStatusImpl(status *milo.Status) bool {
if s.closed {
return false
}
+ // if s.currentTimestamp is not nil, the last annotation was
+ // CURRENT_TIMESTAMP and s.currentTimestamp contains its value.
+ buildEndTime := s.currentTimestamp
+ s.currentTimestamp = nil
+ if buildEndTime == nil && !s.Offline {
+ buildEndTime = s.now()
+ }
+
unfinished := false
for _, step := range s.steps[1:] {
- if u := step.closeWithStatus(nil); u {
+ if step.closeWithStatus(buildEndTime, nil) {
unfinished = true
}
}
// If some steps were unfinished, show a root exception.
- if unfinished && sp == nil {
- status := milo.Status_EXCEPTION
- sp = &status
+ if unfinished && status == nil {
+ exception := milo.Status_EXCEPTION
+ status = &exception
}
- s.rootStep.closeWithStatus(sp)
+ s.rootStep.closeWithStatus(buildEndTime, status)
// Probe the status from our steps, if one is not supplied.
s.closed = true
@@ -412,7 +460,8 @@ func (s *State) ForEachStep(f func(*Step)) {
}
}
-func (s *State) annotationNow() *google.Timestamp {
+// now returns current time of s.Clock. Defaults to system clock.
+func (s *State) now() *google.Timestamp {
c := s.Clock
if c == nil {
c = clock.GetSystemClock()
@@ -530,20 +579,21 @@ func (as *Step) AddStep(name string) *Step {
}
// Start marks the Step as started.
-func (as *Step) Start() bool {
+func (as *Step) Start(startTime *google.Timestamp) bool {
if as.StepComponent.Started != nil {
return false
}
- as.StepComponent.Started = as.s.annotationNow()
+ as.StepComponent.Started = startTime
return true
}
// Close closes this step and any outstanding resources that it owns.
-func (as *Step) Close() bool {
- return as.closeWithStatus(nil)
+// If it is already closed, does not have side effects and returns false.
+func (as *Step) Close(closeTime *google.Timestamp) bool {
+ return as.closeWithStatus(closeTime, nil)
}
-func (as *Step) closeWithStatus(sp *milo.Status) bool {
+func (as *Step) closeWithStatus(closeTime *google.Timestamp, sp *milo.Status) bool {
if as.closed {
return false
}
@@ -551,7 +601,7 @@ func (as *Step) closeWithStatus(sp *milo.Status) bool {
// Close our outstanding substeps, and get their highest status value.
stepStatus := milo.Status_SUCCESS
for _, sub := range as.substeps {
- sub.Close()
+ sub.Close(closeTime)
if sub.StepComponent.Status > stepStatus {
stepStatus = sub.StepComponent.Status
}
@@ -570,7 +620,7 @@ func (as *Step) closeWithStatus(sp *milo.Status) bool {
if as.StepComponent.Status == milo.Status_RUNNING {
as.StepComponent.Status = stepStatus
}
- as.StepComponent.Ended = as.s.annotationNow()
+ as.StepComponent.Ended = closeTime
if as.StepComponent.Started == nil {
as.StepComponent.Started = as.StepComponent.Ended
}
« no previous file with comments | « appengine/cmd/milo/swarming/testdata/build-timeout ('k') | client/logdog/annotee/annotation/annotation_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698