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  }