From 9de501bfaadee7030a70100719563ab7beecb096 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 26 Jun 2019 11:27:07 +0200 Subject: [PATCH] process: read AT_CLKTCK from the auxv read AT_CLKTCK directly from the auxv vector, so it is possible to use psgo without requiring cgo. This is what internally sysconf does, although it doesn't use /proc/self/auxv but read directly from memory. Signed-off-by: Giuseppe Scrivano --- internal/host/host.go | 57 +++++++++++++++++++++++++++++++------ internal/host/host_test.go | 3 +- internal/process/process.go | 16 ++++++++--- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/internal/host/host.go b/internal/host/host.go index 4b145ec..cdf0e3e 100644 --- a/internal/host/host.go +++ b/internal/host/host.go @@ -18,30 +18,71 @@ package host import ( "bufio" + "encoding/binary" "fmt" + "io/ioutil" "os" "strconv" "strings" + "unsafe" ) -/* -#include -*/ -import "C" - var ( // cache host queries to redundant calculations clockTicks *int64 bootTime *int64 ) +func getNativeEndianness() binary.ByteOrder { + var i int32 = 0x00000001 + u := unsafe.Pointer(&i) + if *((*byte)(u)) == 0x01 { + return binary.LittleEndian + } + return binary.BigEndian +} + +const ( + atClktck = 17 +) + +func getFromAuxv(what uint, whatName string) (uint, error) { + dataLen := int(unsafe.Sizeof(int(0))) + p, err := ioutil.ReadFile("/proc/self/auxv") + if err != nil { + return 0, err + } + native := getNativeEndianness() + for i := 0; i < len(p); { + var k, v uint + + switch dataLen { + case 4: + k = uint(native.Uint32(p[i : i+dataLen])) + v = uint(native.Uint32(p[i+dataLen : i+dataLen*2])) + case 8: + k = uint(native.Uint64(p[i : i+dataLen])) + v = uint(native.Uint64(p[i+dataLen : i+dataLen*2])) + } + i += dataLen * 2 + if k == what { + return v, nil + } + } + return 0, fmt.Errorf("cannot find %s in auxv", whatName) +} + // ClockTicks returns sysconf(SC_CLK_TCK). -func ClockTicks() int64 { +func ClockTicks() (int64, error) { if clockTicks == nil { - ticks := int64(C.sysconf(C._SC_CLK_TCK)) + ret, err := getFromAuxv(atClktck, "AT_CLKTCK") + if err != nil { + return -1, err + } + ticks := int64(ret) clockTicks = &ticks } - return *clockTicks + return *clockTicks, nil } // BootTime parses /proc/uptime returns the boot time in seconds since the diff --git a/internal/host/host_test.go b/internal/host/host_test.go index ba6da86..4970c55 100644 --- a/internal/host/host_test.go +++ b/internal/host/host_test.go @@ -22,7 +22,8 @@ import ( func TestClockTicks(t *testing.T) { // no thorough test but it makes sure things are working - ticks := ClockTicks() + ticks, err := ClockTicks() + assert.Nil(t, err) assert.True(t, ticks > 0) } diff --git a/internal/process/process.go b/internal/process/process.go index 2aebfe9..20e4016 100644 --- a/internal/process/process.go +++ b/internal/process/process.go @@ -45,7 +45,7 @@ type Process struct { Hgroup string } -// LookupGID returns the textual group ID, if it can be optained, or the +// LookupGID returns the textual group ID, if it can be obtained, or the // decimal representation otherwise. func LookupGID(gid string) (string, error) { gidNum, err := strconv.Atoi(gid) @@ -59,7 +59,7 @@ func LookupGID(gid string) (string, error) { return g.Name, nil } -// LookupUID return the textual user ID, if it can be optained, or the decimal +// LookupUID return the textual user ID, if it can be obtained, or the decimal // representation otherwise. func LookupUID(uid string) (string, error) { uidNum, err := strconv.Atoi(uid) @@ -192,8 +192,12 @@ func (p *Process) ElapsedTime() (time.Duration, error) { if err != nil { return 0, err } + clockTicks, err := host.ClockTicks() + if err != nil { + return 0, err + } - sinceBoot = sinceBoot / host.ClockTicks() + sinceBoot = sinceBoot / clockTicks bootTime, err := host.BootTime() if err != nil { @@ -213,7 +217,11 @@ func (p *Process) CPUTime() (time.Duration, error) { if err != nil { return 0, err } - secs := (user + system) / host.ClockTicks() + clockTicks, err := host.ClockTicks() + if err != nil { + return 0, err + } + secs := (user + system) / clockTicks cpu := time.Unix(secs, 0) return cpu.Sub(time.Unix(0, 0)), nil }