1// Copyright 2019 The Chromium OS Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package main 6 7import ( 8 "fmt" 9 "os" 10 "path/filepath" 11 "strings" 12 "syscall" 13 "time" 14) 15 16func getRusageLogFilename(env env) string { 17 value, _ := env.getenv("GETRUSAGE") 18 return value 19} 20 21func lockFileExclusive(fd uintptr) error { 22 maxTries := 100 23 for i := 0; i < maxTries; i++ { 24 const seekSet = 0 25 err := syscall.FcntlFlock(fd, syscall.F_SETLKW, &syscall.Flock_t{ 26 Type: syscall.F_WRLCK, 27 Whence: seekSet, 28 Start: 0, 29 Len: 0, 30 }) 31 if err == nil { 32 return nil 33 } 34 if err != syscall.EINTR { 35 return fmt.Errorf("locking file: %v", err) 36 } 37 } 38 return fmt.Errorf("locking file failed after %d tries", maxTries) 39} 40 41func logRusage(env env, logFileName string, compilerCmd *command) (exitCode int, err error) { 42 rusageBefore := syscall.Rusage{} 43 if err := syscall.Getrusage(syscall.RUSAGE_CHILDREN, &rusageBefore); err != nil { 44 return 0, err 45 } 46 compilerCmdWithoutRusage := &command{ 47 Path: compilerCmd.Path, 48 Args: compilerCmd.Args, 49 EnvUpdates: append(compilerCmd.EnvUpdates, "GETRUSAGE="), 50 } 51 startTime := time.Now() 52 exitCode, err = wrapSubprocessErrorWithSourceLoc(compilerCmdWithoutRusage, 53 env.run(compilerCmdWithoutRusage, env.stdin(), env.stdout(), env.stderr())) 54 if err != nil { 55 return 0, err 56 } 57 elapsedRealTime := time.Since(startTime) 58 rusageAfter := syscall.Rusage{} 59 if err := syscall.Getrusage(syscall.RUSAGE_CHILDREN, &rusageAfter); err != nil { 60 return 0, err 61 } 62 elapsedSysTime := time.Duration(rusageAfter.Stime.Nano()-rusageBefore.Stime.Nano()) * time.Nanosecond 63 elapsedUserTime := time.Duration(rusageAfter.Utime.Nano()-rusageBefore.Utime.Nano()) * time.Nanosecond 64 // Note: We assume that the compiler takes more heap than any other 65 // subcommands that we might have executed before. 66 maxMemUsed := rusageAfter.Maxrss 67 absCompilerPath := getAbsCmdPath(env, compilerCmd) 68 69 if err := os.MkdirAll(filepath.Dir(logFileName), 0777); err != nil { 70 return 0, wrapErrorwithSourceLocf(err, "error creating rusage log directory %s", logFileName) 71 } 72 73 timeUnit := float64(time.Second) 74 data := fmt.Sprintf("%.5f : %.5f : %.5f : %d : %s : %s\n", 75 float64(elapsedRealTime)/timeUnit, float64(elapsedUserTime)/timeUnit, float64(elapsedSysTime)/timeUnit, 76 maxMemUsed, absCompilerPath, 77 strings.Join(append([]string{filepath.Base(absCompilerPath)}, compilerCmd.Args...), " ")) 78 79 // Note: using file mode 0666 so that a root-created log is writable by others. 80 logFile, err := os.OpenFile(logFileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666) 81 if err != nil { 82 return 0, wrapErrorwithSourceLocf(err, "creating rusage logfile %s", logFileName) 83 } 84 85 // O_APPEND's atomicity guarantees are only for writes up to a certain size. If we don't 86 // lock the file, we might end up with corrupted records. 87 // 88 // Note that Close()'ing the file releases all associated locks. 89 if err := lockFileExclusive(logFile.Fd()); err != nil { 90 _ = logFile.Close() 91 return 0, wrapErrorwithSourceLocf(err, "locking rusage logfile %s: %v", logFileName, err) 92 } 93 94 _, err = logFile.WriteString(data) 95 closeErr := logFile.Close() 96 if err != nil { 97 return 0, wrapErrorwithSourceLocf(err, "writing to rusage logfile %s: %v", logFileName, err) 98 } 99 if closeErr != nil { 100 return 0, wrapErrorwithSourceLocf(err, "closing rusage logfile %s: %v", logFileName, closeErr) 101 } 102 103 return exitCode, nil 104} 105