|
17 | 17 | package starttime
|
18 | 18 |
|
19 | 19 | import (
|
| 20 | + "fmt" |
| 21 | + "os" |
| 22 | + "strconv" |
| 23 | + "strings" |
20 | 24 | "sync"
|
21 | 25 | "time"
|
| 26 | + |
| 27 | + "gvisor.dev/gvisor/pkg/timing" |
22 | 28 | )
|
23 | 29 |
|
24 | 30 | var (
|
25 |
| - setOnce sync.Once |
26 |
| - startTime time.Time |
| 31 | + setOnce sync.Once |
| 32 | + processStart time.Time |
| 33 | + goStartTime time.Time |
| 34 | + envStartTime time.Time |
27 | 35 | )
|
28 | 36 |
|
29 |
| -// Get returns the time the `runsc` command started. |
| 37 | +// envStartTimeKey is the environment variable that holds the time the `runsc` |
| 38 | +// command started. This is used to track the actual start time across |
| 39 | +// re-execs of `runsc`. |
| 40 | +const envStartTimeKey = "RUNSC_START_TIME_NANOS" |
| 41 | + |
| 42 | +// Get returns the time the `runsc` command started on a best-effort basis. |
| 43 | +// If the RUNSC_START_TIME_NANOS environment variable is set, it is used. |
| 44 | +// Otherwise, it tries to get the time from /proc/self/status. |
| 45 | +// If neither is available, it returns the time the function was first called. |
30 | 46 | func Get() time.Time {
|
31 | 47 | setOnce.Do(func() {
|
32 |
| - startTime = time.Now() |
| 48 | + goStartTime = time.Now() |
| 49 | + if startTimeStr, found := os.LookupEnv(envStartTimeKey); found { |
| 50 | + if startTime, err := strconv.ParseInt(startTimeStr, 10, 64); err == nil { |
| 51 | + envStartTime = time.Unix(0, startTime) |
| 52 | + return // No need to check /proc/self/status. |
| 53 | + } |
| 54 | + } |
| 55 | + if st, err := os.Stat("/proc/self/status"); err == nil { |
| 56 | + processStart = st.ModTime() |
| 57 | + } |
33 | 58 | })
|
34 |
| - return startTime |
| 59 | + if !envStartTime.IsZero() { |
| 60 | + return envStartTime |
| 61 | + } |
| 62 | + if !processStart.IsZero() { |
| 63 | + return processStart |
| 64 | + } |
| 65 | + return goStartTime |
| 66 | +} |
| 67 | + |
| 68 | +// GoStartTime returns the time the `runsc` command's Go code started. |
| 69 | +func GoStartTime() time.Time { |
| 70 | + Get() |
| 71 | + return goStartTime |
| 72 | +} |
| 73 | + |
| 74 | +// Timer returns a Timer object that is rooted at `runsc` execution start. |
| 75 | +// If `runsc` was re-exec'd, this timer will have a midpoint called "re-exec" |
| 76 | +// that corresponds to the time of the re-exec. |
| 77 | +func Timer(name string) *timing.Timer { |
| 78 | + timer := timing.New(name, Get()) |
| 79 | + if !envStartTime.IsZero() { |
| 80 | + if !processStart.IsZero() { |
| 81 | + timer.ReachedAt("re-exec", processStart) |
| 82 | + } else { |
| 83 | + timer.ReachedAt("re-exec", goStartTime) |
| 84 | + } |
| 85 | + } |
| 86 | + return timer |
| 87 | +} |
| 88 | + |
| 89 | +// AppendEnviron appends the RUNSC_START_TIME_NANOS environment variable to |
| 90 | +// the given environment, if it is not already present. Otherwise, it is |
| 91 | +// preserved. |
| 92 | +func AppendEnviron(env []string) []string { |
| 93 | + const envVarPrefix = envStartTimeKey + "=" |
| 94 | + for _, e := range env { |
| 95 | + if strings.HasPrefix(e, envVarPrefix) { |
| 96 | + return env |
| 97 | + } |
| 98 | + } |
| 99 | + return append(env, fmt.Sprintf("%s=%d", envStartTimeKey, Get().UnixNano())) |
35 | 100 | }
|
0 commit comments