Skip to content

Commit

Permalink
process: read AT_CLKTCK from the auxv
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
giuseppe committed Jun 26, 2019
1 parent 2e6ba54 commit 9de501b
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 13 deletions.
57 changes: 49 additions & 8 deletions internal/host/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,71 @@ package host

import (
"bufio"
"encoding/binary"
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
"unsafe"
)

/*
#include <unistd.h>
*/
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
Expand Down
3 changes: 2 additions & 1 deletion internal/host/host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
16 changes: 12 additions & 4 deletions internal/process/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}

0 comments on commit 9de501b

Please sign in to comment.