github.com/Cloud-Foundations/Dominator@v0.3.4/lib/fsbench/fsbench.go (about) 1 package fsbench 2 3 import ( 4 "bufio" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "path" 10 "strings" 11 "time" 12 13 "github.com/Cloud-Foundations/Dominator/lib/wsyscall" 14 ) 15 16 const ( 17 BUFLEN = 1024 * 1024 18 MAX_TO_READ = 1024 * 1024 * 128 19 ) 20 21 func GetDevnumForFile(name string) (devnum uint64, err error) { 22 var stat wsyscall.Stat_t 23 if err = wsyscall.Stat(name, &stat); err != nil { 24 return 0, err 25 } 26 return stat.Dev, nil 27 } 28 29 func getDevnumForDevice(name string) (devnum uint64, err error) { 30 var stat wsyscall.Stat_t 31 if err = wsyscall.Stat(name, &stat); err != nil { 32 return 0, err 33 } 34 return stat.Rdev, nil 35 } 36 37 func getDevnodeForFile(name string) (string, error) { 38 devnum, err := GetDevnumForFile(name) 39 if err != nil { 40 return "", err 41 } 42 fi_list, err := ioutil.ReadDir("/dev") 43 if err != nil { 44 return "", fmt.Errorf("error reading directory: /dev: %s", err) 45 } 46 for _, fi := range fi_list { 47 if (fi.Mode()&os.ModeDevice != 0) && 48 (fi.Mode()&os.ModeCharDevice == 0) { 49 devpath := path.Join("/dev", fi.Name()) 50 dnum, err := getDevnumForDevice(devpath) 51 if err != nil { 52 return "", err 53 } 54 if dnum == devnum { 55 return devpath, nil 56 } 57 } 58 } 59 // Can't find it the traditional way. btrfs is a known culprit (since it 60 // can be on multiple devices, it claims a virtual device). Read 61 // /proc/mounts instead. First walk up the tree to find the mount point. 62 mountPoint := name 63 for { 64 parent := path.Dir(mountPoint) 65 parentDevnum, err := GetDevnumForFile(parent) 66 if err != nil { 67 return "", err 68 } 69 if parentDevnum != devnum { 70 break 71 } 72 mountPoint = parent 73 if parent == "/" { 74 break 75 } 76 } 77 file, err := os.Open("/proc/mounts") 78 if err != nil { 79 return "", err 80 } 81 defer file.Close() 82 scanner := bufio.NewScanner(file) 83 for scanner.Scan() { 84 fields := strings.Fields(scanner.Text()) 85 if len(fields) < 3 { 86 continue 87 } 88 if fields[1] == mountPoint { 89 if strings.HasPrefix(fields[0], "/dev/") { 90 return fields[0], nil 91 } 92 } 93 } 94 if err := scanner.Err(); err != nil { 95 return "", err 96 } 97 return "", fmt.Errorf("unable to find device path for: %s", name) 98 } 99 100 // Compute the maximum read speed of a block device, given a file within a 101 // file-system mounted on the block device. 102 // Returns: bytesPerSecond, blocksPerSecond, error 103 // If I/O accounting is enabled, blocksPerSecond will be non-zero. 104 func GetReadSpeed(name string) (uint64, uint64, error) { 105 devpath, err := getDevnodeForFile(name) 106 if err != nil { 107 return 0, 0, err 108 } 109 file, err := openDirect(devpath, os.O_RDONLY, 0) 110 if err != nil { 111 return 0, 0, fmt.Errorf("error opening: %s: %s", devpath, err) 112 } 113 defer file.Close() 114 var tread uint = 0 115 buffer := make([]byte, BUFLEN) 116 var rusage_start, rusage_stop wsyscall.Rusage 117 err = wsyscall.Getrusage(wsyscall.RUSAGE_SELF, &rusage_start) 118 if err != nil { 119 return 0, 0, fmt.Errorf("error getting resource usage: %s", err) 120 } 121 time_start := time.Now() 122 for tread < MAX_TO_READ { 123 var nread int 124 nread, err = file.Read(buffer) 125 tread += uint(nread) 126 if err != nil { 127 if err == io.EOF { 128 break 129 } 130 return 0, 0, fmt.Errorf("error reading: %s: %s", devpath, err) 131 } 132 } 133 elapsed := time.Since(time_start) 134 bytesPerSecond := uint64(float64(tread) / elapsed.Seconds()) 135 err = wsyscall.Getrusage(wsyscall.RUSAGE_SELF, &rusage_stop) 136 if err != nil { 137 return 0, 0, fmt.Errorf("error getting resource usage: %s", err) 138 } 139 var blocksPerSecond uint64 140 if rusage_stop.Inblock > rusage_start.Inblock { 141 blocksPerSecond = uint64(float64(rusage_stop.Inblock- 142 rusage_start.Inblock) / elapsed.Seconds()) 143 } 144 return bytesPerSecond, blocksPerSecond, nil 145 }