faster file read

pull/1514/head
Antoine Toulme 2 years ago
parent 6084c1e2a4
commit 4bc9e37b0f
No known key found for this signature in database
GPG Key ID: 63199BF2E58EE4AE

@ -114,6 +114,29 @@ func ReadLines(filename string) ([]string, error) {
return ReadLinesOffsetN(filename, 0, -1) return ReadLinesOffsetN(filename, 0, -1)
} }
// ReadLine reads a file and returns the first occurrence of a line that is prefixed with prefix.
func ReadLine(filename string, prefix string) (string, error) {
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer f.Close()
r := bufio.NewReader(f)
for {
line, err := r.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
}
if strings.HasPrefix(line, prefix) {
return line, nil
}
}
return "", nil
}
// ReadLinesOffsetN reads contents from file and splits them by new line. // ReadLinesOffsetN reads contents from file and splits them by new line.
// The offset tells at which line number to start. // The offset tells at which line number to start.
// The count determines the number of lines to read (starting from offset): // The count determines the number of lines to read (starting from offset):

@ -62,17 +62,38 @@ func BootTimeWithContext(ctx context.Context) (uint64, error) {
return 0, err return 0, err
} }
statFile := "stat" useStatFile := true
if system == "lxc" && role == "guest" { if system == "lxc" && role == "guest" {
// if lxc, /proc/uptime is used. // if lxc, /proc/uptime is used.
statFile = "uptime" useStatFile = false
} else if system == "docker" && role == "guest" { } else if system == "docker" && role == "guest" {
// also docker, guest // also docker, guest
statFile = "uptime" useStatFile = false
} }
filename := HostProcWithContext(ctx, statFile) if useStatFile {
lines, err := ReadLines(filename) return readBootTimeStat(ctx)
} else {
filename := HostProcWithContext(ctx, "uptime")
lines, err := ReadLines(filename)
if err != nil {
return handleBootTimeFileReadErr(err)
}
if len(lines) != 1 {
return 0, fmt.Errorf("wrong uptime format")
}
f := strings.Fields(lines[0])
b, err := strconv.ParseFloat(f[0], 64)
if err != nil {
return 0, err
}
currentTime := float64(time.Now().UnixNano()) / float64(time.Second)
t := currentTime - b
return uint64(t), nil
}
}
func handleBootTimeFileReadErr(err error) (uint64, error) {
if os.IsPermission(err) { if os.IsPermission(err) {
var info syscall.Sysinfo_t var info syscall.Sysinfo_t
err := syscall.Sysinfo(&info) err := syscall.Sysinfo(&info)
@ -81,42 +102,30 @@ func BootTimeWithContext(ctx context.Context) (uint64, error) {
} }
currentTime := time.Now().UnixNano() / int64(time.Second) currentTime := time.Now().UnixNano() / int64(time.Second)
t := currentTime - int64(info.Uptime) t := currentTime - info.Uptime
return uint64(t), nil return uint64(t), nil
} }
return 0, err
}
func readBootTimeStat(ctx context.Context) (uint64, error) {
filename := HostProcWithContext(ctx, "stat")
line, err := ReadLine(filename, "btime")
if err != nil { if err != nil {
return 0, err return handleBootTimeFileReadErr(err)
} }
if strings.HasPrefix(line, "btime") {
if statFile == "stat" { f := strings.Fields(line)
for _, line := range lines { if len(f) != 2 {
if strings.HasPrefix(line, "btime") { return 0, fmt.Errorf("wrong btime format")
f := strings.Fields(line)
if len(f) != 2 {
return 0, fmt.Errorf("wrong btime format")
}
b, err := strconv.ParseInt(f[1], 10, 64)
if err != nil {
return 0, err
}
t := uint64(b)
return t, nil
}
}
} else if statFile == "uptime" {
if len(lines) != 1 {
return 0, fmt.Errorf("wrong uptime format")
} }
f := strings.Fields(lines[0]) b, err := strconv.ParseInt(f[1], 10, 64)
b, err := strconv.ParseFloat(f[0], 64)
if err != nil { if err != nil {
return 0, err return 0, err
} }
currentTime := float64(time.Now().UnixNano()) / float64(time.Second) t := uint64(b)
t := currentTime - b return t, nil
return uint64(t), nil
} }
return 0, fmt.Errorf("could not find btime") return 0, fmt.Errorf("could not find btime")
} }

@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/shirou/gopsutil/v3/internal/common" "github.com/shirou/gopsutil/v3/internal/common"
"github.com/stretchr/testify/require"
) )
var mu sync.Mutex var mu sync.Mutex
@ -862,3 +863,9 @@ func BenchmarkProcessPpid(b *testing.B) {
p.Ppid() p.Ppid()
} }
} }
func BenchmarkProcesses(b *testing.B) {
ps, err := Processes()
require.NoError(b, err)
require.Greater(b, len(ps), 0)
}

Loading…
Cancel
Save