Skip to content

Commit c29a7a7

Browse files
author
Todd Wang
committed
lib: Added v.io/x/lib/timing package.
Adds the timing package, which provides utilities for collecting trees of timing information and dumping it with a nice format. An example of the output format: 00:00:01.000 root 98.000s 00:01:39.000 00:00:01.000 * 9.000s 00:00:10.000 00:00:10.000 foo 45.000s 00:00:55.000 00:00:10.000 * 5.000s 00:00:15.000 00:00:15.000 foo1 22.000s 00:00:37.000 00:00:37.000 foo2 18.000s 00:00:55.000 00:00:55.000 bar 25.000s 00:01:20.000 00:01:20.000 baz 19.000s 00:01:39.000 Also add this support to the cmdline package via the -time flag, and add some preliminary timings for "jiri update". MultiPart: 3/4 Change-Id: Ic22476e7b75ad31756eec8b6554991dd885b7632
1 parent e8ec36e commit c29a7a7

File tree

10 files changed

+1135
-15
lines changed

10 files changed

+1135
-15
lines changed

Diff for: cmdline/.api

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ pkg cmdline, func HideGlobalFlagsExcept(...*regexp.Regexp)
55
pkg cmdline, func Main(*Command)
66
pkg cmdline, func Parse(*Command, *Env, []string) (Runner, []string, error)
77
pkg cmdline, func ParseAndRun(*Command, *Env, []string) error
8+
pkg cmdline, method (*Env) TimerPop()
9+
pkg cmdline, method (*Env) TimerPush(string)
810
pkg cmdline, method (*Env) UsageErrorf(string, ...interface{}) error
911
pkg cmdline, method (ErrExitCode) Error() string
1012
pkg cmdline, method (RunnerFunc) Run(*Env, []string) error
@@ -23,6 +25,7 @@ pkg cmdline, type Env struct
2325
pkg cmdline, type Env struct, Stderr io.Writer
2426
pkg cmdline, type Env struct, Stdin io.Reader
2527
pkg cmdline, type Env struct, Stdout io.Writer
28+
pkg cmdline, type Env struct, Timer timing.Timer
2629
pkg cmdline, type Env struct, Usage func(*Env, io.Writer)
2730
pkg cmdline, type Env struct, Vars map[string]string
2831
pkg cmdline, type ErrExitCode int

Diff for: cmdline/cmdline.go

+29-11
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import (
5353

5454
"v.io/x/lib/envvar"
5555
_ "v.io/x/lib/metadata" // for the -metadata flag
56+
"v.io/x/lib/timing"
5657
)
5758

5859
// Command represents a single command in a command-line program. A program
@@ -121,9 +122,16 @@ type Topic struct {
121122
func Main(root *Command) {
122123
env := EnvFromOS()
123124
err := ParseAndRun(root, env, os.Args[1:])
124-
os.Exit(ExitCode(err, env.Stderr))
125+
code := ExitCode(err, env.Stderr)
126+
if *flagTime && env.Timer != nil {
127+
env.Timer.Finish()
128+
timing.IntervalPrinter{}.Print(env.Stderr, env.Timer.Root())
129+
}
130+
os.Exit(code)
125131
}
126132

133+
var flagTime = flag.Bool("time", false, "Dump timing information to stderr before exiting the program.")
134+
127135
// Parse parses args against the command tree rooted at root down to a leaf
128136
// command. A single path through the command tree is traversed, based on the
129137
// sub-commands specified in args. Global and command-specific flags are parsed
@@ -156,6 +164,8 @@ func Main(root *Command) {
156164
// Parse merges root flags into flag.CommandLine and sets ContinueOnError, so
157165
// that subsequent calls to flag.Parsed return true.
158166
func Parse(root *Command, env *Env, args []string) (Runner, []string, error) {
167+
env.TimerPush("cmdline parse")
168+
defer env.TimerPop()
159169
if globalFlags == nil {
160170
// Initialize our global flags to a cleaned copy. We don't want the merging
161171
// in parseFlags to contaminate the global flags, even if Parse is called
@@ -203,6 +213,8 @@ func ParseAndRun(root *Command, env *Env, args []string) error {
203213
if err != nil {
204214
return err
205215
}
216+
env.TimerPush("cmdline run")
217+
defer env.TimerPop()
206218
return runner.Run(env, args)
207219
}
208220

@@ -337,7 +349,7 @@ func (cmd *Command) parse(path []*Command, env *Env, args []string) (Runner, []s
337349
if cmd.LookPath {
338350
// Look for a matching executable in PATH.
339351
subCmd := cmd.Name + "-" + subName
340-
if lookPath(subCmd, env.pathDirs()) {
352+
if lookPath(env, subCmd) {
341353
return binaryRunner{subCmd, cmdPath}, subArgs, nil
342354
}
343355
}
@@ -464,6 +476,8 @@ type binaryRunner struct {
464476
}
465477

466478
func (b binaryRunner) Run(env *Env, args []string) error {
479+
env.TimerPush("run " + b.subCmd)
480+
defer env.TimerPop()
467481
vars := envvar.CopyMap(env.Vars)
468482
vars["CMDLINE_PREFIX"] = b.cmdPath
469483
cmd := exec.Command(b.subCmd, args...)
@@ -481,10 +495,12 @@ func (b binaryRunner) Run(env *Env, args []string) error {
481495
return err
482496
}
483497

484-
// lookPath returns a boolean that indicates whether executable <name>
485-
// can be found in any of the given directories.
486-
func lookPath(name string, dirs []string) bool {
487-
for _, dir := range dirs {
498+
// lookPath returns true iff an executable with the given name can be found in
499+
// any of the PATH directories.
500+
func lookPath(env *Env, name string) bool {
501+
env.TimerPush("lookpath " + name)
502+
defer env.TimerPop()
503+
for _, dir := range env.pathDirs() {
488504
fileInfos, err := ioutil.ReadDir(dir)
489505
if err != nil {
490506
continue
@@ -501,11 +517,13 @@ func lookPath(name string, dirs []string) bool {
501517
return false
502518
}
503519

504-
// lookPathAll returns a deduped list of all executables found in the given
505-
// directories whose name starts with "<name>-", and where the name doesn't
506-
// match the given seen set. The seen set may be mutated by this function.
507-
func lookPathAll(name string, dirs []string, seen map[string]bool) (result []string) {
508-
for _, dir := range dirs {
520+
// lookPathAll returns a deduped list of all executables found in the PATH
521+
// directories whose name starts with "name-", and where the name doesn't match
522+
// the given seen set. The seen set may be mutated by this function.
523+
func lookPathAll(env *Env, name string, seen map[string]bool) (result []string) {
524+
env.TimerPush("lookpathall " + name)
525+
defer env.TimerPop()
526+
for _, dir := range env.pathDirs() {
509527
fileInfos, err := ioutil.ReadDir(dir)
510528
if err != nil {
511529
continue

Diff for: cmdline/cmdline_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -2770,6 +2770,8 @@ Usage:
27702770
The global flags are:
27712771
-metadata=<just specify -metadata to activate>
27722772
Displays metadata for the program and exits.
2773+
-time=false
2774+
Dump timing information to stderr before exiting the program.
27732775
`,
27742776
},
27752777
{
@@ -2788,6 +2790,8 @@ Usage:
27882790
The global flags are:
27892791
-metadata=<just specify -metadata to activate>
27902792
Displays metadata for the program and exits.
2793+
-time=false
2794+
Dump timing information to stderr before exiting the program.
27912795
`,
27922796
},
27932797
{

Diff for: cmdline/env.go

+18-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"v.io/x/lib/envvar"
1515
"v.io/x/lib/textutil"
16+
"v.io/x/lib/timing"
1617
)
1718

1819
// EnvFromOS returns a new environment based on the operating system.
@@ -22,6 +23,7 @@ func EnvFromOS() *Env {
2223
Stdout: os.Stdout,
2324
Stderr: os.Stderr,
2425
Vars: envvar.SliceToMap(os.Environ()),
26+
Timer: timing.NewFullTimer("root"),
2527
}
2628
}
2729

@@ -33,6 +35,7 @@ type Env struct {
3335
Stdout io.Writer
3436
Stderr io.Writer
3537
Vars map[string]string // Environment variables
38+
Timer timing.Timer
3639

3740
// Usage is a function that prints usage information to w. Typically set by
3841
// calls to Main or Parse to print usage of the leaf command.
@@ -46,14 +49,28 @@ func (e *Env) UsageErrorf(format string, args ...interface{}) error {
4649
return usageErrorf(e, e.Usage, format, args...)
4750
}
4851

49-
// Clone creates a deep copy of Env.
52+
// TimerPush calls e.Timer.Push(name), only if the Timer is non-nil.
53+
func (e *Env) TimerPush(name string) {
54+
if e.Timer != nil {
55+
e.Timer.Push(name)
56+
}
57+
}
58+
59+
// TimerPop calls e.Timer.Pop(), only if the Timer is non-nil.
60+
func (e *Env) TimerPop() {
61+
if e.Timer != nil {
62+
e.Timer.Pop()
63+
}
64+
}
65+
5066
func (e *Env) clone() *Env {
5167
return &Env{
5268
Stdin: e.Stdin,
5369
Stdout: e.Stdout,
5470
Stderr: e.Stderr,
5571
Vars: envvar.CopyMap(e.Vars),
5672
Usage: e.Usage,
73+
Timer: e.Timer, // use the same timer for all operations
5774
}
5875
}
5976

Diff for: cmdline/help.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func runHelp(w *textutil.LineWriter, env *Env, args []string, path []*Command, c
126126
if cmd.LookPath {
127127
// Look for a matching executable in PATH.
128128
extName := cmd.Name + "-" + subName
129-
if lookPath(extName, env.pathDirs()) {
129+
if lookPath(env, extName) {
130130
runner := binaryRunner{extName, cmdPath}
131131
envCopy := env.clone()
132132
envCopy.Vars["CMDLINE_STYLE"] = config.style.String()
@@ -225,7 +225,7 @@ func usageAll(w *textutil.LineWriter, env *Env, path []*Command, config *helpCon
225225
usageAll(w, env, append(path, help), config, false)
226226
}
227227
if cmd.LookPath {
228-
extNames := lookPathAll(cmd.Name, env.pathDirs(), cmd.subNames())
228+
extNames := lookPathAll(env, cmd.Name, cmd.subNames())
229229
for _, extName := range extNames {
230230
runner := binaryRunner{extName, cmdPath}
231231
var buffer bytes.Buffer
@@ -277,6 +277,8 @@ func usageAll(w *textutil.LineWriter, env *Env, path []*Command, config *helpCon
277277
// avoid printing redundant information (e.g. help command, global flags).
278278
func usage(w *textutil.LineWriter, env *Env, path []*Command, config *helpConfig, firstCall bool) {
279279
cmd, cmdPath := path[len(path)-1], pathName(config.prefix, path)
280+
env.TimerPush("usage " + cmdPath)
281+
defer env.TimerPop()
280282
if config.style == styleShort {
281283
fmt.Fprintln(w, cmd.Short)
282284
return
@@ -305,7 +307,7 @@ func usage(w *textutil.LineWriter, env *Env, path []*Command, config *helpConfig
305307
}
306308
var extChildren []string
307309
if cmd.LookPath {
308-
extChildren = lookPathAll(cmd.Name, env.pathDirs(), cmd.subNames())
310+
extChildren = lookPathAll(env, cmd.Name, cmd.subNames())
309311
}
310312
hasSubcommands := len(cmd.Children) > 0 || len(extChildren) > 0
311313
if hasSubcommands {

Diff for: timing/.api

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
pkg timing, func IntervalDuration(Interval, time.Time) time.Duration
2+
pkg timing, func NewCompactTimer(string) *CompactTimer
3+
pkg timing, func NewFullTimer(string) *FullTimer
4+
pkg timing, method (*CompactTimer) Finish()
5+
pkg timing, method (*CompactTimer) Pop()
6+
pkg timing, method (*CompactTimer) Push(string)
7+
pkg timing, method (*CompactTimer) Root() Interval
8+
pkg timing, method (*CompactTimer) String() string
9+
pkg timing, method (*FullTimer) Finish()
10+
pkg timing, method (*FullTimer) Pop()
11+
pkg timing, method (*FullTimer) Push(string)
12+
pkg timing, method (*FullTimer) Root() Interval
13+
pkg timing, method (*FullTimer) String() string
14+
pkg timing, method (FullInterval) Child(int) Interval
15+
pkg timing, method (FullInterval) End() time.Time
16+
pkg timing, method (FullInterval) Name() string
17+
pkg timing, method (FullInterval) NumChild() int
18+
pkg timing, method (FullInterval) Start() time.Time
19+
pkg timing, method (FullInterval) String() string
20+
pkg timing, method (IntervalPrinter) Print(io.Writer, Interval) error
21+
pkg timing, type CompactTimer struct
22+
pkg timing, type FullInterval struct
23+
pkg timing, type FullInterval struct, Children []FullInterval
24+
pkg timing, type FullInterval struct, EndTime time.Time
25+
pkg timing, type FullInterval struct, Label string
26+
pkg timing, type FullInterval struct, StartTime time.Time
27+
pkg timing, type FullTimer struct
28+
pkg timing, type FullTimer struct, FullRoot FullInterval
29+
pkg timing, type Interval interface { Child, End, Name, NumChild, Start, String }
30+
pkg timing, type Interval interface, Child(int) Interval
31+
pkg timing, type Interval interface, End() time.Time
32+
pkg timing, type Interval interface, Name() string
33+
pkg timing, type Interval interface, NumChild() int
34+
pkg timing, type Interval interface, Start() time.Time
35+
pkg timing, type Interval interface, String() string
36+
pkg timing, type IntervalPrinter struct
37+
pkg timing, type IntervalPrinter struct, Indent int
38+
pkg timing, type IntervalPrinter struct, MinGap time.Duration
39+
pkg timing, type IntervalPrinter struct, TimeFormat string
40+
pkg timing, type Timer interface { Finish, Pop, Push, Root, String }
41+
pkg timing, type Timer interface, Finish()
42+
pkg timing, type Timer interface, Pop()
43+
pkg timing, type Timer interface, Push(string)
44+
pkg timing, type Timer interface, Root() Interval
45+
pkg timing, type Timer interface, String() string

0 commit comments

Comments
 (0)