Skip to content

Commit

Permalink
chore: refactor time pkg
Browse files Browse the repository at this point in the history
  • Loading branch information
NDStrahilevitz committed Sep 3, 2024
1 parent d2b1111 commit 69318a0
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 59 deletions.
64 changes: 33 additions & 31 deletions pkg/ebpf/tracee.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,39 @@ func New(cfg config.Config) (*Tracee, error) {
func (t *Tracee) Init(ctx gocontext.Context) error {
var err error

// Initialize time

// Checking the kernel symbol needs to happen after obtaining the capability;
// otherwise, we get a warning.
usedClockID := traceetime.CLOCK_BOOTTIME
err = capabilities.GetInstance().EBPF(
func() error {
supported, innerErr := bpf.BPFHelperIsSupported(bpf.BPFProgTypeKprobe, bpf.BPFFuncKtimeGetBootNs)

// only report if operation not permitted
if errors.Is(innerErr, syscall.EPERM) {
return innerErr
}

// If BPFFuncKtimeGetBootNs is not available, eBPF will generate events based on monotonic time.
if !supported {
usedClockID = traceetime.CLOCK_MONOTONIC
}
return nil
})
if err != nil {
return errfmt.WrapError(err)
}

traceetime.Init(int32(usedClockID))

// elapsed time in nanoseconds since system start
t.startTime = uint64(traceetime.GetStartTimeNS())
// time in nanoseconds when the system was booted
t.bootTime = uint64(traceetime.GetBootTimeNS())

t.timeNormalizer = traceetime.CreateTimeNormalizerByConfig(t.config.Output.RelativeTime, t.startTime, t.bootTime)

// Initialize buckets cache

var mntNSProcs map[int]int
Expand Down Expand Up @@ -538,37 +571,6 @@ func (t *Tracee) Init(ctx gocontext.Context) error {
}
}

// Initialize time normalizer

// Checking the kernel symbol needs to happen after obtaining the capability;
// otherwise, we get a warning.
usedClockID := traceetime.CLOCK_BOOTTIME
err = capabilities.GetInstance().EBPF(
func() error {
supported, innerErr := bpf.BPFHelperIsSupported(bpf.BPFProgTypeKprobe, bpf.BPFFuncKtimeGetBootNs)

// only report if operation not permitted
if errors.Is(innerErr, syscall.EPERM) {
return innerErr
}

// If BPFFuncKtimeGetBootNs is not available, eBPF will generate events based on monotonic time.
if !supported {
usedClockID = traceetime.CLOCK_MONOTONIC
}
return nil
})
if err != nil {
return errfmt.WrapError(err)
}

// elapsed time in nanoseconds since system start
t.startTime = uint64(traceetime.GetStartTimeNS(int32(usedClockID)))
// time in nanoseconds when the system was booted
t.bootTime = uint64(traceetime.GetBootTimeNS(int32(usedClockID)))

t.timeNormalizer = traceetime.CreateTimeNormalizerByConfig(t.config.Output.RelativeTime, t.startTime, t.bootTime)

// Initialize Process Tree (if enabled)

if t.config.ProcTree.Source != proctree.SourceNone {
Expand Down
110 changes: 82 additions & 28 deletions pkg/time/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ import (
"github.com/aquasecurity/tracee/pkg/utils"
)

var configHZOnce, clockTickOnce, bootTimeOnce sync.Once
var configHZOnce, clockTickOnce sync.Once
var configHZ int
var userHZ int64
var bootTime int64 // To normalize times, this should be constant

// GetSystemHZ returns an approximation of CONFIG_HZ (the kernel timer interrupt).
func GetSystemHZ() int {
Expand Down Expand Up @@ -77,42 +76,77 @@ func getBootTimeInJiffies() int64 {
// Boot time functions
//

var initTimeOnce sync.Once // Set reference times times once
var startTime, bootTime int64 // To normalize times, these should be constant

// Common clock IDs
const (
CLOCK_MONOTONIC = unix.CLOCK_MONOTONIC // Time since a boot (not including time spent in suspend)
CLOCK_BOOTTIME = unix.CLOCK_BOOTTIME // Time since a boot (including time spent in suspend)
)

// GetStartTimeNS returns the elapsed time since system start in nanoseconds.
// Possible to retrieve from two differents clocks: CLOCK_MONOTONIC or CLOCK_BOOTTIME.
func GetStartTimeNS(clockID int32) int64 {
var ts unix.Timespec
// Init sets the reference points for (approximate) system and process start time.
// Run this function ASAP. Not running this function first will cause wrong behaviour
// in other functions of the package.
//
// Reference points can be set from two differents clocks: CLOCK_MONOTONIC or CLOCK_BOOTTIME.
// Tracee bpf code tries to use boottime clock if available, otherwise uses monotonic clock.
// ClockGettime get time elapsed since start (boot) so tracee can calculate event timestamps.
func Init(clockID int32) error {
var err error
initTimeOnce.Do(func() {
startTimeMonotonic, errIn := getClockTimeNS(clockID)
if errIn != nil {
err = errIn
return
}
startTimeEpoch := time.Now().UnixNano()

// process start time since boot
startTime = startTimeMonotonic

/*
Note how the epoch read is just after the monotonic read, allowing us
to approximate the boot time
Epoch
Read
---|-----------...-----|-----------...-------|-|--------------------->
Epoch Boot Monotonic
Start Read
*/
// process start time since boot - process start time since epoch = (approx) boot time since epoch
bootTime = startTimeEpoch - startTimeMonotonic
})

return err
}

// Tracee bpf code try to use boottime clock if available, otherwise uses monotonic clock.
// ClockGettime get time elapsed since start (boot) so tracee can calculate event timestamps
// relative to it.
err := unix.ClockGettime(clockID, &ts)
if err != nil {
logger.Debugw("error getting time", "err", err)
return 0
}
return ts.Nano()
// GetStartTimeNS sets the constant start time and returns it.

// relative to it.
func GetStartTimeNS() int64 {
return startTime
}

// GetBootTimeNS returns the boot time of the system in nanoseconds.
func GetBootTimeNS(clockID int32) int64 {
bootTimeOnce.Do(
func() {
startTime := GetStartTimeNS(clockID)
bootTime = time.Now().UnixNano() - startTime
})
// GetBootTimeNS returns the boot time of the system in nanoseconds since epoch.
func GetBootTimeNS() int64 {
return bootTime
}

func GetBootTime(clockID int32) time.Time {
startTime := GetStartTimeNS(clockID)
uptime := time.Duration(startTime) * time.Nanosecond
return time.Now().Add(-uptime)
func GetBootTime() time.Time {
bootNS := GetBootTimeNS()
return time.Unix(0, bootNS)
}

func getClockTimeNS(clockID int32) (int64, error) {
var ts unix.Timespec

err := unix.ClockGettime(clockID, &ts)
if err != nil {
logger.Debugw("error getting time", "err", err)
return 0, err
}
return ts.Nano(), nil
}

//
Expand All @@ -134,10 +168,30 @@ func ClockTicksToNsSinceBootTime(ticks int64) uint64 {
return uint64(ticks * 1000000000 / GetUserHZ())
}

// BootToEpochNS converts time since boot to the epoch time
func BootToEpochNS(ns uint64) uint64 {
return uint64(GetBootTimeNS()) + ns
}

// EpochToBootTimeNS converts time since epoch to relative time from boot
func EpochToBootTimeNS(ns uint64) uint64 {
return ns - uint64(GetBootTimeNS())
}

// BootToProcessNS converts time since boot to time since process start
func BootToProcessNS(ns uint64) uint64 {
return ns - uint64(GetStartTimeNS())
}

// ProcessToBootNS converts time since process start to time since boot
func ProcessToBootNS(ns uint64) uint64 {
return ns + uint64(GetStartTimeNS())
}

// NsSinceBootTimeToTime converts nanoseconds timestamp (since boot) to a time.Time object.
func NsSinceBootTimeToTime(clockID int32, ns uint64) time.Time {
func NsSinceBootTimeToTime(ns uint64) time.Time {
duration := time.Duration(int64(ns))
bootTime := GetBootTime(clockID)
bootTime := GetBootTime()
return bootTime.Add(duration)
}

Expand Down

0 comments on commit 69318a0

Please sign in to comment.